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 by the GROMACS development team.
7 * Copyright (c) 2019,2020, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
45 #include "gromacs/commandline/pargs.h"
46 #include "gromacs/commandline/viewit.h"
47 #include "gromacs/fileio/tpxio.h"
48 #include "gromacs/fileio/trxio.h"
49 #include "gromacs/fileio/xvgr.h"
50 #include "gromacs/gmxana/gmx_ana.h"
51 #include "gromacs/linearalgebra/nrjac.h"
52 #include "gromacs/math/vec.h"
53 #include "gromacs/pbcutil/rmpbc.h"
54 #include "gromacs/topology/index.h"
55 #include "gromacs/topology/topology.h"
56 #include "gromacs/utility/arraysize.h"
57 #include "gromacs/utility/cstringutil.h"
58 #include "gromacs/utility/futil.h"
59 #include "gromacs/utility/smalloc.h"
61 static void gyro_eigen(double** gyr, double* eig, double** eigv, int* ord)
65 jacobi(gyr, DIM, eig, eigv, &nrot);
66 /* Order the eigenvalues */
69 for (d = 0; d < DIM; d++)
71 if (eig[d] > eig[ord[0]])
75 if (eig[d] < eig[ord[2]])
80 for (d = 0; d < DIM; d++)
82 if (ord[0] != d && ord[2] != d)
89 /* Calculate mean square internal distances (Auhl et al., JCP 119, 12718) */
90 static void calc_int_dist(double* intd, rvec* x, int i0, int i1)
93 const int ml = i1 - i0 + 1; /* Number of beads in molecule. */
94 int bd; /* Distance between beads */
97 for (bd = 1; bd < ml; bd++)
100 for (ii = i0; ii <= i1 - bd; ii++)
102 d += distance2(x[ii], x[ii + bd]);
109 int gmx_polystat(int argc, char* argv[])
111 const char* desc[] = {
112 "[THISMODULE] plots static properties of polymers as a function of time",
113 "and prints the average.[PAR]",
114 "By default it determines the average end-to-end distance and radii",
115 "of gyration of polymers. It asks for an index group and split this",
116 "into molecules. The end-to-end distance is then determined using",
117 "the first and the last atom in the index group for each molecules.",
118 "For the radius of gyration the total and the three principal components",
119 "for the average gyration tensor are written.",
120 "With option [TT]-v[tt] the eigenvectors are written.",
121 "With option [TT]-pc[tt] also the average eigenvalues of the individual",
122 "gyration tensors are written.",
123 "With option [TT]-i[tt] the mean square internal distances are",
125 "With option [TT]-p[tt] the persistence length is determined.",
126 "The chosen index group should consist of atoms that are",
127 "consecutively bonded in the polymer mainchains.",
128 "The persistence length is then determined from the cosine of",
129 "the angles between bonds with an index difference that is even,",
130 "the odd pairs are not used, because straight polymer backbones",
131 "are usually all trans and therefore only every second bond aligns.",
132 "The persistence length is defined as number of bonds where",
133 "the average cos reaches a value of 1/e. This point is determined",
134 "by a linear interpolation of [LOG]<cos>[log]."
136 static gmx_bool bMW = TRUE, bPC = FALSE;
138 { "-mw", FALSE, etBOOL, { &bMW }, "Use the mass weighting for radii of gyration" },
139 { "-pc", FALSE, etBOOL, { &bPC }, "Plot average eigenvalues" }
142 t_filenm fnm[] = { { efTPR, nullptr, nullptr, ffREAD }, { efTRX, "-f", nullptr, ffREAD },
143 { efNDX, nullptr, nullptr, ffOPTRD }, { efXVG, "-o", "polystat", ffWRITE },
144 { efXVG, "-v", "polyvec", ffOPTWR }, { efXVG, "-p", "persist", ffOPTWR },
145 { efXVG, "-i", "intdist", ffOPTWR } };
146 #define NFILE asize(fnm)
149 gmx_output_env_t* oenv;
151 int isize, *index, nmol, *molind, mol, nat_min = 0, nat_max = 0;
155 rvec * x, *bond = nullptr;
157 int natoms, i, j, frame, ind0, ind1, a, d, d2, ord[DIM] = { 0 };
158 dvec cm, sum_eig = { 0, 0, 0 };
159 double ** gyr, **gyr_all, eig[DIM], **eigv;
160 double sum_eed2, sum_eed2_tot, sum_gyro, sum_gyro_tot, sum_pers_tot;
162 double * sum_inp = nullptr, pers;
163 double * intd, ymax, ymin;
166 FILE * out, *outv, *outp, *outi;
167 const char* leg[8] = { "end to end", "<R\\sg\\N>", "<R\\sg\\N> eig1",
168 "<R\\sg\\N> eig2", "<R\\sg\\N> eig3", "<R\\sg\\N eig1>",
169 "<R\\sg\\N eig2>", "<R\\sg\\N eig3>" };
170 char ** legp, buf[STRLEN];
171 gmx_rmpbc_t gpbc = nullptr;
173 if (!parse_common_args(&argc,
175 PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT,
190 pbcType = read_tpx_top(ftp2fn(efTPR, NFILE, fnm), nullptr, box, &natoms, nullptr, nullptr, top);
192 fprintf(stderr, "Select a group of polymer mainchain atoms:\n");
193 get_index(&top->atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
195 snew(molind, top->mols.nr + 1);
198 for (i = 0; i < isize; i++)
200 if (i == 0 || index[i] >= top->mols.index[mol + 1])
206 } while (index[i] >= top->mols.index[mol + 1]);
210 nat_min = top->atoms.nr;
212 for (mol = 0; mol < nmol; mol++)
214 nat_min = std::min(nat_min, molind[mol + 1] - molind[mol]);
215 nat_max = std::max(nat_max, molind[mol + 1] - molind[mol]);
217 fprintf(stderr, "Group %s consists of %d molecules\n", grpname, nmol);
218 fprintf(stderr, "Group size per molecule, min: %d atoms, max %d atoms\n", nat_min, nat_max);
220 sprintf(title, "Size of %d polymers", nmol);
221 out = xvgropen(opt2fn("-o", NFILE, fnm), title, output_env_get_xvgr_tlabel(oenv), "(nm)", oenv);
222 xvgr_legend(out, bPC ? 8 : 5, leg, oenv);
224 if (opt2bSet("-v", NFILE, fnm))
226 outv = xvgropen(opt2fn("-v", NFILE, fnm),
227 "Principal components",
228 output_env_get_xvgr_tlabel(oenv),
231 snew(legp, DIM * DIM);
232 for (d = 0; d < DIM; d++)
234 for (d2 = 0; d2 < DIM; d2++)
236 sprintf(buf, "eig%d %c", d + 1, 'x' + d2);
237 legp[d * DIM + d2] = gmx_strdup(buf);
240 xvgr_legend(outv, DIM * DIM, legp, oenv);
247 if (opt2bSet("-p", NFILE, fnm))
249 outp = xvgropen(opt2fn("-p", NFILE, fnm),
250 "Persistence length",
251 output_env_get_xvgr_tlabel(oenv),
254 snew(bond, nat_max - 1);
255 snew(sum_inp, nat_min / 2);
256 snew(ninp, nat_min / 2);
263 if (opt2bSet("-i", NFILE, fnm))
266 opt2fn("-i", NFILE, fnm), "Internal distances", "n", "<R\\S2\\N(n)>/n (nm\\S2\\N)", oenv);
267 i = index[molind[1] - 1] - index[molind[0]]; /* Length of polymer -1 */
276 natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
281 for (d = 0; d < DIM; d++)
284 snew(gyr_all[d], DIM);
293 gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
297 gmx_rmpbc(gpbc, natoms, box, x);
300 for (d = 0; d < DIM; d++)
302 clear_dvec(gyr_all[d]);
312 for (i = 0; i < nat_min / 2; i++)
319 for (mol = 0; mol < nmol; mol++)
322 ind1 = molind[mol + 1];
324 /* Determine end to end distance */
325 sum_eed2 += distance2(x[index[ind0]], x[index[ind1 - 1]]);
327 /* Determine internal distances */
330 calc_int_dist(intd, x, index[ind0], index[ind1 - 1]);
333 /* Determine the radius of gyration */
335 for (d = 0; d < DIM; d++)
341 for (i = ind0; i < ind1; i++)
346 m = top->atoms.atom[a].m;
353 for (d = 0; d < DIM; d++)
355 cm[d] += m * x[a][d];
356 for (d2 = 0; d2 < DIM; d2++)
358 gyr[d][d2] += m * x[a][d] * x[a][d2];
362 dsvmul(1 / mmol, cm, cm);
363 for (d = 0; d < DIM; d++)
365 for (d2 = 0; d2 < DIM; d2++)
367 gyr[d][d2] = gyr[d][d2] / mmol - cm[d] * cm[d2];
368 gyr_all[d][d2] += gyr[d][d2];
373 gyro_eigen(gyr, eig, eigv, ord);
374 for (d = 0; d < DIM; d++)
376 sum_eig[d] += eig[ord[d]];
381 for (i = ind0; i < ind1 - 1; i++)
383 rvec_sub(x[index[i + 1]], x[index[i]], bond[i - ind0]);
384 unitv(bond[i - ind0], bond[i - ind0]);
386 for (i = ind0; i < ind1 - 1; i++)
388 for (j = 0; (i + j < ind1 - 1 && j < nat_min / 2); j += 2)
390 sum_inp[j] += iprod(bond[i - ind0], bond[i - ind0 + j]);
399 for (d = 0; d < DIM; d++)
401 for (d2 = 0; d2 < DIM; d2++)
403 gyr_all[d][d2] /= nmol;
405 sum_gyro += gyr_all[d][d];
408 gyro_eigen(gyr_all, eig, eigv, ord);
411 "%10.3f %8.4f %8.4f %8.4f %8.4f %8.4f",
412 t * output_env_get_time_factor(oenv),
415 std::sqrt(eig[ord[0]]),
416 std::sqrt(eig[ord[1]]),
417 std::sqrt(eig[ord[2]]));
420 for (d = 0; d < DIM; d++)
422 fprintf(out, " %8.4f", std::sqrt(sum_eig[d] / nmol));
429 fprintf(outv, "%10.3f", t * output_env_get_time_factor(oenv));
430 for (d = 0; d < DIM; d++)
432 for (d2 = 0; d2 < DIM; d2++)
434 fprintf(outv, " %6.3f", eigv[ord[d]][d2]);
440 sum_eed2_tot += sum_eed2;
441 sum_gyro_tot += sum_gyro;
446 for (j = 0; j < nat_min / 2; j += 2)
448 sum_inp[j] /= ninp[j];
449 if (i == -1 && sum_inp[j] <= std::exp(-1.0))
460 /* Do linear interpolation on a log scale */
462 + 2.0 * (std::log(sum_inp[i - 2]) + 1.0)
463 / (std::log(sum_inp[i - 2]) - std::log(sum_inp[i]));
465 fprintf(outp, "%10.3f %8.4f\n", t * output_env_get_time_factor(oenv), pers);
466 sum_pers_tot += pers;
470 } while (read_next_x(oenv, status, &t, x, box));
472 gmx_rmpbc_done(gpbc);
486 sum_eed2_tot /= frame;
487 sum_gyro_tot /= frame;
488 sum_pers_tot /= frame;
489 fprintf(stdout, "\nAverage end to end distance: %.3f (nm)\n", std::sqrt(sum_eed2_tot));
490 fprintf(stdout, "\nAverage radius of gyration: %.3f (nm)\n", std::sqrt(sum_gyro_tot));
491 if (opt2bSet("-p", NFILE, fnm))
493 fprintf(stdout, "\nAverage persistence length: %.2f bonds\n", sum_pers_tot);
496 /* Handle printing of internal distances. */
499 if (output_env_get_print_xvgr_codes(oenv))
501 fprintf(outi, "@ xaxes scale Logarithmic\n");
505 j = index[molind[1] - 1] - index[molind[0]]; /* Polymer length -1. */
506 for (i = 0; i < j; i++)
508 intd[i] /= (i + 1) * frame * nmol;
518 xvgr_world(outi, 1, ymin, j, ymax, oenv);
519 for (i = 0; i < j; i++)
521 fprintf(outi, "%d %8.4f\n", i + 1, intd[i]);
526 do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
527 if (opt2bSet("-v", NFILE, fnm))
529 do_view(oenv, opt2fn("-v", NFILE, fnm), "-nxy");
531 if (opt2bSet("-p", NFILE, fnm))
533 do_view(oenv, opt2fn("-p", NFILE, fnm), "-nxy");