Update instructions in containers.rst
[gromacs.git] / src / gromacs / gmxana / gmx_polystat.cpp
blobee1cfe1af4e367e400a820bcde332bd361fc6ad9
1 /*
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.
38 #include "gmxpre.h"
40 #include <cmath>
41 #include <cstring>
43 #include <algorithm>
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)
63 int nrot, d;
65 jacobi(gyr, DIM, eig, eigv, &nrot);
66 /* Order the eigenvalues */
67 ord[0] = 0;
68 ord[2] = 2;
69 for (d = 0; d < DIM; d++)
71 if (eig[d] > eig[ord[0]])
73 ord[0] = d;
75 if (eig[d] < eig[ord[2]])
77 ord[2] = d;
80 for (d = 0; d < DIM; d++)
82 if (ord[0] != d && ord[2] != d)
84 ord[1] = 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)
92 int ii;
93 const int ml = i1 - i0 + 1; /* Number of beads in molecule. */
94 int bd; /* Distance between beads */
95 double d;
97 for (bd = 1; bd < ml; bd++)
99 d = 0.;
100 for (ii = i0; ii <= i1 - bd; ii++)
102 d += distance2(x[ii], x[ii + bd]);
104 d /= ml - bd;
105 intd[bd - 1] += d;
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",
124 "written.[PAR]",
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;
137 t_pargs pa[] = {
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)
148 t_topology* top;
149 gmx_output_env_t* oenv;
150 PbcType pbcType;
151 int isize, *index, nmol, *molind, mol, nat_min = 0, nat_max = 0;
152 char* grpname;
153 t_trxstatus* status;
154 real t;
155 rvec * x, *bond = nullptr;
156 matrix box;
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;
161 int* ninp = nullptr;
162 double * sum_inp = nullptr, pers;
163 double * intd, ymax, ymin;
164 double mmol, m;
165 char title[STRLEN];
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, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT, NFILE, fnm,
174 asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
176 return 0;
179 snew(top, 1);
180 pbcType = read_tpx_top(ftp2fn(efTPR, NFILE, fnm), nullptr, box, &natoms, nullptr, nullptr, top);
182 fprintf(stderr, "Select a group of polymer mainchain atoms:\n");
183 get_index(&top->atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
185 snew(molind, top->mols.nr + 1);
186 nmol = 0;
187 mol = -1;
188 for (i = 0; i < isize; i++)
190 if (i == 0 || index[i] >= top->mols.index[mol + 1])
192 molind[nmol++] = i;
195 mol++;
196 } while (index[i] >= top->mols.index[mol + 1]);
199 molind[nmol] = i;
200 nat_min = top->atoms.nr;
201 nat_max = 0;
202 for (mol = 0; mol < nmol; mol++)
204 nat_min = std::min(nat_min, molind[mol + 1] - molind[mol]);
205 nat_max = std::max(nat_max, molind[mol + 1] - molind[mol]);
207 fprintf(stderr, "Group %s consists of %d molecules\n", grpname, nmol);
208 fprintf(stderr, "Group size per molecule, min: %d atoms, max %d atoms\n", nat_min, nat_max);
210 sprintf(title, "Size of %d polymers", nmol);
211 out = xvgropen(opt2fn("-o", NFILE, fnm), title, output_env_get_xvgr_tlabel(oenv), "(nm)", oenv);
212 xvgr_legend(out, bPC ? 8 : 5, leg, oenv);
214 if (opt2bSet("-v", NFILE, fnm))
216 outv = xvgropen(opt2fn("-v", NFILE, fnm), "Principal components",
217 output_env_get_xvgr_tlabel(oenv), "(nm)", oenv);
218 snew(legp, DIM * DIM);
219 for (d = 0; d < DIM; d++)
221 for (d2 = 0; d2 < DIM; d2++)
223 sprintf(buf, "eig%d %c", d + 1, 'x' + d2);
224 legp[d * DIM + d2] = gmx_strdup(buf);
227 xvgr_legend(outv, DIM * DIM, legp, oenv);
229 else
231 outv = nullptr;
234 if (opt2bSet("-p", NFILE, fnm))
236 outp = xvgropen(opt2fn("-p", NFILE, fnm), "Persistence length",
237 output_env_get_xvgr_tlabel(oenv), "bonds", oenv);
238 snew(bond, nat_max - 1);
239 snew(sum_inp, nat_min / 2);
240 snew(ninp, nat_min / 2);
242 else
244 outp = nullptr;
247 if (opt2bSet("-i", NFILE, fnm))
249 outi = xvgropen(opt2fn("-i", NFILE, fnm), "Internal distances", "n",
250 "<R\\S2\\N(n)>/n (nm\\S2\\N)", oenv);
251 i = index[molind[1] - 1] - index[molind[0]]; /* Length of polymer -1 */
252 snew(intd, i);
254 else
256 intd = nullptr;
257 outi = nullptr;
260 natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
262 snew(gyr, DIM);
263 snew(gyr_all, DIM);
264 snew(eigv, DIM);
265 for (d = 0; d < DIM; d++)
267 snew(gyr[d], DIM);
268 snew(gyr_all[d], DIM);
269 snew(eigv[d], DIM);
272 frame = 0;
273 sum_eed2_tot = 0;
274 sum_gyro_tot = 0;
275 sum_pers_tot = 0;
277 gpbc = gmx_rmpbc_init(&top->idef, pbcType, natoms);
281 gmx_rmpbc(gpbc, natoms, box, x);
283 sum_eed2 = 0;
284 for (d = 0; d < DIM; d++)
286 clear_dvec(gyr_all[d]);
289 if (bPC)
291 clear_dvec(sum_eig);
294 if (outp)
296 for (i = 0; i < nat_min / 2; i++)
298 sum_inp[i] = 0;
299 ninp[i] = 0;
303 for (mol = 0; mol < nmol; mol++)
305 ind0 = molind[mol];
306 ind1 = molind[mol + 1];
308 /* Determine end to end distance */
309 sum_eed2 += distance2(x[index[ind0]], x[index[ind1 - 1]]);
311 /* Determine internal distances */
312 if (outi)
314 calc_int_dist(intd, x, index[ind0], index[ind1 - 1]);
317 /* Determine the radius of gyration */
318 clear_dvec(cm);
319 for (d = 0; d < DIM; d++)
321 clear_dvec(gyr[d]);
323 mmol = 0;
325 for (i = ind0; i < ind1; i++)
327 a = index[i];
328 if (bMW)
330 m = top->atoms.atom[a].m;
332 else
334 m = 1;
336 mmol += m;
337 for (d = 0; d < DIM; d++)
339 cm[d] += m * x[a][d];
340 for (d2 = 0; d2 < DIM; d2++)
342 gyr[d][d2] += m * x[a][d] * x[a][d2];
346 dsvmul(1 / mmol, cm, cm);
347 for (d = 0; d < DIM; d++)
349 for (d2 = 0; d2 < DIM; d2++)
351 gyr[d][d2] = gyr[d][d2] / mmol - cm[d] * cm[d2];
352 gyr_all[d][d2] += gyr[d][d2];
355 if (bPC)
357 gyro_eigen(gyr, eig, eigv, ord);
358 for (d = 0; d < DIM; d++)
360 sum_eig[d] += eig[ord[d]];
363 if (outp)
365 for (i = ind0; i < ind1 - 1; i++)
367 rvec_sub(x[index[i + 1]], x[index[i]], bond[i - ind0]);
368 unitv(bond[i - ind0], bond[i - ind0]);
370 for (i = ind0; i < ind1 - 1; i++)
372 for (j = 0; (i + j < ind1 - 1 && j < nat_min / 2); j += 2)
374 sum_inp[j] += iprod(bond[i - ind0], bond[i - ind0 + j]);
375 ninp[j]++;
380 sum_eed2 /= nmol;
382 sum_gyro = 0;
383 for (d = 0; d < DIM; d++)
385 for (d2 = 0; d2 < DIM; d2++)
387 gyr_all[d][d2] /= nmol;
389 sum_gyro += gyr_all[d][d];
392 gyro_eigen(gyr_all, eig, eigv, ord);
394 fprintf(out, "%10.3f %8.4f %8.4f %8.4f %8.4f %8.4f", t * output_env_get_time_factor(oenv),
395 std::sqrt(sum_eed2), sqrt(sum_gyro), std::sqrt(eig[ord[0]]), std::sqrt(eig[ord[1]]),
396 std::sqrt(eig[ord[2]]));
397 if (bPC)
399 for (d = 0; d < DIM; d++)
401 fprintf(out, " %8.4f", std::sqrt(sum_eig[d] / nmol));
404 fprintf(out, "\n");
406 if (outv)
408 fprintf(outv, "%10.3f", t * output_env_get_time_factor(oenv));
409 for (d = 0; d < DIM; d++)
411 for (d2 = 0; d2 < DIM; d2++)
413 fprintf(outv, " %6.3f", eigv[ord[d]][d2]);
416 fprintf(outv, "\n");
419 sum_eed2_tot += sum_eed2;
420 sum_gyro_tot += sum_gyro;
422 if (outp)
424 i = -1;
425 for (j = 0; j < nat_min / 2; j += 2)
427 sum_inp[j] /= ninp[j];
428 if (i == -1 && sum_inp[j] <= std::exp(-1.0))
430 i = j;
433 if (i == -1)
435 pers = j;
437 else
439 /* Do linear interpolation on a log scale */
440 pers = i - 2.0
441 + 2.0 * (std::log(sum_inp[i - 2]) + 1.0)
442 / (std::log(sum_inp[i - 2]) - std::log(sum_inp[i]));
444 fprintf(outp, "%10.3f %8.4f\n", t * output_env_get_time_factor(oenv), pers);
445 sum_pers_tot += pers;
448 frame++;
449 } while (read_next_x(oenv, status, &t, x, box));
451 gmx_rmpbc_done(gpbc);
453 close_trx(status);
455 xvgrclose(out);
456 if (outv)
458 xvgrclose(outv);
460 if (outp)
462 xvgrclose(outp);
465 sum_eed2_tot /= frame;
466 sum_gyro_tot /= frame;
467 sum_pers_tot /= frame;
468 fprintf(stdout, "\nAverage end to end distance: %.3f (nm)\n", std::sqrt(sum_eed2_tot));
469 fprintf(stdout, "\nAverage radius of gyration: %.3f (nm)\n", std::sqrt(sum_gyro_tot));
470 if (opt2bSet("-p", NFILE, fnm))
472 fprintf(stdout, "\nAverage persistence length: %.2f bonds\n", sum_pers_tot);
475 /* Handle printing of internal distances. */
476 if (outi)
478 if (output_env_get_print_xvgr_codes(oenv))
480 fprintf(outi, "@ xaxes scale Logarithmic\n");
482 ymax = -1;
483 ymin = 1e300;
484 j = index[molind[1] - 1] - index[molind[0]]; /* Polymer length -1. */
485 for (i = 0; i < j; i++)
487 intd[i] /= (i + 1) * frame * nmol;
488 if (intd[i] > ymax)
490 ymax = intd[i];
492 if (intd[i] < ymin)
494 ymin = intd[i];
497 xvgr_world(outi, 1, ymin, j, ymax, oenv);
498 for (i = 0; i < j; i++)
500 fprintf(outi, "%d %8.4f\n", i + 1, intd[i]);
502 xvgrclose(outi);
505 do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
506 if (opt2bSet("-v", NFILE, fnm))
508 do_view(oenv, opt2fn("-v", NFILE, fnm), "-nxy");
510 if (opt2bSet("-p", NFILE, fnm))
512 do_view(oenv, opt2fn("-p", NFILE, fnm), "-nxy");
515 return 0;