2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2007, The GROMACS development team.
6 * Copyright (c) 2013,2014, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
52 #include "gromacs/fileio/futil.h"
53 #include "gromacs/commandline/pargs.h"
56 #include "gromacs/fileio/pdbio.h"
57 #include "gromacs/fileio/confio.h"
62 #include "gromacs/fileio/tpxio.h"
63 #include "gromacs/fileio/trxio.h"
72 void add_rec(t_conect c[], atom_id i, atom_id j, real d2)
74 if (c[i].aa == NO_ATID)
79 else if (c[i].ab == NO_ATID)
84 else if (d2 < c[i].d2a)
89 else if (d2 < c[i].d2b)
94 /* Swap them if necessary: a must be larger than b */
95 if (c[i].d2a < c[i].d2b)
106 void do_conect(const char *fn, int n, rvec x[])
114 fprintf(stderr, "Building CONECT records\n");
116 for (i = 0; (i < n); i++)
118 c[i].aa = c[i].ab = NO_ATID;
121 for (i = 0; (i < n); i++)
123 for (j = i+1; (j < n); j++)
125 rvec_sub(x[i], x[j], dx);
127 add_rec(c, i, j, d2);
128 add_rec(c, j, i, d2);
131 fp = gmx_ffopen(fn, "a");
132 for (i = 0; (i < n); i++)
134 if ((c[i].aa == NO_ATID) || (c[i].ab == NO_ATID))
136 fprintf(stderr, "Warning dot %d has no conections\n", i+1);
138 fprintf(fp, "CONECT%5d%5d%5d\n", i+1, c[i].aa+1, c[i].ab+1);
144 void connelly_plot(const char *fn, int ndots, real dots[], rvec x[], t_atoms *atoms,
145 t_symtab *symtab, int ePBC, matrix box, gmx_bool bSave)
147 static const char *atomnm = "DOT";
148 static const char *resnm = "DOT";
149 static const char *title = "Connely Dot Surface Generated by g_sas";
151 int i, i0, r0, ii0, k;
159 srenew(atoms->atom, atoms->nr+ndots);
160 srenew(atoms->atomname, atoms->nr+ndots);
161 srenew(atoms->resinfo, r0+1);
162 atoms->atom[i0].resind = r0;
163 t_atoms_set_resinfo(atoms, i0, symtab, resnm, r0+1, ' ', 0, ' ');
164 srenew(atoms->pdbinfo, atoms->nr+ndots);
165 snew(xnew, atoms->nr+ndots);
166 for (i = 0; (i < atoms->nr); i++)
168 copy_rvec(x[i], xnew[i]);
170 for (i = k = 0; (i < ndots); i++)
173 atoms->atomname[ii0] = put_symtab(symtab, atomnm);
174 atoms->pdbinfo[ii0].type = epdbATOM;
175 atoms->pdbinfo[ii0].atomnr = ii0;
176 atoms->atom[ii0].resind = r0;
177 xnew[ii0][XX] = dots[k++];
178 xnew[ii0][YY] = dots[k++];
179 xnew[ii0][ZZ] = dots[k++];
180 atoms->pdbinfo[ii0].bfac = 0.0;
181 atoms->pdbinfo[ii0].occup = 0.0;
183 atoms->nr = i0+ndots;
185 write_sto_conf(fn, title, atoms, xnew, NULL, ePBC, box);
191 init_t_atoms(&aaa, ndots, TRUE);
192 aaa.atom[0].resind = 0;
193 t_atoms_set_resinfo(&aaa, 0, symtab, resnm, 1, ' ', 0, ' ');
195 for (i = k = 0; (i < ndots); i++)
198 aaa.atomname[ii0] = put_symtab(symtab, atomnm);
199 aaa.pdbinfo[ii0].type = epdbATOM;
200 aaa.pdbinfo[ii0].atomnr = ii0;
201 aaa.atom[ii0].resind = 0;
202 xnew[ii0][XX] = dots[k++];
203 xnew[ii0][YY] = dots[k++];
204 xnew[ii0][ZZ] = dots[k++];
205 aaa.pdbinfo[ii0].bfac = 0.0;
206 aaa.pdbinfo[ii0].occup = 0.0;
209 write_sto_conf(fn, title, &aaa, xnew, NULL, ePBC, box);
210 do_conect(fn, ndots, xnew);
211 free_t_atoms(&aaa, FALSE);
216 real calc_radius(char *atom)
243 void sas_plot(int nfile, t_filenm fnm[], real solsize, int ndots,
244 real qcut, gmx_bool bSave, real minarea, gmx_bool bPBC,
245 real dgs_default, gmx_bool bFindex, const output_env_t oenv)
247 FILE *fp, *fp2, *fp3 = NULL, *vp;
248 const char *flegend[] = {
249 "Hydrophobic", "Hydrophilic",
252 const char *vlegend[] = { "Volume (nm\\S3\\N)", "Density (g/l)" };
253 const char *or_and_oa_legend[] = { "Average (nm\\S2\\N)", "Standard deviation (nm\\S2\\N)" };
256 gmx_atomprop_t aps = NULL;
257 gmx_rmpbc_t gpbc = NULL;
260 int i, j, ii, nfr, natoms, flag, nsurfacedots, res;
268 gmx_bool *bOut, *bPhobic;
270 gmx_bool bResAt, bITP, bDGsol;
271 real *radius, *dgs_factor = NULL, *area = NULL, *surfacedots = NULL;
272 real at_area, *atom_area = NULL, *atom_area2 = NULL;
273 real *res_a = NULL, *res_area = NULL, *res_area2 = NULL;
274 real totarea, totvolume, totmass = 0, density, harea, tarea, fluc2;
275 atom_id **index, *findex;
276 int *nx, nphobic, npcheck, retval;
277 char **grpname, *fgrpname;
280 bITP = opt2bSet("-i", nfile, fnm);
281 bResAt = opt2bSet("-or", nfile, fnm) || opt2bSet("-oa", nfile, fnm) || bITP;
283 bTop = read_tps_conf(ftp2fn(efTPS, nfile, fnm), title, &top, &ePBC,
284 &xtop, NULL, topbox, FALSE);
285 atoms = &(top.atoms);
289 fprintf(stderr, "No tpr file, will not compute Delta G of solvation\n");
294 bDGsol = strcmp(*(atoms->atomtype[0]), "?") != 0;
297 fprintf(stderr, "Warning: your tpr file is too old, will not compute "
298 "Delta G of solvation\n");
302 printf("In case you use free energy of solvation predictions:\n");
303 please_cite(stdout, "Eisenberg86a");
307 aps = gmx_atomprop_init();
309 if ((natoms = read_first_x(oenv, &status, ftp2fn(efTRX, nfile, fnm),
312 gmx_fatal(FARGS, "Could not read coordinates from statusfile\n");
315 if ((ePBC != epbcXYZ) || (TRICLINIC(box)))
317 fprintf(stderr, "\n\nWARNING: non-rectangular boxes may give erroneous results or crashes.\n"
318 "Analysis based on vacuum simulations (with the possibility of evaporation)\n"
319 "will certainly crash the analysis.\n\n");
324 fprintf(stderr, "Select a group for calculation of surface and a group for output:\n");
325 get_index(atoms, ftp2fn_null(efNDX, nfile, fnm), 2, nx, index, grpname);
329 fprintf(stderr, "Select a group of hydrophobic atoms:\n");
330 get_index(atoms, ftp2fn_null(efNDX, nfile, fnm), 1, &nphobic, &findex, &fgrpname);
333 for (i = 0; i < nx[1]; i++)
335 bOut[index[1][i]] = TRUE;
338 /* Now compute atomic readii including solvent probe size */
339 snew(radius, natoms);
340 snew(bPhobic, nx[0]);
343 snew(atom_area, nx[0]);
344 snew(atom_area2, nx[0]);
345 snew(res_a, atoms->nres);
346 snew(res_area, atoms->nres);
347 snew(res_area2, atoms->nres);
351 snew(dgs_factor, nx[0]);
354 /* Get a Van der Waals radius for each atom */
356 for (i = 0; (i < natoms); i++)
358 if (!gmx_atomprop_query(aps, epropVDW,
359 *(atoms->resinfo[atoms->atom[i].resind].name),
360 *(atoms->atomname[i]), &radius[i]))
364 /* radius[i] = calc_radius(*(top->atoms.atomname[i])); */
365 radius[i] += solsize;
369 fprintf(stderr, "WARNING: could not find a Van der Waals radius for %d atoms\n", ndefault);
371 /* Determine which atom is counted as hydrophobic */
375 for (i = 0; (i < nx[0]); i++)
378 for (j = 0; (j < nphobic); j++)
390 if (npcheck != nphobic)
392 gmx_fatal(FARGS, "Consistency check failed: not all %d atoms in the hydrophobic index\n"
393 "found in the normal index selection (%d atoms)", nphobic, npcheck);
401 for (i = 0; (i < nx[0]); i++)
406 bPhobic[i] = fabs(atoms->atom[ii].q) <= qcut;
407 if (bPhobic[i] && bOut[ii])
414 if (!gmx_atomprop_query(aps, epropDGsol,
415 *(atoms->resinfo[atoms->atom[ii].resind].name),
416 *(atoms->atomtype[ii]), &(dgs_factor[i])))
418 dgs_factor[i] = dgs_default;
423 fprintf(debug, "Atom %5d %5s-%5s: q= %6.3f, r= %6.3f, dgsol= %6.3f, hydrophobic= %s\n",
424 ii+1, *(atoms->resinfo[atoms->atom[ii].resind].name),
425 *(atoms->atomname[ii]),
426 atoms->atom[ii].q, radius[ii]-solsize, dgs_factor[i],
430 fprintf(stderr, "%d out of %d atoms were classified as hydrophobic\n",
433 fp = xvgropen(opt2fn("-o", nfile, fnm), "Solvent Accessible Surface", "Time (ps)",
434 "Area (nm\\S2\\N)", oenv);
435 xvgr_legend(fp, asize(flegend) - (bDGsol ? 0 : 1), flegend, oenv);
436 vfile = opt2fn_null("-tv", nfile, fnm);
441 gmx_fatal(FARGS, "Need a tpr file for option -tv");
443 vp = xvgropen(vfile, "Volume and Density", "Time (ps)", "", oenv);
444 xvgr_legend(vp, asize(vlegend), vlegend, oenv);
447 for (i = 0; (i < nx[0]); i++)
452 if (!query_atomprop(atomprop,epropMass,
453 *(top->atoms.resname[top->atoms.atom[ii].resnr]),
454 *(top->atoms.atomname[ii]),&mm))
458 totmass += atoms->atom[ii].m;
462 fprintf(stderr, "WARNING: Using %d default masses for density calculation, which most likely are inaccurate\n", ndefault);
470 gmx_atomprop_destroy(aps);
474 gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms);
482 gmx_rmpbc(gpbc, natoms, box, x);
485 bConnelly = (nfr == 0 && opt2bSet("-q", nfile, fnm));
490 gmx_fatal(FARGS, "Need a tpr file for Connelly plot");
492 flag = FLAG_ATOM_AREA | FLAG_DOTS;
496 flag = FLAG_ATOM_AREA;
500 flag = flag | FLAG_VOLUME;
505 write_sto_conf("check.pdb", "pbc check", atoms, x, NULL, ePBC, box);
508 retval = nsc_dclm_pbc(x, radius, nx[0], ndots, flag, &totarea,
509 &area, &totvolume, &surfacedots, &nsurfacedots,
510 index[0], ePBC, bPBC ? box : NULL);
513 gmx_fatal(FARGS, "Something wrong in nsc_dclm_pbc");
518 connelly_plot(ftp2fn(efPDB, nfile, fnm),
519 nsurfacedots, surfacedots, x, atoms,
520 &(top.symtab), ePBC, box, bSave);
527 for (i = 0; i < atoms->nres; i++)
532 for (i = 0; (i < nx[0]); i++)
540 atom_area[i] += at_area;
541 atom_area2[i] += sqr(at_area);
542 res_a[atoms->atom[ii].resind] += at_area;
547 dgsolv += at_area*dgs_factor[i];
557 for (i = 0; i < atoms->nres; i++)
559 res_area[i] += res_a[i];
560 res_area2[i] += sqr(res_a[i]);
563 fprintf(fp, "%10g %10g %10g %10g", t, harea, tarea-harea, tarea);
566 fprintf(fp, " %10g\n", dgsolv);
576 density = totmass*AMU/(totvolume*NANO*NANO*NANO);
577 fprintf(vp, "%12.5e %12.5e %12.5e\n", t, totvolume, density);
591 while (read_next_x(oenv, status, &t, x, box));
595 gmx_rmpbc_done(gpbc);
598 fprintf(stderr, "\n");
606 /* if necessary, print areas per atom to file too: */
609 for (i = 0; i < atoms->nres; i++)
614 for (i = 0; i < nx[0]; i++)
617 atom_area2[i] /= nfr;
619 fprintf(stderr, "Printing out areas per atom\n");
620 fp = xvgropen(opt2fn("-or", nfile, fnm), "Area per residue over the trajectory", "Residue",
621 "Area (nm\\S2\\N)", oenv);
622 xvgr_legend(fp, asize(or_and_oa_legend), or_and_oa_legend, oenv);
623 fp2 = xvgropen(opt2fn("-oa", nfile, fnm), "Area per atom over the trajectory", "Atom #",
624 "Area (nm\\S2\\N)", oenv);
625 xvgr_legend(fp2, asize(or_and_oa_legend), or_and_oa_legend, oenv);
628 fp3 = ftp2FILE(efITP, nfile, fnm, "w");
629 fprintf(fp3, "[ position_restraints ]\n"
633 "; Atom Type fx fy fz\n");
635 for (i = 0; i < nx[0]; i++)
638 res = atoms->atom[ii].resind;
639 if (i == nx[0]-1 || res != atoms->atom[index[0][i+1]].resind)
641 fluc2 = res_area2[res]-sqr(res_area[res]);
646 fprintf(fp, "%10d %10g %10g\n",
647 atoms->resinfo[res].nr, res_area[res], sqrt(fluc2));
649 fluc2 = atom_area2[i]-sqr(atom_area[i]);
654 fprintf(fp2, "%d %g %g\n", index[0][i]+1, atom_area[i], sqrt(fluc2));
655 if (bITP && (atom_area[i] > minarea))
657 fprintf(fp3, "%5d 1 FCX FCX FCZ\n", ii+1);
667 /* Be a good citizen, keep our memory free! */
670 for (i = 0; i < 2; i++)
693 int gmx_sas(int argc, char *argv[])
695 const char *desc[] = {
696 "[THISMODULE] computes hydrophobic, hydrophilic and total solvent",
697 "accessible surface area. See Eisenhaber F, Lijnzaad P, Argos P,",
698 "Sander C, & Scharf M (1995) J. Comput. Chem. 16, 273-284.",
699 "As a side effect, the Connolly surface can be generated as well in",
700 "a [TT].pdb[tt] file where the nodes are represented as atoms and the",
701 "vertice connecting the nearest nodes as CONECT records.",
702 "The program will ask for a group for the surface calculation",
703 "and a group for the output. The calculation group should always",
704 "consists of all the non-solvent atoms in the system.",
705 "The output group can be the whole or part of the calculation group.",
706 "The average and standard deviation of the area over the trajectory can be plotted",
707 "per residue and atom as well (options [TT]-or[tt] and [TT]-oa[tt]).",
708 "In combination with the latter option an [TT].itp[tt] file can be",
709 "generated (option [TT]-i[tt])",
710 "which can be used to restrain surface atoms.[PAR]",
712 "By default, periodic boundary conditions are taken into account,",
713 "this can be turned off using the [TT]-nopbc[tt] option.[PAR]",
715 "With the [TT]-tv[tt] option the total volume and density of the",
716 "molecule can be computed.",
717 "Please consider whether the normal probe radius is appropriate",
718 "in this case or whether you would rather use e.g. 0. It is good",
719 "to keep in mind that the results for volume and density are very",
720 "approximate. For example, in ice Ih, one can easily fit water molecules in the",
721 "pores which would yield a volume that is too low, and surface area and density",
722 "that are both too high."
726 static real solsize = 0.14;
727 static int ndots = 24;
728 static real qcut = 0.2;
729 static real minarea = 0.5, dgs_default = 0;
730 static gmx_bool bSave = TRUE, bPBC = TRUE, bFindex = FALSE;
732 { "-probe", FALSE, etREAL, {&solsize},
733 "Radius of the solvent probe (nm)" },
734 { "-ndots", FALSE, etINT, {&ndots},
735 "Number of dots per sphere, more dots means more accuracy" },
736 { "-qmax", FALSE, etREAL, {&qcut},
737 "The maximum charge (e, absolute value) of a hydrophobic atom" },
738 { "-f_index", FALSE, etBOOL, {&bFindex},
739 "Determine from a group in the index file what are the hydrophobic atoms rather than from the charge" },
740 { "-minarea", FALSE, etREAL, {&minarea},
741 "The minimum area (nm^2) to count an atom as a surface atom when writing a position restraint file (see help)" },
742 { "-pbc", FALSE, etBOOL, {&bPBC},
743 "Take periodicity into account" },
744 { "-prot", FALSE, etBOOL, {&bSave},
745 "Output the protein to the Connelly [TT].pdb[tt] file too" },
746 { "-dgs", FALSE, etREAL, {&dgs_default},
747 "Default value for solvation free energy per area (kJ/mol/nm^2)" }
750 { efTRX, "-f", NULL, ffREAD },
751 { efTPS, "-s", NULL, ffREAD },
752 { efXVG, "-o", "area", ffWRITE },
753 { efXVG, "-or", "resarea", ffOPTWR },
754 { efXVG, "-oa", "atomarea", ffOPTWR },
755 { efXVG, "-tv", "volume", ffOPTWR },
756 { efPDB, "-q", "connelly", ffOPTWR },
757 { efNDX, "-n", "index", ffOPTRD },
758 { efITP, "-i", "surfat", ffOPTWR }
760 #define NFILE asize(fnm)
762 if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
763 NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
770 fprintf(stderr, "Probe size too small, setting it to %g\n", solsize);
775 fprintf(stderr, "Ndots too small, setting it to %d\n", ndots);
778 please_cite(stderr, "Eisenhaber95");
780 sas_plot(NFILE, fnm, solsize, ndots, qcut, bSave, minarea, bPBC, dgs_default, bFindex,
783 do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
784 do_view(oenv, opt2fn_null("-or", NFILE, fnm), "-nxy");
785 do_view(oenv, opt2fn_null("-oa", NFILE, fnm), "-nxy");