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,2021, 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.
47 #include "gromacs/fileio/confio.h"
48 #include "gromacs/fileio/trxio.h"
49 #include "gromacs/fileio/xvgr.h"
50 #include "gromacs/gmxana/angle_correction.h"
51 #include "gromacs/gmxana/gstat.h"
52 #include "gromacs/listed_forces/bonded.h"
53 #include "gromacs/math/functions.h"
54 #include "gromacs/math/units.h"
55 #include "gromacs/math/utilities.h"
56 #include "gromacs/math/vec.h"
57 #include "gromacs/math/vecdump.h"
58 #include "gromacs/pbcutil/pbc.h"
59 #include "gromacs/utility/fatalerror.h"
60 #include "gromacs/utility/smalloc.h"
62 void print_one(const gmx_output_env_t* oenv,
72 char buf[256], t2[256];
75 sprintf(buf, "%s%s.xvg", base, name);
76 fprintf(stderr, "\rPrinting %s ", buf);
78 sprintf(t2, "%s %s", title, name);
79 fp = xvgropen(buf, t2, "Time (ps)", ylabel, oenv);
80 for (k = 0; (k < nf); k++)
82 fprintf(fp, "%10g %10g\n", time[k], data[k]);
87 static int calc_RBbin(real phi, int gmx_unused multiplicity, real gmx_unused core_frac)
89 /* multiplicity and core_frac NOT used,
90 * just given to enable use of pt-to-fn in caller low_ana_dih_trans*/
91 static const real r30 = M_PI / 6.0;
92 static const real r90 = M_PI / 2.0;
93 static const real r150 = M_PI * 5.0 / 6.0;
95 if ((phi < r30) && (phi > -r30))
99 else if ((phi > -r150) && (phi < -r90))
103 else if ((phi < r150) && (phi > r90))
110 static int calc_Nbin(real phi, int multiplicity, real core_frac)
112 static const real r360 = 360 * gmx::c_deg2Rad;
113 real rot_width, core_width, core_offset, low, hi;
115 /* with multiplicity 3 and core_frac 0.5
116 * 0<g(-)<120, 120<t<240, 240<g(+)<360
117 * 0< bin0 < 30, 30<bin1<90, 90<bin0<150, 150<bin2<210, 210<bin0<270, 270<bin3<330, 330<bin0<360
118 * so with multiplicity 3, bin1 is core g(-), bin2 is core t, bin3 is
119 core g(+), bin0 is between rotamers */
125 rot_width = 360. / multiplicity;
126 core_width = core_frac * rot_width;
127 core_offset = (rot_width - core_width) / 2.0;
128 for (bin = 1; bin <= multiplicity; bin++)
130 low = ((bin - 1) * rot_width) + core_offset;
131 hi = ((bin - 1) * rot_width) + core_offset + core_width;
132 low *= gmx::c_deg2Rad;
133 hi *= gmx::c_deg2Rad;
134 if ((phi > low) && (phi < hi))
142 void ana_dih_trans(const char* fn_trans,
143 const char* fn_histo,
150 const gmx_output_env_t* oenv)
152 /* just a wrapper; declare extra args, then chuck away at end. */
157 std::vector<t_dlist> dlist(nangles);
158 snew(multiplicity, nangles);
159 for (k = 0; (k < nangles); k++)
165 TRUE, fn_trans, TRUE, fn_histo, maxchi, dih, dlist, nframes, nangles, grpname, multiplicity, time, bRb, 0.5, oenv);
169 void low_ana_dih_trans(gmx_bool bTrans,
170 const char* fn_trans,
172 const char* fn_histo,
175 gmx::ArrayRef<t_dlist> dlist,
183 const gmx_output_env_t* oenv)
189 int cur_bin, new_bin;
192 int (*calc_bin)(real, int, real);
199 /* Assumes the frames are equally spaced in time */
200 dt = (time[nframes - 1] - time[0]) / (nframes - 1);
202 /* Analysis of dihedral transitions */
203 fprintf(stderr, "Now calculating transitions...\n");
207 calc_bin = calc_RBbin;
211 calc_bin = calc_Nbin;
214 for (int k = 0; k < NROT; k++)
216 snew(rot_occ[k], nangles);
217 for (int i = 0; (i < nangles); i++)
225 /* dih[i][j] is the dihedral angle i in frame j */
227 for (int i = 0; (i < nangles); i++)
232 mind = maxd = prev = dih[i][0];
234 cur_bin = calc_bin(dih[i][0], multiplicity[i], core_frac);
235 rot_occ[cur_bin][i]++;
237 for (int j = 1; (j < nframes); j++)
239 new_bin = calc_bin(dih[i][j], multiplicity[i], core_frac);
240 rot_occ[new_bin][i]++;
246 else if ((new_bin != 0) && (cur_bin != new_bin))
254 /* why is all this md rubbish periodic? Remove 360 degree periodicity */
255 if ((dih[i][j] - prev) > M_PI)
257 dih[i][j] -= 2 * M_PI;
259 else if ((dih[i][j] - prev) < -M_PI)
261 dih[i][j] += 2 * M_PI;
266 mind = std::min(mind, dih[i][j]);
267 maxd = std::max(maxd, dih[i][j]);
268 if ((maxd - mind) > 2 * M_PI / 3) /* or 120 degrees, assuming */
269 { /* multiplicity 3. Not so general.*/
272 maxd = mind = dih[i][j]; /* get ready for next transition */
277 for (int k = 0; k < NROT; k++)
279 rot_occ[k][i] /= nframes;
282 fprintf(stderr, "Total number of transitions: %10d\n", ntrans);
285 ttime = (dt * nframes * nangles) / ntrans;
286 fprintf(stderr, "Time between transitions: %10.3f ps\n", ttime);
289 /* Copy transitions from tr_h[] to dlist->ntr[]
290 * and rotamer populations from rot_occ to dlist->rot_occ[]
291 * based on fn histogramming in g_chi. diff roles for i and j here */
294 for (Dih = 0; (Dih < NONCHI + maxchi); Dih++)
296 for (auto& dihedral : dlist)
298 if (((Dih < edOmega)) || ((Dih == edOmega) && (has_dihedral(edOmega, dihedral)))
299 || ((Dih > edOmega) && (dihedral.atm.Cn[Dih - NONCHI + 3] != -1)))
301 dihedral.ntr[Dih] = tr_h[j];
302 for (int k = 0; k < NROT; k++)
304 dihedral.rot_occ[Dih][k] = rot_occ[k][j];
314 sprintf(title, "Number of transitions: %s", grpname);
315 fp = xvgropen(fn_trans, title, "Time (ps)", "# transitions/timeframe", oenv);
316 for (int j = 0; (j < nframes); j++)
318 fprintf(fp, "%10.3f %10d\n", time[j], tr_f[j]);
323 /* Compute histogram from # transitions per dihedral */
325 for (int j = 0; (j < nframes); j++)
329 for (int i = 0; (i < nangles); i++)
334 for (j = nframes; ((tr_f[j - 1] == 0) && (j > 0)); j--) {}
336 ttime = dt * nframes;
339 sprintf(title, "Transition time: %s", grpname);
340 fp = xvgropen(fn_histo, title, "Time (ps)", "#", oenv);
341 for (int i = j - 1; (i > 0); i--)
345 fprintf(fp, "%10.3f %10d\n", ttime / i, tr_f[i]);
353 for (int k = 0; k < NROT; k++)
359 void mk_multiplicity_lookup(int* multiplicity, int maxchi, gmx::ArrayRef<const t_dlist> dlist, int nangles)
361 /* new by grs - for dihedral j (as in dih[j]) get multiplicity from dlist
362 * and store in multiplicity[j]
368 for (int Dih = 0; (Dih < NONCHI + maxchi); Dih++)
370 for (const auto& dihedral : dlist)
372 std::strncpy(name, dihedral.name, 3);
374 if (((Dih < edOmega)) || ((Dih == edOmega) && (has_dihedral(edOmega, dihedral)))
375 || ((Dih > edOmega) && (dihedral.atm.Cn[Dih - NONCHI + 3] != -1)))
377 /* default - we will correct the rest below */
380 /* make omegas 2fold, though doesn't make much more sense than 3 */
381 if (Dih == edOmega && (has_dihedral(edOmega, dihedral)))
386 /* dihedrals to aromatic rings, COO, CONH2 or guanidinium are 2fold*/
387 if (Dih > edOmega && (dihedral.atm.Cn[Dih - NONCHI + 3] != -1))
389 if (((std::strstr(name, "PHE") != nullptr) && (Dih == edChi2))
390 || ((std::strstr(name, "TYR") != nullptr) && (Dih == edChi2))
391 || ((std::strstr(name, "PTR") != nullptr) && (Dih == edChi2))
392 || ((std::strstr(name, "TRP") != nullptr) && (Dih == edChi2))
393 || ((std::strstr(name, "HIS") != nullptr) && (Dih == edChi2))
394 || ((std::strstr(name, "GLU") != nullptr) && (Dih == edChi3))
395 || ((std::strstr(name, "ASP") != nullptr) && (Dih == edChi2))
396 || ((std::strstr(name, "GLN") != nullptr) && (Dih == edChi3))
397 || ((std::strstr(name, "ASN") != nullptr) && (Dih == edChi2))
398 || ((std::strstr(name, "ARG") != nullptr) && (Dih == edChi4)))
409 fprintf(stderr, "WARNING: not all dihedrals found in topology (only %d out of %d)!\n", j, nangles);
411 /* Check for remaining dihedrals */
412 for (; (j < nangles); j++)
418 void mk_chi_lookup(int** lookup, int maxchi, gmx::ArrayRef<const t_dlist> dlist)
421 /* by grs. should rewrite everything to use this. (but haven't,
422 * and at mmt only used in get_chi_product_traj
423 * returns the dihed number given the residue number (from-0)
424 * and chi (from-0) nr. -1 for chi undefined for that res (eg gly, ala..)*/
427 /* NONCHI points to chi1, therefore we have to start counting there. */
428 for (int Dih = NONCHI; (Dih < NONCHI + maxchi); Dih++)
430 for (size_t i = 0; i < dlist.size(); i++)
432 int Chi = Dih - NONCHI;
433 if (((Dih < edOmega)) || ((Dih == edOmega) && (has_dihedral(edOmega, dlist[i])))
434 || ((Dih > edOmega) && (dlist[i].atm.Cn[Dih - NONCHI + 3] != -1)))
436 /* grs debug printf("Not OK? i %d j %d Dih %d \n", i, j, Dih) ; */
452 void get_chi_product_traj(real** dih,
455 gmx::ArrayRef<const t_dlist> dlist,
464 const gmx_output_env_t* oenv)
467 gmx_bool bRotZero, bHaveChi = FALSE;
468 int accum = 0, index, j, k, n, b;
472 char hisfile[256], histitle[256];
474 int (*calc_bin)(real, int, real);
476 /* Analysis of dihedral transitions */
477 fprintf(stderr, "Now calculating Chi product trajectories...\n");
481 calc_bin = calc_RBbin;
485 calc_bin = calc_Nbin;
488 snew(chi_prtrj, nframes);
490 /* file for info on all residues */
493 fpall = xvgropen(fnall, "Cumulative Rotamers", "Residue", "Probability", oenv);
497 fpall = xvgropen(fnall, "Cumulative Rotamers", "Residue", "# Counts", oenv);
501 for (const auto& dihedral : dlist)
504 /* get nbin, the nr. of cumulative rotamers that need to be considered */
506 for (int Xi = 0; Xi < maxchi; Xi++)
508 index = lookup[i][Xi]; /* chi_(Xi+1) of res i (-1 if off end) */
511 n = multiplicity[index];
515 nbin += 1; /* for the "zero rotamer", outside the core region */
517 for (j = 0; (j < nframes); j++)
522 index = lookup[i][0]; /* index into dih of chi1 of res i */
530 b = calc_bin(dih[index][j], multiplicity[index], core_frac);
536 for (int Xi = 1; Xi < maxchi; Xi++)
538 index = lookup[i][Xi]; /* chi_(Xi+1) of res i (-1 if off end) */
541 n = multiplicity[index];
542 b = calc_bin(dih[index][j], n, core_frac);
543 accum = n * accum + b - 1;
558 chi_prtrj[j] = accum;
559 if (accum + 1 > nbin)
570 /* print cumulative rotamer vs time */
571 print_one(oenv, "chiproduct", dlist[i].name, "chi product for", "cumulative rotamer", nframes, time, chi_prtrj);
574 /* make a histogram of cumulative rotamer occupancy too */
575 snew(chi_prhist, nbin);
576 make_histo(nullptr, nframes, chi_prtrj, nbin, chi_prhist, 0, nbin);
579 sprintf(hisfile, "histo-chiprod%s.xvg", dihedral.name);
580 sprintf(histitle, "cumulative rotamer distribution for %s", dihedral.name);
581 fprintf(stderr, " and %s ", hisfile);
582 fp = xvgropen(hisfile, histitle, "number", "", oenv);
583 if (output_env_get_print_xvgr_codes(oenv))
585 fprintf(fp, "@ xaxis tick on\n");
586 fprintf(fp, "@ xaxis tick major 1\n");
587 fprintf(fp, "@ type xy\n");
589 for (k = 0; (k < nbin); k++)
593 fprintf(fp, "%5d %10g\n", k, (1.0 * chi_prhist[k]) / nframes);
597 fprintf(fp, "%5d %10d\n", k, chi_prhist[k]);
600 fprintf(fp, "%s\n", output_env_get_print_xvgr_codes(oenv) ? "&" : "");
604 /* and finally print out occupancies to a single file */
605 /* get the gmx from-1 res nr by setting a ptr to the number part
606 * of dihedral.name - potential bug for 4-letter res names... */
607 const char* namept = dihedral.name + 3;
608 fprintf(fpall, "%5s ", namept);
609 for (k = 0; (k < nbin); k++)
613 fprintf(fpall, " %10g", (1.0 * chi_prhist[k]) / nframes);
617 fprintf(fpall, " %10d", chi_prhist[k]);
620 fprintf(fpall, "\n");
630 fprintf(stderr, "\n");
633 void calc_distribution_props(int nh, const int histo[], real start, int nkkk, t_karplus kkk[], real* S2)
635 real d, dc, ds, c1, c2, tdc, tds;
636 real fac, ang, invth, Jc;
641 gmx_fatal(FARGS, "No points in histogram (%s, %d)", __FILE__, __LINE__);
645 /* Compute normalisation factor */
647 for (j = 0; (j < nh); j++)
653 for (i = 0; (i < nkkk); i++)
660 for (j = 0; (j < nh); j++)
662 d = invth * histo[j];
663 ang = j * fac - start;
666 ds = d * std::sin(ang);
669 for (i = 0; (i < nkkk); i++)
671 c1 = std::cos(ang + kkk[i].offset);
673 Jc = (kkk[i].A * c2 + kkk[i].B * c1 + kkk[i].C);
674 kkk[i].Jc += histo[j] * Jc;
675 kkk[i].Jcsig += histo[j] * gmx::square(Jc);
678 for (i = 0; (i < nkkk); i++)
681 kkk[i].Jcsig = std::sqrt(kkk[i].Jcsig / th - gmx::square(kkk[i].Jc));
683 *S2 = tdc * tdc + tds * tds;
686 static void calc_angles(struct t_pbc* pbc, int n3, int index[], real ang[], rvec x_s[])
692 for (i = ix = 0; (ix < n3); i++, ix += 3)
695 x_s[index[ix]], x_s[index[ix + 1]], x_s[index[ix + 2]], pbc, r_ij, r_kj, &costh, &t1, &t2);
699 fprintf(debug, "Angle[0]=%g, costh=%g, index0 = %d, %d, %d\n", ang[0], costh, index[0], index[1], index[2]);
700 pr_rvec(debug, 0, "rij", r_ij, DIM, TRUE);
701 pr_rvec(debug, 0, "rkj", r_kj, DIM, TRUE);
705 static real calc_fraction(const real angles[], int nangles)
708 real trans = 0, gauche = 0;
711 for (i = 0; i < nangles; i++)
713 angle = angles[i] * gmx::c_rad2Deg;
715 if (angle > 135 && angle < 225)
719 else if ((angle > 270 && angle < 330) || (angle < 90 && angle > 30))
724 if (trans + gauche > 0)
726 return trans / (trans + gauche);
734 static void calc_dihs(struct t_pbc* pbc, int n4, const int index[], real ang[], rvec x_s[])
736 int i, ix, t1, t2, t3;
737 rvec r_ij, r_kj, r_kl, m, n;
740 for (i = ix = 0; (ix < n4); i++, ix += 4)
742 aaa = dih_angle(x_s[index[ix]],
756 ang[i] = aaa; /* not taking into account ryckaert bellemans yet */
760 void make_histo(FILE* log, int ndata, real data[], int npoints, int histo[], real minx, real maxx)
767 minx = maxx = data[0];
768 for (i = 1; (i < ndata); i++)
770 minx = std::min(minx, data[i]);
771 maxx = std::max(maxx, data[i]);
773 fprintf(log, "Min data: %10g Max data: %10g\n", minx, maxx);
775 dx = npoints / (maxx - minx);
778 fprintf(debug, "Histogramming: ndata=%d, nhisto=%d, minx=%g,maxx=%g,dx=%g\n", ndata, npoints, minx, maxx, dx);
780 for (i = 0; (i < ndata); i++)
782 ind = static_cast<int>((data[i] - minx) * dx);
783 if ((ind >= 0) && (ind < npoints))
789 fprintf(log, "index = %d, data[%d] = %g\n", ind, i, data[i]);
794 void normalize_histo(gmx::ArrayRef<const int> histo, real dx, gmx::ArrayRef<real> normhisto)
797 for (const auto& point : histo)
803 fprintf(stderr, "Empty histogram!\n");
806 double fac = 1.0 / d;
808 histo.begin(), histo.end(), normhisto.begin(), [fac](int point) { return fac * point; });
811 void read_ang_dih(const char* trj_fn,
825 const gmx_output_env_t* oenv)
829 int i, angind, total, teller;
830 int nangles, n_alloc;
831 real t, fraction, pifac, angle;
836 #define prev (1 - cur)
839 gmx::sfree_guard pbcGuard(pbc);
840 read_first_x(oenv, &status, trj_fn, &t, &x, box);
852 snew(angles[cur], nangles);
853 snew(angles[prev], nangles);
855 /* Start the loop over frames */
860 *trans_frac = nullptr;
861 *aver_angle = nullptr;
865 if (teller >= n_alloc)
870 for (i = 0; (i < nangles); i++)
872 srenew(dih[i], n_alloc);
875 srenew(*time, n_alloc);
876 srenew(*trans_frac, n_alloc);
877 srenew(*aver_angle, n_alloc);
884 set_pbc(pbc, PbcType::Unset, box);
889 calc_angles(pbc, isize, index, angles[cur], x);
893 calc_dihs(pbc, isize, index, angles[cur], x);
896 fraction = calc_fraction(angles[cur], nangles);
897 (*trans_frac)[teller] = fraction;
899 /* Change Ryckaert-Bellemans dihedrals to polymer convention
900 * Modified 990913 by Erik:
901 * We actually shouldn't change the convention, since it's
902 * calculated from polymer above, but we change the intervall
903 * from [-180,180] to [0,360].
907 for (i = 0; (i < nangles); i++)
909 if (angles[cur][i] <= 0.0)
911 angles[cur][i] += 2 * M_PI;
916 /* Periodicity in dihedral space... */
919 for (i = 0; (i < nangles); i++)
921 real dd = angles[cur][i];
922 angles[cur][i] = std::atan2(std::sin(dd), std::cos(dd));
929 for (i = 0; (i < nangles); i++)
931 while (angles[cur][i] <= angles[prev][i] - M_PI)
933 angles[cur][i] += 2 * M_PI;
935 while (angles[cur][i] > angles[prev][i] + M_PI)
937 angles[cur][i] -= 2 * M_PI;
946 for (i = 0; (i < nangles); i++)
948 if (!bAngles && i > 0)
950 real diffa = angles[cur][i] - angles[cur][i - 1];
951 diffa = correctRadianAngleRange(diffa);
952 angles[cur][i] = angles[cur][i - 1] + diffa;
955 aa = aa + angles[cur][i];
957 /* angle in rad / 2Pi * max determines bin. bins go from 0 to maxangstat,
958 even though scale goes from -pi to pi (dihedral) or -pi/2 to pi/2
959 (angle) Basically: translate the x-axis by Pi. Translate it back by
963 angle = angles[cur][i];
966 angle = correctRadianAngleRange(angle);
970 /* Update the distribution histogram */
971 angind = gmx::roundToInt((angle * maxangstat) / pifac);
972 if (angind == maxangstat)
976 if ((angind < 0) || (angind >= maxangstat))
978 /* this will never happen */
979 gmx_fatal(FARGS, "angle (%f) index out of range (0..%d) : %d\n", angle, maxangstat, angind);
983 if (angind == maxangstat)
985 fprintf(stderr, "angle %d fr %d = %g\n", i, cur, angle);
991 /* average over all angles */
992 aa = correctRadianAngleRange(aa / nangles);
993 (*aver_angle)[teller] = (aa);
995 /* this copies all current dih. angles to dih[i], teller is frame */
998 for (i = 0; i < nangles; i++)
1002 dih[i][teller] = correctRadianAngleRange(angles[cur][i]);
1006 dih[i][teller] = angles[cur][i];
1014 /* Increment loop counter */
1016 } while (read_next_x(oenv, status, &t, x, box));
1017 done_trx_xframe(status);
1021 sfree(angles[prev]);