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-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2017,2018,2019, 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.
44 #include "gromacs/commandline/pargs.h"
45 #include "gromacs/commandline/viewit.h"
46 #include "gromacs/fileio/tpxio.h"
47 #include "gromacs/fileio/trxio.h"
48 #include "gromacs/fileio/xvgr.h"
49 #include "gromacs/gmxana/gmx_ana.h"
50 #include "gromacs/linearalgebra/nrjac.h"
51 #include "gromacs/math/vec.h"
52 #include "gromacs/pbcutil/rmpbc.h"
53 #include "gromacs/topology/index.h"
54 #include "gromacs/topology/topology.h"
55 #include "gromacs/utility/arraysize.h"
56 #include "gromacs/utility/cstringutil.h"
57 #include "gromacs/utility/futil.h"
58 #include "gromacs/utility/smalloc.h"
60 static void gyro_eigen(double** gyr, double* eig, double** eigv, int* ord)
64 jacobi(gyr, DIM, eig, eigv, &nrot);
65 /* Order the eigenvalues */
68 for (d = 0; d < DIM; d++)
70 if (eig[d] > eig[ord[0]])
74 if (eig[d] < eig[ord[2]])
79 for (d = 0; d < DIM; d++)
81 if (ord[0] != d && ord[2] != d)
88 /* Calculate mean square internal distances (Auhl et al., JCP 119, 12718) */
89 static void calc_int_dist(double* intd, rvec* x, int i0, int i1)
92 const int ml = i1 - i0 + 1; /* Number of beads in molecule. */
93 int bd; /* Distance between beads */
96 for (bd = 1; bd < ml; bd++)
99 for (ii = i0; ii <= i1 - bd; ii++)
101 d += distance2(x[ii], x[ii + bd]);
108 int gmx_polystat(int argc, char* argv[])
110 const char* desc[] = {
111 "[THISMODULE] plots static properties of polymers as a function of time",
112 "and prints the average.[PAR]",
113 "By default it determines the average end-to-end distance and radii",
114 "of gyration of polymers. It asks for an index group and split this",
115 "into molecules. The end-to-end distance is then determined using",
116 "the first and the last atom in the index group for each molecules.",
117 "For the radius of gyration the total and the three principal components",
118 "for the average gyration tensor are written.",
119 "With option [TT]-v[tt] the eigenvectors are written.",
120 "With option [TT]-pc[tt] also the average eigenvalues of the individual",
121 "gyration tensors are written.",
122 "With option [TT]-i[tt] the mean square internal distances are",
124 "With option [TT]-p[tt] the persistence length is determined.",
125 "The chosen index group should consist of atoms that are",
126 "consecutively bonded in the polymer mainchains.",
127 "The persistence length is then determined from the cosine of",
128 "the angles between bonds with an index difference that is even,",
129 "the odd pairs are not used, because straight polymer backbones",
130 "are usually all trans and therefore only every second bond aligns.",
131 "The persistence length is defined as number of bonds where",
132 "the average cos reaches a value of 1/e. This point is determined",
133 "by a linear interpolation of [LOG]<cos>[log]."
135 static gmx_bool bMW = TRUE, bPC = FALSE;
137 { "-mw", FALSE, etBOOL, { &bMW }, "Use the mass weighting for radii of gyration" },
138 { "-pc", FALSE, etBOOL, { &bPC }, "Plot average eigenvalues" }
141 t_filenm fnm[] = { { efTPR, nullptr, nullptr, ffREAD }, { efTRX, "-f", nullptr, ffREAD },
142 { efNDX, nullptr, nullptr, ffOPTRD }, { efXVG, "-o", "polystat", ffWRITE },
143 { efXVG, "-v", "polyvec", ffOPTWR }, { efXVG, "-p", "persist", ffOPTWR },
144 { efXVG, "-i", "intdist", ffOPTWR } };
145 #define NFILE asize(fnm)
148 gmx_output_env_t* oenv;
150 int isize, *index, nmol, *molind, mol, nat_min = 0, nat_max = 0;
154 rvec * x, *bond = nullptr;
156 int natoms, i, j, frame, ind0, ind1, a, d, d2, ord[DIM] = { 0 };
157 dvec cm, sum_eig = { 0, 0, 0 };
158 double ** gyr, **gyr_all, eig[DIM], **eigv;
159 double sum_eed2, sum_eed2_tot, sum_gyro, sum_gyro_tot, sum_pers_tot;
161 double * sum_inp = nullptr, pers;
162 double * intd, ymax, ymin;
165 FILE * out, *outv, *outp, *outi;
166 const char* leg[8] = { "end to end", "<R\\sg\\N>", "<R\\sg\\N> eig1",
167 "<R\\sg\\N> eig2", "<R\\sg\\N> eig3", "<R\\sg\\N eig1>",
168 "<R\\sg\\N eig2>", "<R\\sg\\N eig3>" };
169 char ** legp, buf[STRLEN];
170 gmx_rmpbc_t gpbc = nullptr;
172 if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT, NFILE, fnm,
173 asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
179 ePBC = read_tpx_top(ftp2fn(efTPR, NFILE, fnm), nullptr, box, &natoms, nullptr, nullptr, top);
181 fprintf(stderr, "Select a group of polymer mainchain atoms:\n");
182 get_index(&top->atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
184 snew(molind, top->mols.nr + 1);
187 for (i = 0; i < isize; i++)
189 if (i == 0 || index[i] >= top->mols.index[mol + 1])
195 } while (index[i] >= top->mols.index[mol + 1]);
199 nat_min = top->atoms.nr;
201 for (mol = 0; mol < nmol; mol++)
203 nat_min = std::min(nat_min, molind[mol + 1] - molind[mol]);
204 nat_max = std::max(nat_max, molind[mol + 1] - molind[mol]);
206 fprintf(stderr, "Group %s consists of %d molecules\n", grpname, nmol);
207 fprintf(stderr, "Group size per molecule, min: %d atoms, max %d atoms\n", nat_min, nat_max);
209 sprintf(title, "Size of %d polymers", nmol);
210 out = xvgropen(opt2fn("-o", NFILE, fnm), title, output_env_get_xvgr_tlabel(oenv), "(nm)", oenv);
211 xvgr_legend(out, bPC ? 8 : 5, leg, oenv);
213 if (opt2bSet("-v", NFILE, fnm))
215 outv = xvgropen(opt2fn("-v", NFILE, fnm), "Principal components",
216 output_env_get_xvgr_tlabel(oenv), "(nm)", oenv);
217 snew(legp, DIM * DIM);
218 for (d = 0; d < DIM; d++)
220 for (d2 = 0; d2 < DIM; d2++)
222 sprintf(buf, "eig%d %c", d + 1, 'x' + d2);
223 legp[d * DIM + d2] = gmx_strdup(buf);
226 xvgr_legend(outv, DIM * DIM, legp, oenv);
233 if (opt2bSet("-p", NFILE, fnm))
235 outp = xvgropen(opt2fn("-p", NFILE, fnm), "Persistence length",
236 output_env_get_xvgr_tlabel(oenv), "bonds", oenv);
237 snew(bond, nat_max - 1);
238 snew(sum_inp, nat_min / 2);
239 snew(ninp, nat_min / 2);
246 if (opt2bSet("-i", NFILE, fnm))
248 outi = xvgropen(opt2fn("-i", NFILE, fnm), "Internal distances", "n",
249 "<R\\S2\\N(n)>/n (nm\\S2\\N)", oenv);
250 i = index[molind[1] - 1] - index[molind[0]]; /* Length of polymer -1 */
259 natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
264 for (d = 0; d < DIM; d++)
267 snew(gyr_all[d], DIM);
276 gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
280 gmx_rmpbc(gpbc, natoms, box, x);
283 for (d = 0; d < DIM; d++)
285 clear_dvec(gyr_all[d]);
295 for (i = 0; i < nat_min / 2; i++)
302 for (mol = 0; mol < nmol; mol++)
305 ind1 = molind[mol + 1];
307 /* Determine end to end distance */
308 sum_eed2 += distance2(x[index[ind0]], x[index[ind1 - 1]]);
310 /* Determine internal distances */
313 calc_int_dist(intd, x, index[ind0], index[ind1 - 1]);
316 /* Determine the radius of gyration */
318 for (d = 0; d < DIM; d++)
324 for (i = ind0; i < ind1; i++)
329 m = top->atoms.atom[a].m;
336 for (d = 0; d < DIM; d++)
338 cm[d] += m * x[a][d];
339 for (d2 = 0; d2 < DIM; d2++)
341 gyr[d][d2] += m * x[a][d] * x[a][d2];
345 dsvmul(1 / mmol, cm, cm);
346 for (d = 0; d < DIM; d++)
348 for (d2 = 0; d2 < DIM; d2++)
350 gyr[d][d2] = gyr[d][d2] / mmol - cm[d] * cm[d2];
351 gyr_all[d][d2] += gyr[d][d2];
356 gyro_eigen(gyr, eig, eigv, ord);
357 for (d = 0; d < DIM; d++)
359 sum_eig[d] += eig[ord[d]];
364 for (i = ind0; i < ind1 - 1; i++)
366 rvec_sub(x[index[i + 1]], x[index[i]], bond[i - ind0]);
367 unitv(bond[i - ind0], bond[i - ind0]);
369 for (i = ind0; i < ind1 - 1; i++)
371 for (j = 0; (i + j < ind1 - 1 && j < nat_min / 2); j += 2)
373 sum_inp[j] += iprod(bond[i - ind0], bond[i - ind0 + j]);
382 for (d = 0; d < DIM; d++)
384 for (d2 = 0; d2 < DIM; d2++)
386 gyr_all[d][d2] /= nmol;
388 sum_gyro += gyr_all[d][d];
391 gyro_eigen(gyr_all, eig, eigv, ord);
393 fprintf(out, "%10.3f %8.4f %8.4f %8.4f %8.4f %8.4f", t * output_env_get_time_factor(oenv),
394 std::sqrt(sum_eed2), sqrt(sum_gyro), std::sqrt(eig[ord[0]]), std::sqrt(eig[ord[1]]),
395 std::sqrt(eig[ord[2]]));
398 for (d = 0; d < DIM; d++)
400 fprintf(out, " %8.4f", std::sqrt(sum_eig[d] / nmol));
407 fprintf(outv, "%10.3f", t * output_env_get_time_factor(oenv));
408 for (d = 0; d < DIM; d++)
410 for (d2 = 0; d2 < DIM; d2++)
412 fprintf(outv, " %6.3f", eigv[ord[d]][d2]);
418 sum_eed2_tot += sum_eed2;
419 sum_gyro_tot += sum_gyro;
424 for (j = 0; j < nat_min / 2; j += 2)
426 sum_inp[j] /= ninp[j];
427 if (i == -1 && sum_inp[j] <= std::exp(-1.0))
438 /* Do linear interpolation on a log scale */
440 + 2.0 * (std::log(sum_inp[i - 2]) + 1.0)
441 / (std::log(sum_inp[i - 2]) - std::log(sum_inp[i]));
443 fprintf(outp, "%10.3f %8.4f\n", t * output_env_get_time_factor(oenv), pers);
444 sum_pers_tot += pers;
448 } while (read_next_x(oenv, status, &t, x, box));
450 gmx_rmpbc_done(gpbc);
464 sum_eed2_tot /= frame;
465 sum_gyro_tot /= frame;
466 sum_pers_tot /= frame;
467 fprintf(stdout, "\nAverage end to end distance: %.3f (nm)\n", std::sqrt(sum_eed2_tot));
468 fprintf(stdout, "\nAverage radius of gyration: %.3f (nm)\n", std::sqrt(sum_gyro_tot));
469 if (opt2bSet("-p", NFILE, fnm))
471 fprintf(stdout, "\nAverage persistence length: %.2f bonds\n", sum_pers_tot);
474 /* Handle printing of internal distances. */
477 if (output_env_get_print_xvgr_codes(oenv))
479 fprintf(outi, "@ xaxes scale Logarithmic\n");
483 j = index[molind[1] - 1] - index[molind[0]]; /* Polymer length -1. */
484 for (i = 0; i < j; i++)
486 intd[i] /= (i + 1) * frame * nmol;
496 xvgr_world(outi, 1, ymin, j, ymax, oenv);
497 for (i = 0; i < j; i++)
499 fprintf(outi, "%d %8.4f\n", i + 1, intd[i]);
504 do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
505 if (opt2bSet("-v", NFILE, fnm))
507 do_view(oenv, opt2fn("-v", NFILE, fnm), "-nxy");
509 if (opt2bSet("-p", NFILE, fnm))
511 do_view(oenv, opt2fn("-p", NFILE, fnm), "-nxy");