Update instructions in containers.rst
[gromacs.git] / src / gromacs / gmxana / gmx_angle.cpp
blob70f006512aa2ec3f47278245fd1f64fe615aeb05
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,2016,2017 by the GROMACS development team.
7 * Copyright (c) 2018,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/correlationfunctions/autocorr.h"
48 #include "gromacs/fileio/trrio.h"
49 #include "gromacs/fileio/xvgr.h"
50 #include "gromacs/gmxana/angle_correction.h"
51 #include "gromacs/gmxana/gmx_ana.h"
52 #include "gromacs/gmxana/gstat.h"
53 #include "gromacs/math/functions.h"
54 #include "gromacs/math/units.h"
55 #include "gromacs/math/vec.h"
56 #include "gromacs/topology/index.h"
57 #include "gromacs/utility/arraysize.h"
58 #include "gromacs/utility/fatalerror.h"
59 #include "gromacs/utility/futil.h"
60 #include "gromacs/utility/gmxassert.h"
61 #include "gromacs/utility/pleasecite.h"
62 #include "gromacs/utility/smalloc.h"
64 static void dump_dih_trr(int nframes, int nangles, real** dih, const char* fn, real* time)
66 int i, j, k, l, m, na;
67 struct t_fileio* fio;
68 rvec* x;
69 matrix box = { { 2, 0, 0 }, { 0, 2, 0 }, { 0, 0, 2 } };
71 na = (nangles * 2);
72 if ((na % 3) != 0)
74 na = 1 + na / 3;
76 else
78 na = na / 3;
80 printf("There are %d dihedrals. Will fill %d atom positions with cos/sin\n", nangles, na);
81 snew(x, na);
82 fio = gmx_trr_open(fn, "w");
83 for (i = 0; (i < nframes); i++)
85 k = l = 0;
86 for (j = 0; (j < nangles); j++)
88 for (m = 0; (m < 2); m++)
90 // This is just because the compler and static-analyzer cannot
91 // know that dih[j][i] is always valid. Since it occurs in the innermost
92 // loop over angles and will only trigger on coding errors, we
93 // only enable it for debug builds.
94 GMX_ASSERT(dih != nullptr && dih[j] != nullptr, "Incorrect dihedral array data");
95 x[k][l] = (m == 0) ? std::cos(dih[j][i]) : std::sin(dih[j][i]);
96 l++;
97 if (l == DIM)
99 l = 0;
100 k++;
104 gmx_trr_write_frame(fio, i, time[i], 0, box, na, x, nullptr, nullptr);
106 gmx_trr_close(fio);
107 sfree(x);
110 int gmx_g_angle(int argc, char* argv[])
112 static const char* desc[] = {
113 "[THISMODULE] computes the angle distribution for a number of angles",
114 "or dihedrals.[PAR]",
115 "With option [TT]-ov[tt], you can plot the average angle of",
116 "a group of angles as a function of time. With the [TT]-all[tt] option,",
117 "the first graph is the average and the rest are the individual angles.[PAR]",
118 "With the [TT]-of[tt] option, [THISMODULE] also calculates the fraction of trans",
119 "dihedrals (only for dihedrals) as function of time, but this is",
120 "probably only fun for a select few.[PAR]",
121 "With option [TT]-oc[tt], a dihedral correlation function is calculated.[PAR]",
122 "It should be noted that the index file must contain",
123 "atom triplets for angles or atom quadruplets for dihedrals.",
124 "If this is not the case, the program will crash.[PAR]",
125 "With option [TT]-or[tt], a trajectory file is dumped containing cos and",
126 "sin of selected dihedral angles, which subsequently can be used as",
127 "input for a principal components analysis using [gmx-covar].[PAR]",
128 "Option [TT]-ot[tt] plots when transitions occur between",
129 "dihedral rotamers of multiplicity 3 and [TT]-oh[tt]",
130 "records a histogram of the times between such transitions,",
131 "assuming the input trajectory frames are equally spaced in time."
133 static const char* opt[] = { nullptr, "angle", "dihedral", "improper", "ryckaert-bellemans",
134 nullptr };
135 static gmx_bool bALL = FALSE, bChandler = FALSE, bAverCorr = FALSE, bPBC = TRUE;
136 static real binwidth = 1;
137 t_pargs pa[] = {
138 { "-type", FALSE, etENUM, { opt }, "Type of angle to analyse" },
139 { "-all",
140 FALSE,
141 etBOOL,
142 { &bALL },
143 "Plot all angles separately in the averages file, in the order of appearance in the "
144 "index file." },
145 { "-binwidth",
146 FALSE,
147 etREAL,
148 { &binwidth },
149 "binwidth (degrees) for calculating the distribution" },
150 { "-periodic", FALSE, etBOOL, { &bPBC }, "Print dihedral angles modulo 360 degrees" },
151 { "-chandler",
152 FALSE,
153 etBOOL,
154 { &bChandler },
155 "Use Chandler correlation function (N[trans] = 1, N[gauche] = 0) rather than cosine "
156 "correlation function. Trans is defined as phi < -60 or phi > 60." },
157 { "-avercorr",
158 FALSE,
159 etBOOL,
160 { &bAverCorr },
161 "Average the correlation functions for the individual angles/dihedrals" }
163 static const char* bugs[] = {
164 "Counting transitions only works for dihedrals with multiplicity 3"
167 FILE* out;
168 real dt;
169 int isize;
170 int* index;
171 char* grpname;
172 real maxang, S2, norm_fac, maxstat;
173 unsigned long mode;
174 int nframes, maxangstat, mult, *angstat;
175 int i, j, nangles, first, last;
176 gmx_bool bAver, bRb, bPeriodic, bFrac, /* calculate fraction too? */
177 bTrans, /* worry about transtions too? */
178 bCorr; /* correlation function ? */
179 double tfrac = 0;
180 char title[256];
181 real** dih = nullptr; /* mega array with all dih. angles at all times*/
182 real * time, *trans_frac, *aver_angle;
183 t_filenm fnm[] = { { efTRX, "-f", nullptr, ffREAD }, { efNDX, nullptr, "angle", ffREAD },
184 { efXVG, "-od", "angdist", ffWRITE }, { efXVG, "-ov", "angaver", ffOPTWR },
185 { efXVG, "-of", "dihfrac", ffOPTWR }, { efXVG, "-ot", "dihtrans", ffOPTWR },
186 { efXVG, "-oh", "trhisto", ffOPTWR }, { efXVG, "-oc", "dihcorr", ffOPTWR },
187 { efTRR, "-or", nullptr, ffOPTWR } };
188 #define NFILE asize(fnm)
189 int npargs;
190 t_pargs* ppa;
191 gmx_output_env_t* oenv;
193 npargs = asize(pa);
194 ppa = add_acf_pargs(&npargs, pa);
195 if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME, NFILE, fnm, npargs, ppa,
196 asize(desc), desc, asize(bugs), bugs, &oenv))
198 sfree(ppa);
199 return 0;
202 mult = 4;
203 maxang = 360.0;
204 bRb = FALSE;
206 GMX_RELEASE_ASSERT(opt[0] != nullptr,
207 "Internal option inconsistency; opt[0]==NULL after processing");
209 switch (opt[0][0])
211 case 'a':
212 mult = 3;
213 maxang = 180.0;
214 break;
215 case 'd': // Intended fall through
216 case 'i': break;
217 case 'r': bRb = TRUE; break;
220 if (opt2bSet("-or", NFILE, fnm))
222 if (mult != 4)
224 gmx_fatal(FARGS, "Can not combine angles with trr dump");
226 else
228 please_cite(stdout, "Mu2005a");
232 /* Calculate bin size */
233 maxangstat = gmx::roundToInt(maxang / binwidth);
234 binwidth = maxang / maxangstat;
236 rd_index(ftp2fn(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
237 nangles = isize / mult;
238 if ((isize % mult) != 0)
240 gmx_fatal(FARGS,
241 "number of index elements not multiple of %d, "
242 "these can not be %s\n",
243 mult, (mult == 3) ? "angle triplets" : "dihedral quadruplets");
247 /* Check whether specific analysis has to be performed */
248 bCorr = opt2bSet("-oc", NFILE, fnm);
249 bAver = opt2bSet("-ov", NFILE, fnm);
250 bTrans = opt2bSet("-ot", NFILE, fnm);
251 bFrac = opt2bSet("-of", NFILE, fnm);
252 if (bTrans && opt[0][0] != 'd')
254 fprintf(stderr, "Option -ot should only accompany -type dihedral. Disabling -ot.\n");
255 bTrans = FALSE;
258 if (bChandler && !bCorr)
260 bCorr = TRUE;
263 if (bFrac && !bRb)
265 fprintf(stderr,
266 "Warning:"
267 " calculating fractions as defined in this program\n"
268 "makes sense for Ryckaert Bellemans dihs. only. Ignoring -of\n\n");
269 bFrac = FALSE;
272 if ((bTrans || bFrac || bCorr) && mult == 3)
274 gmx_fatal(FARGS,
275 "Can only do transition, fraction or correlation\n"
276 "on dihedrals. Select -d\n");
280 * We need to know the nr of frames so we can allocate memory for an array
281 * with all dihedral angles at all timesteps. Works for me.
283 if (bTrans || bCorr || bALL || opt2bSet("-or", NFILE, fnm))
285 snew(dih, nangles);
288 snew(angstat, maxangstat);
290 read_ang_dih(ftp2fn(efTRX, NFILE, fnm), (mult == 3),
291 bALL || bCorr || bTrans || opt2bSet("-or", NFILE, fnm), bRb, bPBC, maxangstat,
292 angstat, &nframes, &time, isize, index, &trans_frac, &aver_angle, dih, oenv);
294 dt = (time[nframes - 1] - time[0]) / (nframes - 1);
296 if (bAver)
298 sprintf(title, "Average Angle: %s", grpname);
299 out = xvgropen(opt2fn("-ov", NFILE, fnm), title, "Time (ps)", "Angle (degrees)", oenv);
300 for (i = 0; (i < nframes); i++)
302 fprintf(out, "%10.5f %8.3f", time[i], aver_angle[i] * RAD2DEG);
303 if (bALL)
305 for (j = 0; (j < nangles); j++)
307 if (bPBC)
309 real dd = dih[j][i];
310 fprintf(out, " %8.3f", std::atan2(std::sin(dd), std::cos(dd)) * RAD2DEG);
312 else
314 fprintf(out, " %8.3f", dih[j][i] * RAD2DEG);
318 fprintf(out, "\n");
320 xvgrclose(out);
322 if (opt2bSet("-or", NFILE, fnm))
324 dump_dih_trr(nframes, nangles, dih, opt2fn("-or", NFILE, fnm), time);
327 if (bFrac)
329 sprintf(title, "Trans fraction: %s", grpname);
330 out = xvgropen(opt2fn("-of", NFILE, fnm), title, "Time (ps)", "Fraction", oenv);
331 tfrac = 0.0;
332 for (i = 0; (i < nframes); i++)
334 fprintf(out, "%10.5f %10.3f\n", time[i], trans_frac[i]);
335 tfrac += trans_frac[i];
337 xvgrclose(out);
339 tfrac /= nframes;
340 fprintf(stderr, "Average trans fraction: %g\n", tfrac);
342 sfree(trans_frac);
344 if (bTrans)
346 ana_dih_trans(opt2fn("-ot", NFILE, fnm), opt2fn("-oh", NFILE, fnm), dih, nframes, nangles,
347 grpname, time, bRb, oenv);
350 if (bCorr)
352 /* Autocorrelation function */
353 if (nframes < 2)
355 fprintf(stderr, "Not enough frames for correlation function\n");
357 else
360 if (bChandler)
362 real dval, sixty = DEG2RAD * 60;
363 gmx_bool bTest;
365 for (i = 0; (i < nangles); i++)
367 for (j = 0; (j < nframes); j++)
369 dval = dih[i][j];
370 if (bRb)
372 bTest = (dval > -sixty) && (dval < sixty);
374 else
376 bTest = (dval < -sixty) || (dval > sixty);
378 if (bTest)
380 dih[i][j] = dval - tfrac;
382 else
384 dih[i][j] = -tfrac;
389 if (bChandler)
391 mode = eacNormal;
393 else
395 mode = eacCos;
397 do_autocorr(opt2fn("-oc", NFILE, fnm), oenv, "Dihedral Autocorrelation Function",
398 nframes, nangles, dih, dt, mode, bAverCorr);
403 /* Determine the non-zero part of the distribution */
404 for (first = 0; (first < maxangstat - 1) && (angstat[first + 1] == 0); first++) {}
405 for (last = maxangstat - 1; (last > 0) && (angstat[last - 1] == 0); last--) {}
407 double aver = 0;
408 printf("Found points in the range from %d to %d (max %d)\n", first, last, maxangstat);
409 if (bTrans || bCorr || bALL || opt2bSet("-or", NFILE, fnm))
410 { /* It's better to re-calculate Std. Dev per sample */
411 real b_aver = aver_angle[0];
412 real b = dih[0][0];
413 real delta;
414 for (int i = 0; (i < nframes); i++)
416 delta = correctRadianAngleRange(aver_angle[i] - b_aver);
417 b_aver += delta;
418 aver += b_aver;
419 for (int j = 0; (j < nangles); j++)
421 delta = correctRadianAngleRange(dih[j][i] - b);
422 b += delta;
426 else
427 { /* Incorrect for Std. Dev. */
428 real delta, b_aver = aver_angle[0];
429 for (i = 0; (i < nframes); i++)
431 delta = correctRadianAngleRange(aver_angle[i] - b_aver);
432 b_aver += delta;
433 aver += b_aver;
436 aver /= nframes;
437 double aversig = correctRadianAngleRange(aver);
438 aversig *= RAD2DEG;
439 aver *= RAD2DEG;
440 printf(" < angle > = %g\n", aversig);
442 if (mult == 3)
444 sprintf(title, "Angle Distribution: %s", grpname);
446 else
448 sprintf(title, "Dihedral Distribution: %s", grpname);
450 calc_distribution_props(maxangstat, angstat, -180.0, 0, nullptr, &S2);
451 fprintf(stderr, "Order parameter S^2 = %g\n", S2);
454 bPeriodic = (mult == 4) && (first == 0) && (last == maxangstat - 1);
456 out = xvgropen(opt2fn("-od", NFILE, fnm), title, "Degrees", "", oenv);
457 if (output_env_get_print_xvgr_codes(oenv))
459 fprintf(out, "@ subtitle \"average angle: %g\\So\\N\"\n", aver);
461 norm_fac = 1.0 / (nangles * nframes * binwidth);
462 if (bPeriodic)
464 maxstat = 0;
465 for (i = first; (i <= last); i++)
467 maxstat = std::max(maxstat, angstat[i] * norm_fac);
469 if (output_env_get_print_xvgr_codes(oenv))
471 fprintf(out, "@with g0\n");
472 fprintf(out, "@ world xmin -180\n");
473 fprintf(out, "@ world xmax 180\n");
474 fprintf(out, "@ world ymin 0\n");
475 fprintf(out, "@ world ymax %g\n", maxstat * 1.05);
476 fprintf(out, "@ xaxis tick major 60\n");
477 fprintf(out, "@ xaxis tick minor 30\n");
478 fprintf(out, "@ yaxis tick major 0.005\n");
479 fprintf(out, "@ yaxis tick minor 0.0025\n");
482 for (i = first; (i <= last); i++)
484 fprintf(out, "%10g %10f\n", i * binwidth + 180.0 - maxang, angstat[i] * norm_fac);
486 if (bPeriodic)
488 /* print first bin again as last one */
489 fprintf(out, "%10g %10f\n", 180.0, angstat[0] * norm_fac);
492 xvgrclose(out);
494 do_view(oenv, opt2fn("-od", NFILE, fnm), "-nxy");
495 if (bAver)
497 do_view(oenv, opt2fn("-ov", NFILE, fnm), "-nxy");
500 return 0;