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-2008, 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.
49 #include "gromacs/commandline/pargs.h"
50 #include "gromacs/commandline/viewit.h"
51 #include "gromacs/correlationfunctions/autocorr.h"
52 #include "gromacs/correlationfunctions/crosscorr.h"
53 #include "gromacs/correlationfunctions/expfit.h"
54 #include "gromacs/correlationfunctions/integrate.h"
55 #include "gromacs/fileio/matio.h"
56 #include "gromacs/fileio/tpxio.h"
57 #include "gromacs/fileio/trxio.h"
58 #include "gromacs/fileio/xvgr.h"
59 #include "gromacs/gmxana/gmx_ana.h"
60 #include "gromacs/gmxana/gstat.h"
61 #include "gromacs/math/functions.h"
62 #include "gromacs/math/units.h"
63 #include "gromacs/math/utilities.h"
64 #include "gromacs/math/vec.h"
65 #include "gromacs/mdtypes/inputrec.h"
66 #include "gromacs/pbcutil/pbc.h"
67 #include "gromacs/topology/ifunc.h"
68 #include "gromacs/topology/index.h"
69 #include "gromacs/topology/topology.h"
70 #include "gromacs/utility/arraysize.h"
71 #include "gromacs/utility/cstringutil.h"
72 #include "gromacs/utility/exceptions.h"
73 #include "gromacs/utility/fatalerror.h"
74 #include "gromacs/utility/futil.h"
75 #include "gromacs/utility/gmxomp.h"
76 #include "gromacs/utility/pleasecite.h"
77 #include "gromacs/utility/programcontext.h"
78 #include "gromacs/utility/smalloc.h"
79 #include "gromacs/utility/snprintf.h"
82 typedef int t_hx[max_hx];
83 #define NRHXTYPES max_hx
84 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
85 static const char* hxtypenames[NRHXTYPES] = { "n-n", "n-n+1", "n-n+2", "n-n+3",
86 "n-n+4", "n-n+5", "n-n>6" };
89 static const int NOTSET = -49297;
91 /* -----------------------------------------*/
108 static const unsigned char c_acceptorMask = (1 << 0);
109 static const unsigned char c_donorMask = (1 << 1);
110 static const unsigned char c_inGroupMask = (1 << 2);
112 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
113 static const char* grpnames[grNR] = { "0", "1", "I" };
114 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
115 static gmx_bool bDebug = FALSE;
117 #define HB_NR (1 << 2)
120 #define ISHB(h) ((h)&2)
121 #define ISDIST(h) ((h)&1)
122 #define ISDON(h) ((h)&c_donorMask)
123 #define ISINGRP(h) ((h)&c_inGroupMask)
138 typedef int t_icell[grNR];
139 typedef int h_id[MAXHYDRO];
143 int history[MAXHYDRO];
144 /* Has this hbond existed ever? If so as hbDist or hbHB or both.
145 * Result is stored as a bitmap (1 = hbDist) || (2 = hbHB)
147 /* Bitmask array which tells whether a hbond is present
148 * at a given time. Either of these may be NULL
150 int n0; /* First frame a HB was found */
151 int nframes, maxframes; /* Amount of frames in this hbond */
154 /* See Xu and Berne, JPCB 105 (2001), p. 11929. We define the
155 * function g(t) = [1-h(t)] H(t) where H(t) is one when the donor-
156 * acceptor distance is less than the user-specified distance (typically
164 int* acc; /* Atom numbers of the acceptors */
165 int* grp; /* Group index */
166 int* aptr; /* Map atom number to acceptor index */
172 int* don; /* Atom numbers of the donors */
173 int* grp; /* Group index */
174 int* dptr; /* Map atom number to donor index */
175 int* nhydro; /* Number of hydrogens for each donor */
176 h_id* hydro; /* The atom numbers of the hydrogens */
177 h_id* nhbonds; /* The number of HBs per H at current */
182 gmx_bool bHBmap, bDAnr;
184 /* The following arrays are nframes long */
185 int nframes, max_frames, maxhydro;
191 /* These structures are initialized from the topology at start up */
194 /* This holds a matrix with all possible hydrogen bonds */
199 /* Changed argument 'bMerge' into 'oneHB' below,
200 * since -contact should cause maxhydro to be 1,
202 * - Erik Marklund May 29, 2006
205 static t_hbdata* mk_hbdata(gmx_bool bHBmap, gmx_bool bDAnr, gmx_bool oneHB)
210 hb->wordlen = 8 * sizeof(unsigned int);
219 hb->maxhydro = MAXHYDRO;
224 static void mk_hbmap(t_hbdata* hb)
228 snew(hb->hbmap, hb->d.nrd);
229 for (i = 0; (i < hb->d.nrd); i++)
231 snew(hb->hbmap[i], hb->a.nra);
232 if (hb->hbmap[i] == nullptr)
234 gmx_fatal(FARGS, "Could not allocate enough memory for hbmap");
236 for (j = 0; j < hb->a.nra; j++)
238 hb->hbmap[i][j] = nullptr;
243 static void add_frames(t_hbdata* hb, int nframes)
245 if (nframes >= hb->max_frames)
247 hb->max_frames += 4096;
248 srenew(hb->time, hb->max_frames);
249 srenew(hb->nhb, hb->max_frames);
250 srenew(hb->ndist, hb->max_frames);
251 srenew(hb->n_bound, hb->max_frames);
252 srenew(hb->nhx, hb->max_frames);
255 srenew(hb->danr, hb->max_frames);
258 hb->nframes = nframes;
261 #define OFFSET(frame) ((frame) / 32)
262 #define MASK(frame) (1 << ((frame) % 32))
264 static void set_hb_function(unsigned int hbexist[], unsigned int frame, gmx_bool bValue)
268 hbexist[OFFSET(frame)] |= MASK(frame);
272 hbexist[OFFSET(frame)] &= ~MASK(frame);
276 static gmx_bool is_hb(const unsigned int hbexist[], int frame)
278 return (hbexist[OFFSET(frame)] & MASK(frame)) != 0;
281 static void set_hb(t_hbdata* hb, int id, int ih, int ia, int frame, int ihb)
283 unsigned int* ghptr = nullptr;
287 ghptr = hb->hbmap[id][ia]->h[ih];
289 else if (ihb == hbDist)
291 ghptr = hb->hbmap[id][ia]->g[ih];
295 gmx_fatal(FARGS, "Incomprehensible iValue %d in set_hb", ihb);
298 set_hb_function(ghptr, frame - hb->hbmap[id][ia]->n0, TRUE);
301 static void add_ff(t_hbdata* hbd, int id, int h, int ia, int frame, int ihb)
304 t_hbond* hb = hbd->hbmap[id][ia];
305 int maxhydro = std::min(hbd->maxhydro, hbd->d.nhydro[id]);
306 int wlen = hbd->wordlen;
307 int delta = 32 * wlen;
312 hb->maxframes = delta;
313 for (i = 0; (i < maxhydro); i++)
315 snew(hb->h[i], hb->maxframes / wlen);
316 snew(hb->g[i], hb->maxframes / wlen);
321 hb->nframes = frame - hb->n0;
322 /* We need a while loop here because hbonds may be returning
325 while (hb->nframes >= hb->maxframes)
327 n = hb->maxframes + delta;
328 for (i = 0; (i < maxhydro); i++)
330 srenew(hb->h[i], n / wlen);
331 srenew(hb->g[i], n / wlen);
332 for (j = hb->maxframes / wlen; (j < n / wlen); j++)
344 set_hb(hbd, id, h, ia, frame, ihb);
348 static void inc_nhbonds(t_donors* ddd, int d, int h)
351 int dptr = ddd->dptr[d];
353 for (j = 0; (j < ddd->nhydro[dptr]); j++)
355 if (ddd->hydro[dptr][j] == h)
357 ddd->nhbonds[dptr][j]++;
361 if (j == ddd->nhydro[dptr])
363 gmx_fatal(FARGS, "No such hydrogen %d on donor %d\n", h + 1, d + 1);
367 static int acceptor_index_function(t_acceptors* a, int grp, int i, const char* file, int line)
371 if (a->grp[ai] != grp)
375 fprintf(debug, "Acc. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n", ai, a->grp[ai], grp, file, line);
384 #define acceptor_index(a, grp, i) acceptor_index_function(a, grp, i, __FILE__, __LINE__)
386 static int donor_index_function(t_donors* d, int grp, int i, const char* file, int line)
395 if (d->grp[di] != grp)
399 fprintf(debug, "Don. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n", di, d->grp[di], grp, file, line);
408 #define donor_index(d, grp, i) donor_index_function(d, grp, i, __FILE__, __LINE__)
410 static gmx_bool isInterchangable(t_hbdata* hb, int d, int a, int grpa, int grpd)
412 /* g_hbond doesn't allow overlapping groups */
417 return donor_index(&hb->d, grpd, a) != NOTSET && acceptor_index(&hb->a, grpa, d) != NOTSET;
422 add_hbond(t_hbdata* hb, int d, int a, int h, int grpd, int grpa, int frame, gmx_bool bMerge, int ihb, gmx_bool bContact)
425 gmx_bool daSwap = FALSE;
427 if ((id = hb->d.dptr[d]) == NOTSET)
429 gmx_fatal(FARGS, "No donor atom %d", d + 1);
431 else if (grpd != hb->d.grp[id])
433 gmx_fatal(FARGS, "Inconsistent donor groups, %d instead of %d, atom %d", grpd, hb->d.grp[id], d + 1);
435 if ((ia = hb->a.aptr[a]) == NOTSET)
437 gmx_fatal(FARGS, "No acceptor atom %d", a + 1);
439 else if (grpa != hb->a.grp[ia])
441 gmx_fatal(FARGS, "Inconsistent acceptor groups, %d instead of %d, atom %d", grpa, hb->a.grp[ia], a + 1);
447 if (isInterchangable(hb, d, a, grpd, grpa) && d > a)
448 /* Then swap identity so that the id of d is lower then that of a.
450 * This should really be redundant by now, as is_hbond() now ought to return
451 * hbNo in the cases where this conditional is TRUE. */
458 /* Now repeat donor/acc check. */
459 if ((id = hb->d.dptr[d]) == NOTSET)
461 gmx_fatal(FARGS, "No donor atom %d", d + 1);
463 else if (grpd != hb->d.grp[id])
466 "Inconsistent donor groups, %d instead of %d, atom %d",
471 if ((ia = hb->a.aptr[a]) == NOTSET)
473 gmx_fatal(FARGS, "No acceptor atom %d", a + 1);
475 else if (grpa != hb->a.grp[ia])
478 "Inconsistent acceptor groups, %d instead of %d, atom %d",
488 /* Loop over hydrogens to find which hydrogen is in this particular HB */
489 if ((ihb == hbHB) && !bMerge && !bContact)
491 for (k = 0; (k < hb->d.nhydro[id]); k++)
493 if (hb->d.hydro[id][k] == h)
498 if (k == hb->d.nhydro[id])
500 gmx_fatal(FARGS, "Donor %d does not have hydrogen %d (a = %d)", d + 1, h + 1, a + 1);
515 if (hb->hbmap[id][ia] == nullptr)
517 snew(hb->hbmap[id][ia], 1);
518 snew(hb->hbmap[id][ia]->h, hb->maxhydro);
519 snew(hb->hbmap[id][ia]->g, hb->maxhydro);
521 add_ff(hb, id, k, ia, frame, ihb);
523 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
527 /* Strange construction with frame >=0 is a relic from old code
528 * for selected hbond analysis. It may be necessary again if that
529 * is made to work again.
533 hh = hb->hbmap[id][ia]->history[k];
539 hb->hbmap[id][ia]->history[k] = hh | 2;
550 hb->hbmap[id][ia]->history[k] = hh | 1;
574 if (bMerge && daSwap)
576 h = hb->d.hydro[id][0];
578 /* Increment number if HBonds per H */
579 if (ihb == hbHB && !bContact)
581 inc_nhbonds(&(hb->d), d, h);
585 static char* mkatomname(const t_atoms* atoms, int i)
590 rnr = atoms->atom[i].resind;
591 sprintf(buf, "%4s%d%-4s", *atoms->resinfo[rnr].name, atoms->resinfo[rnr].nr, *atoms->atomname[i]);
596 static void gen_datable(int* index, int isize, unsigned char* datable, int natoms)
598 /* Generates table of all atoms and sets the ingroup bit for atoms in index[] */
601 for (i = 0; i < isize; i++)
603 if (index[i] >= natoms)
605 gmx_fatal(FARGS, "Atom has index %d larger than number of atoms %d.", index[i], natoms);
607 datable[index[i]] |= c_inGroupMask;
611 static void clear_datable_grp(unsigned char* datable, int size)
613 /* Clears group information from the table */
617 for (i = 0; i < size; i++)
619 datable[i] &= ~c_inGroupMask;
624 static void add_acc(t_acceptors* a, int ia, int grp)
626 if (a->nra >= a->max_nra)
629 srenew(a->acc, a->max_nra);
630 srenew(a->grp, a->max_nra);
632 a->grp[a->nra] = grp;
633 a->acc[a->nra++] = ia;
636 static void search_acceptors(const t_topology* top,
644 unsigned char* datable)
650 for (i = 0; (i < isize); i++)
654 || (((*top->atoms.atomname[n])[0] == 'O')
655 || (bNitAcc && ((*top->atoms.atomname[n])[0] == 'N'))))
656 && ISINGRP(datable[n]))
658 datable[n] |= c_acceptorMask;
663 snew(a->aptr, top->atoms.nr);
664 for (i = 0; (i < top->atoms.nr); i++)
668 for (i = 0; (i < a->nra); i++)
670 a->aptr[a->acc[i]] = i;
674 static void add_h2d(int id, int ih, t_donors* ddd)
678 for (i = 0; (i < ddd->nhydro[id]); i++)
680 if (ddd->hydro[id][i] == ih)
682 printf("Hm. This isn't the first time I found this donor (%d,%d)\n", ddd->don[id], ih);
686 if (i == ddd->nhydro[id])
688 if (ddd->nhydro[id] >= MAXHYDRO)
690 gmx_fatal(FARGS, "Donor %d has more than %d hydrogens!", ddd->don[id], MAXHYDRO);
692 ddd->hydro[id][i] = ih;
697 static void add_dh(t_donors* ddd, int id, int ih, int grp, const unsigned char* datable)
701 if (!datable || ISDON(datable[id]))
703 if (ddd->dptr[id] == NOTSET) /* New donor */
715 if (ddd->nrd >= ddd->max_nrd)
718 srenew(ddd->don, ddd->max_nrd);
719 srenew(ddd->nhydro, ddd->max_nrd);
720 srenew(ddd->hydro, ddd->max_nrd);
721 srenew(ddd->nhbonds, ddd->max_nrd);
722 srenew(ddd->grp, ddd->max_nrd);
724 ddd->don[ddd->nrd] = id;
725 ddd->nhydro[ddd->nrd] = 0;
726 ddd->grp[ddd->nrd] = grp;
737 printf("Warning: Atom %d is not in the d/a-table!\n", id);
741 static void search_donors(const t_topology* top,
748 unsigned char* datable)
751 t_functype func_type;
756 snew(ddd->dptr, top->atoms.nr);
757 for (i = 0; (i < top->atoms.nr); i++)
759 ddd->dptr[i] = NOTSET;
767 for (i = 0; (i < isize); i++)
769 datable[index[i]] |= c_donorMask;
770 add_dh(ddd, index[i], -1, grp, datable);
776 for (func_type = 0; (func_type < F_NRE); func_type++)
778 const t_ilist* interaction = &(top->idef.il[func_type]);
779 if (func_type == F_POSRES || func_type == F_FBPOSRES)
781 /* The ilist looks strange for posre. Bug in grompp?
782 * We don't need posre interactions for hbonds anyway.*/
785 for (i = 0; i < interaction->nr;
786 i += interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms + 1)
789 if (func_type != top->idef.functype[interaction->iatoms[i]])
791 fprintf(stderr, "Error in func_type %s", interaction_function[func_type].longname);
795 /* check out this functype */
796 if (func_type == F_SETTLE)
798 nr1 = interaction->iatoms[i + 1];
799 nr2 = interaction->iatoms[i + 2];
800 nr3 = interaction->iatoms[i + 3];
802 if (ISINGRP(datable[nr1]))
804 if (ISINGRP(datable[nr2]))
806 datable[nr1] |= c_donorMask;
807 add_dh(ddd, nr1, nr1 + 1, grp, datable);
809 if (ISINGRP(datable[nr3]))
811 datable[nr1] |= c_donorMask;
812 add_dh(ddd, nr1, nr1 + 2, grp, datable);
816 else if (IS_CHEMBOND(func_type))
818 for (j = 0; j < 2; j++)
820 nr1 = interaction->iatoms[i + 1 + j];
821 nr2 = interaction->iatoms[i + 2 - j];
822 if ((*top->atoms.atomname[nr1][0] == 'H')
823 && ((*top->atoms.atomname[nr2][0] == 'O') || (*top->atoms.atomname[nr2][0] == 'N'))
824 && ISINGRP(datable[nr1]) && ISINGRP(datable[nr2]))
826 datable[nr2] |= c_donorMask;
827 add_dh(ddd, nr2, nr1, grp, datable);
834 for (func_type = 0; func_type < F_NRE; func_type++)
836 interaction = &top->idef.il[func_type];
837 for (i = 0; i < interaction->nr;
838 i += interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms + 1)
841 if (func_type != top->idef.functype[interaction->iatoms[i]])
843 gmx_incons("function type in search_donors");
846 if (interaction_function[func_type].flags & IF_VSITE)
848 nr1 = interaction->iatoms[i + 1];
849 if (*top->atoms.atomname[nr1][0] == 'H')
853 while (!stop && (*top->atoms.atomname[nr2][0] == 'H'))
865 && ((*top->atoms.atomname[nr2][0] == 'O') || (*top->atoms.atomname[nr2][0] == 'N'))
866 && ISINGRP(datable[nr1]) && ISINGRP(datable[nr2]))
868 datable[nr2] |= c_donorMask;
869 add_dh(ddd, nr2, nr1, grp, datable);
879 static t_gridcell*** init_grid(gmx_bool bBox, rvec box[], real rcut, ivec ngrid)
886 for (i = 0; i < DIM; i++)
888 ngrid[i] = static_cast<int>(box[i][i] / (1.2 * rcut));
892 if (!bBox || (ngrid[XX] < 3) || (ngrid[YY] < 3) || (ngrid[ZZ] < 3))
894 for (i = 0; i < DIM; i++)
901 printf("\nWill do grid-search on %dx%dx%d grid, rcut=%3.8f\n", ngrid[XX], ngrid[YY], ngrid[ZZ], rcut);
903 if (((ngrid[XX] * ngrid[YY] * ngrid[ZZ]) * sizeof(grid)) > INT_MAX)
906 "Failed to allocate memory for %d x %d x %d grid points, which is larger than "
907 "the maximum of %zu. "
908 "You are likely either using a box that is too large (box dimensions are %3.8f "
909 "nm x%3.8f nm x%3.8f nm) or a cutoff (%3.8f nm) that is too small.",
913 INT_MAX / sizeof(grid),
919 snew(grid, ngrid[ZZ]);
920 for (z = 0; z < ngrid[ZZ]; z++)
922 snew((grid)[z], ngrid[YY]);
923 for (y = 0; y < ngrid[YY]; y++)
925 snew((grid)[z][y], ngrid[XX]);
931 static void reset_nhbonds(t_donors* ddd)
935 for (i = 0; (i < ddd->nrd); i++)
937 for (j = 0; (j < MAXHH); j++)
939 ddd->nhbonds[i][j] = 0;
944 static void pbc_correct_gem(rvec dx, matrix box, const rvec hbox);
945 static void pbc_in_gridbox(rvec dx, matrix box);
947 static void build_grid(t_hbdata* hb,
958 int i, m, gr, xi, yi, zi, nr;
961 rvec invdelta, dshell;
963 gmx_bool bDoRshell, bInShell;
968 bDoRshell = (rshell > 0);
969 rshell2 = gmx::square(rshell);
973 if (debug && bDebug) \
974 fprintf(debug, "build_grid, line %d, %s = %d\n", __LINE__, #x, x)
976 for (m = 0; m < DIM; m++)
978 hbox[m] = box[m][m] * 0.5;
981 invdelta[m] = ngrid[m] / box[m][m];
982 if (1 / invdelta[m] < rcut)
985 "Your computational box has shrunk too much.\n"
986 "%s can not handle this situation, sorry.\n",
987 gmx::getProgramContext().displayName());
999 /* resetting atom counts */
1000 for (gr = 0; (gr < grNR); gr++)
1002 for (zi = 0; zi < ngrid[ZZ]; zi++)
1004 for (yi = 0; yi < ngrid[YY]; yi++)
1006 for (xi = 0; xi < ngrid[XX]; xi++)
1008 grid[zi][yi][xi].d[gr].nr = 0;
1009 grid[zi][yi][xi].a[gr].nr = 0;
1015 /* put atoms in grid cells */
1016 for (int acc = 0; acc < 2; acc++)
1029 for (i = 0; (i < nr); i++)
1031 /* check if we are inside the shell */
1032 /* if bDoRshell=FALSE then bInShell=TRUE always */
1037 rvec_sub(x[ad[i]], xshell, dshell);
1040 gmx_bool bDone = FALSE;
1044 for (m = DIM - 1; m >= 0 && bInShell; m--)
1046 if (dshell[m] < -hbox[m])
1049 rvec_inc(dshell, box[m]);
1051 if (dshell[m] >= hbox[m])
1054 dshell[m] -= 2 * hbox[m];
1058 for (m = DIM - 1; m >= 0 && bInShell; m--)
1060 /* if we're outside the cube, we're outside the sphere also! */
1061 if ((dshell[m] > rshell) || (-dshell[m] > rshell))
1067 /* if we're inside the cube, check if we're inside the sphere */
1070 bInShell = norm2(dshell) < rshell2;
1078 pbc_in_gridbox(x[ad[i]], box);
1080 for (m = DIM - 1; m >= 0; m--)
1081 { /* determine grid index of atom */
1082 grididx[m] = static_cast<int>(x[ad[i]][m] * invdelta[m]);
1083 grididx[m] = (grididx[m] + ngrid[m]) % ngrid[m];
1090 range_check(gx, 0, ngrid[XX]);
1091 range_check(gy, 0, ngrid[YY]);
1092 range_check(gz, 0, ngrid[ZZ]);
1096 /* add atom to grid cell */
1099 newgrid = &(grid[gz][gy][gx].a[gr]);
1103 newgrid = &(grid[gz][gy][gx].d[gr]);
1105 if (newgrid->nr >= newgrid->maxnr)
1107 newgrid->maxnr += 10;
1108 DBB(newgrid->maxnr);
1109 srenew(newgrid->atoms, newgrid->maxnr);
1112 newgrid->atoms[newgrid->nr] = ad[i];
1120 static void count_da_grid(const ivec ngrid, t_gridcell*** grid, t_icell danr)
1124 for (gr = 0; (gr < grNR); gr++)
1127 for (zi = 0; zi < ngrid[ZZ]; zi++)
1129 for (yi = 0; yi < ngrid[YY]; yi++)
1131 for (xi = 0; xi < ngrid[XX]; xi++)
1133 danr[gr] += grid[zi][yi][xi].d[gr].nr;
1141 * Without a box, the grid is 1x1x1, so all loops are 1 long.
1142 * With a rectangular box (bTric==FALSE) all loops are 3 long.
1143 * With a triclinic box all loops are 3 long, except when a cell is
1144 * located next to one of the box edges which is not parallel to the
1145 * x/y-plane, in that case all cells in a line or layer are searched.
1146 * This could be implemented slightly more efficient, but the code
1147 * would get much more complicated.
1149 static inline int grid_loop_begin(int n, int x, gmx_bool bTric, gmx_bool bEdge)
1151 return ((n == 1) ? x : bTric && bEdge ? 0 : (x - 1));
1153 static inline int grid_loop_end(int n, int x, gmx_bool bTric, gmx_bool bEdge)
1155 return ((n == 1) ? x : bTric && bEdge ? (n - 1) : (x + 1));
1157 static inline int grid_mod(int j, int n)
1159 return (j + n) % (n);
1162 static void dump_grid(FILE* fp, ivec ngrid, t_gridcell*** grid)
1164 int gr, x, y, z, sum[grNR];
1166 fprintf(fp, "grid %dx%dx%d\n", ngrid[XX], ngrid[YY], ngrid[ZZ]);
1167 for (gr = 0; gr < grNR; gr++)
1170 fprintf(fp, "GROUP %d (%s)\n", gr, grpnames[gr]);
1171 for (z = 0; z < ngrid[ZZ]; z += 2)
1173 fprintf(fp, "Z=%d,%d\n", z, z + 1);
1174 for (y = 0; y < ngrid[YY]; y++)
1176 for (x = 0; x < ngrid[XX]; x++)
1178 fprintf(fp, "%3d", grid[x][y][z].d[gr].nr);
1179 sum[gr] += grid[z][y][x].d[gr].nr;
1180 fprintf(fp, "%3d", grid[x][y][z].a[gr].nr);
1181 sum[gr] += grid[z][y][x].a[gr].nr;
1184 if ((z + 1) < ngrid[ZZ])
1186 for (x = 0; x < ngrid[XX]; x++)
1188 fprintf(fp, "%3d", grid[z + 1][y][x].d[gr].nr);
1189 sum[gr] += grid[z + 1][y][x].d[gr].nr;
1190 fprintf(fp, "%3d", grid[z + 1][y][x].a[gr].nr);
1191 sum[gr] += grid[z + 1][y][x].a[gr].nr;
1198 fprintf(fp, "TOTALS:");
1199 for (gr = 0; gr < grNR; gr++)
1201 fprintf(fp, " %d=%d", gr, sum[gr]);
1206 /* New GMX record! 5 * in a row. Congratulations!
1207 * Sorry, only four left.
1209 static void free_grid(const ivec ngrid, t_gridcell**** grid)
1212 t_gridcell*** g = *grid;
1214 for (z = 0; z < ngrid[ZZ]; z++)
1216 for (y = 0; y < ngrid[YY]; y++)
1226 static void pbc_correct_gem(rvec dx, matrix box, const rvec hbox)
1229 gmx_bool bDone = FALSE;
1233 for (m = DIM - 1; m >= 0; m--)
1235 if (dx[m] < -hbox[m])
1238 rvec_inc(dx, box[m]);
1240 if (dx[m] >= hbox[m])
1243 rvec_dec(dx, box[m]);
1249 static void pbc_in_gridbox(rvec dx, matrix box)
1252 gmx_bool bDone = FALSE;
1256 for (m = DIM - 1; m >= 0; m--)
1261 rvec_inc(dx, box[m]);
1263 if (dx[m] >= box[m][m])
1266 rvec_dec(dx, box[m]);
1272 /* Added argument r2cut, changed contact and implemented
1273 * use of second cut-off.
1274 * - Erik Marklund, June 29, 2006
1276 static int is_hbond(t_hbdata* hb,
1296 rvec r_da, r_ha, r_dh;
1297 real rc2, r2c2, rda2, rha2, ca;
1298 gmx_bool HAinrange = FALSE; /* If !bDA. Needed for returning hbDist in a correct way. */
1299 gmx_bool daSwap = FALSE;
1306 if (((id = donor_index(&hb->d, grpd, d)) == NOTSET) || (acceptor_index(&hb->a, grpa, a) == NOTSET))
1312 r2c2 = r2cut * r2cut;
1314 rvec_sub(x[d], x[a], r_da);
1315 /* Insert projection code here */
1317 if (bMerge && d > a && isInterchangable(hb, d, a, grpd, grpa))
1319 /* Then this hbond/contact will be found again, or it has already been found. */
1325 && isInterchangable(hb, d, a, grpd, grpa)) /* acceptor is also a donor and vice versa? */
1326 { /* return hbNo; */
1327 daSwap = TRUE; /* If so, then their history should be filed with donor and acceptor swapped. */
1329 pbc_correct_gem(r_da, box, hbox);
1331 rda2 = iprod(r_da, r_da);
1335 if (daSwap && grpa == grpd)
1343 else if (rda2 < r2c2)
1354 if (bDA && (rda2 > rc2))
1359 for (h = 0; (h < hb->d.nhydro[id]); h++)
1361 hh = hb->d.hydro[id][h];
1365 rvec_sub(x[hh], x[a], r_ha);
1368 pbc_correct_gem(r_ha, box, hbox);
1370 rha2 = iprod(r_ha, r_ha);
1373 if (bDA || (rha2 <= rc2))
1375 rvec_sub(x[d], x[hh], r_dh);
1378 pbc_correct_gem(r_dh, box, hbox);
1385 ca = cos_angle(r_dh, r_da);
1386 /* if angle is smaller, cos is larger */
1390 *d_ha = std::sqrt(bDA ? rda2 : rha2);
1391 *ang = std::acos(ca);
1396 if (bDA || HAinrange)
1406 /* Merging is now done on the fly, so do_merge is most likely obsolete now.
1407 * Will do some more testing before removing the function entirely.
1408 * - Erik Marklund, MAY 10 2010 */
1409 static void do_merge(t_hbdata* hb, int ntmp, bool htmp[], bool gtmp[], t_hbond* hb0, t_hbond* hb1)
1411 /* Here we need to make sure we're treating periodicity in
1412 * the right way for the geminate recombination kinetics. */
1414 int m, mm, n00, n01, nn0, nnframes;
1416 /* Decide where to start from when merging */
1419 nn0 = std::min(n00, n01);
1420 nnframes = std::max(n00 + hb0->nframes, n01 + hb1->nframes) - nn0;
1421 /* Initiate tmp arrays */
1422 for (m = 0; (m < ntmp); m++)
1427 /* Fill tmp arrays with values due to first HB */
1428 /* Once again '<' had to be replaced with '<='
1429 to catch the last frame in which the hbond
1431 - Erik Marklund, June 1, 2006 */
1432 for (m = 0; (m <= hb0->nframes); m++)
1435 htmp[mm] = is_hb(hb0->h[0], m);
1437 for (m = 0; (m <= hb0->nframes); m++)
1440 gtmp[mm] = is_hb(hb0->g[0], m);
1443 for (m = 0; (m <= hb1->nframes); m++)
1446 htmp[mm] = htmp[mm] || is_hb(hb1->h[0], m);
1447 gtmp[mm] = gtmp[mm] || is_hb(hb1->g[0], m);
1449 /* Reallocate target array */
1450 if (nnframes > hb0->maxframes)
1452 srenew(hb0->h[0], 4 + nnframes / hb->wordlen);
1453 srenew(hb0->g[0], 4 + nnframes / hb->wordlen);
1456 /* Copy temp array to target array */
1457 for (m = 0; (m <= nnframes); m++)
1459 set_hb_function(hb0->h[0], m, htmp[m]);
1460 set_hb_function(hb0->g[0], m, gtmp[m]);
1463 /* Set scalar variables */
1465 hb0->maxframes = nnframes;
1468 static void merge_hb(t_hbdata* hb, gmx_bool bTwo, gmx_bool bContact)
1470 int i, inrnew, indnew, j, ii, jj, id, ia, ntmp;
1475 indnew = hb->nrdist;
1477 /* Check whether donors are also acceptors */
1478 printf("Merging hbonds with Acceptor and Donor swapped\n");
1480 ntmp = 2 * hb->max_frames;
1483 for (i = 0; (i < hb->d.nrd); i++)
1485 fprintf(stderr, "\r%d/%d", i + 1, hb->d.nrd);
1488 ii = hb->a.aptr[id];
1489 for (j = 0; (j < hb->a.nra); j++)
1492 jj = hb->d.dptr[ia];
1493 if ((id != ia) && (ii != NOTSET) && (jj != NOTSET)
1494 && (!bTwo || (hb->d.grp[i] != hb->a.grp[j])))
1496 hb0 = hb->hbmap[i][j];
1497 hb1 = hb->hbmap[jj][ii];
1498 if (hb0 && hb1 && ISHB(hb0->history[0]) && ISHB(hb1->history[0]))
1500 do_merge(hb, ntmp, htmp, gtmp, hb0, hb1);
1501 if (ISHB(hb1->history[0]))
1505 else if (ISDIST(hb1->history[0]))
1511 gmx_incons("No contact history");
1515 gmx_incons("Neither hydrogen bond nor distance");
1519 hb1->h[0] = nullptr;
1520 hb1->g[0] = nullptr;
1521 hb1->history[0] = hbNo;
1526 fprintf(stderr, "\n");
1527 printf("- Reduced number of hbonds from %d to %d\n", hb->nrhb, inrnew);
1528 printf("- Reduced number of distances from %d to %d\n", hb->nrdist, indnew);
1530 hb->nrdist = indnew;
1535 static void do_nhb_dist(FILE* fp, t_hbdata* hb, real t)
1537 int i, j, k, n_bound[MAXHH], nbtot;
1539 /* Set array to 0 */
1540 for (k = 0; (k < MAXHH); k++)
1544 /* Loop over possible donors */
1545 for (i = 0; (i < hb->d.nrd); i++)
1547 for (j = 0; (j < hb->d.nhydro[i]); j++)
1549 n_bound[hb->d.nhbonds[i][j]]++;
1552 fprintf(fp, "%12.5e", t);
1554 for (k = 0; (k < MAXHH); k++)
1556 fprintf(fp, " %8d", n_bound[k]);
1557 nbtot += n_bound[k] * k;
1559 fprintf(fp, " %8d\n", nbtot);
1562 static void do_hblife(const char* fn, t_hbdata* hb, gmx_bool bMerge, gmx_bool bContact, const gmx_output_env_t* oenv)
1565 const char* leg[] = { "p(t)", "t p(t)" };
1567 int i, j, j0, k, m, nh, ihb, ohb, nhydro, ndump = 0;
1568 int nframes = hb->nframes;
1571 double sum, integral;
1574 snew(h, hb->maxhydro);
1575 snew(histo, nframes + 1);
1576 /* Total number of hbonds analyzed here */
1577 for (i = 0; (i < hb->d.nrd); i++)
1579 for (k = 0; (k < hb->a.nra); k++)
1581 hbh = hb->hbmap[i][k];
1599 for (m = 0; (m < hb->maxhydro); m++)
1603 h[nhydro++] = bContact ? hbh->g[m] : hbh->h[m];
1607 for (nh = 0; (nh < nhydro); nh++)
1612 for (j = 0; (j <= hbh->nframes); j++)
1614 ihb = static_cast<int>(is_hb(h[nh], j));
1615 if (debug && (ndump < 10))
1617 fprintf(debug, "%5d %5d\n", j, ihb);
1637 fprintf(stderr, "\n");
1640 fp = xvgropen(fn, "Uninterrupted contact lifetime", output_env_get_xvgr_tlabel(oenv), "()", oenv);
1645 fn, "Uninterrupted hydrogen bond lifetime", output_env_get_xvgr_tlabel(oenv), "()", oenv);
1648 xvgr_legend(fp, asize(leg), leg, oenv);
1650 while ((j0 > 0) && (histo[j0] == 0))
1655 for (i = 0; (i <= j0); i++)
1659 dt = hb->time[1] - hb->time[0];
1662 for (i = 1; (i <= j0); i++)
1664 t = hb->time[i] - hb->time[0] - 0.5 * dt;
1665 x1 = t * histo[i] / sum;
1666 fprintf(fp, "%8.3f %10.3e %10.3e\n", t, histo[i] / sum, x1);
1671 printf("%s lifetime = %.2f ps\n", bContact ? "Contact" : "HB", integral);
1672 printf("Note that the lifetime obtained in this manner is close to useless\n");
1673 printf("Use the -ac option instead and check the Forward lifetime\n");
1674 please_cite(stdout, "Spoel2006b");
1679 static void dump_ac(t_hbdata* hb, gmx_bool oneHB, int nDump)
1682 int i, j, k, m, nd, ihb, idist;
1683 int nframes = hb->nframes;
1691 fp = gmx_ffopen("debug-ac.xvg", "w");
1692 for (j = 0; (j < nframes); j++)
1694 fprintf(fp, "%10.3f", hb->time[j]);
1695 for (i = nd = 0; (i < hb->d.nrd) && (nd < nDump); i++)
1697 for (k = 0; (k < hb->a.nra) && (nd < nDump); k++)
1701 hbh = hb->hbmap[i][k];
1706 ihb = static_cast<int>(is_hb(hbh->h[0], j));
1707 idist = static_cast<int>(is_hb(hbh->g[0], j));
1713 for (m = 0; (m < hb->maxhydro) && !ihb; m++)
1715 ihb = static_cast<int>((ihb != 0)
1716 || (((hbh->h[m]) != nullptr) && is_hb(hbh->h[m], j)));
1717 idist = static_cast<int>((idist != 0)
1718 || (((hbh->g[m]) != nullptr) && is_hb(hbh->g[m], j)));
1720 /* This is not correct! */
1721 /* What isn't correct? -Erik M */
1726 fprintf(fp, " %1d-%1d", ihb, idist);
1736 static real calc_dg(real tau, real temp)
1740 kbt = gmx::c_boltz * temp;
1747 return kbt * std::log(kbt * tau / gmx::c_planck);
1753 int n0, n1, nparams, ndelta;
1755 real *t, *ct, *nt, *kt, *sigma_ct, *sigma_nt, *sigma_kt;
1758 static real compute_weighted_rates(int n,
1775 real kkk = 0, kkp = 0, kk2 = 0, kp2 = 0, chi2;
1780 for (i = 0; (i < n); i++)
1782 if (t[i] >= fit_start)
1795 tl.sigma_ct = sigma_ct;
1796 tl.sigma_nt = sigma_nt;
1797 tl.sigma_kt = sigma_kt;
1801 chi2 = 0; /*optimize_luzar_parameters(debug, &tl, 1000, 1e-3); */
1803 *kp = tl.kkk[1] = *kp;
1805 for (j = 0; (j < NK); j++)
1809 kk2 += gmx::square(tl.kkk[0]);
1810 kp2 += gmx::square(tl.kkk[1]);
1813 *sigma_k = std::sqrt(kk2 / NK - gmx::square(kkk / NK));
1814 *sigma_kp = std::sqrt(kp2 / NK - gmx::square(kkp / NK));
1819 void analyse_corr(int n,
1831 real k = 1, kp = 1, kow = 1;
1832 real Q = 0, chi2, tau_hb, dtau, tau_rlx, e_1, sigma_k, sigma_kp, ddg;
1833 double tmp, sn2 = 0, sc2 = 0, sk2 = 0, scn = 0, sck = 0, snk = 0;
1834 gmx_bool bError = (sigma_ct != nullptr) && (sigma_nt != nullptr) && (sigma_kt != nullptr);
1836 for (i0 = 0; (i0 < n - 2) && ((t[i0] - t[0]) < fit_start); i0++) {}
1839 for (i = i0; (i < n); i++)
1841 sc2 += gmx::square(ct[i]);
1842 sn2 += gmx::square(nt[i]);
1843 sk2 += gmx::square(kt[i]);
1844 sck += ct[i] * kt[i];
1845 snk += nt[i] * kt[i];
1846 scn += ct[i] * nt[i];
1848 printf("Hydrogen bond thermodynamics at T = %g K\n", temp);
1849 tmp = (sn2 * sc2 - gmx::square(scn));
1850 if ((tmp > 0) && (sn2 > 0))
1852 k = (sn2 * sck - scn * snk) / tmp;
1853 kp = (k * scn - snk) / sn2;
1857 for (i = i0; (i < n); i++)
1859 chi2 += gmx::square(k * ct[i] - kp * nt[i] - kt[i]);
1861 compute_weighted_rates(
1862 n, t, ct, nt, kt, sigma_ct, sigma_nt, sigma_kt, &k, &kp, &sigma_k, &sigma_kp, fit_start);
1863 Q = 0; /* quality_of_fit(chi2, 2);*/
1864 ddg = gmx::c_boltz * temp * sigma_k / k;
1865 printf("Fitting paramaters chi^2 = %10g, Quality of fit = %10g\n", chi2, Q);
1866 printf("The Rate and Delta G are followed by an error estimate\n");
1867 printf("----------------------------------------------------------\n"
1868 "Type Rate (1/ps) Sigma Time (ps) DG (kJ/mol) Sigma\n");
1869 printf("Forward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
1873 calc_dg(1 / k, temp),
1875 ddg = gmx::c_boltz * temp * sigma_kp / kp;
1876 printf("Backward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
1880 calc_dg(1 / kp, temp),
1886 for (i = i0; (i < n); i++)
1888 chi2 += gmx::square(k * ct[i] - kp * nt[i] - kt[i]);
1890 printf("Fitting parameters chi^2 = %10g\nQ = %10g\n", chi2, Q);
1891 printf("--------------------------------------------------\n"
1892 "Type Rate (1/ps) Time (ps) DG (kJ/mol) Chi^2\n");
1893 printf("Forward %10.3f %8.3f %10.3f %10g\n", k, 1 / k, calc_dg(1 / k, temp), chi2);
1894 printf("Backward %10.3f %8.3f %10.3f\n", kp, 1 / kp, calc_dg(1 / kp, temp));
1899 kow = 2 * sck / sc2;
1900 printf("One-way %10.3f %s%8.3f %10.3f\n",
1904 calc_dg(1 / kow, temp));
1908 printf(" - Numerical problems computing HB thermodynamics:\n"
1909 "sc2 = %g sn2 = %g sk2 = %g sck = %g snk = %g scn = %g\n",
1917 /* Determine integral of the correlation function */
1918 tau_hb = evaluate_integral(n, t, ct, nullptr, (t[n - 1] - t[0]) / 2, &dtau);
1919 printf("Integral %10.3f %s%8.3f %10.3f\n",
1923 calc_dg(tau_hb, temp));
1924 e_1 = std::exp(-1.0);
1925 for (i = 0; (i < n - 2); i++)
1927 if ((ct[i] > e_1) && (ct[i + 1] <= e_1))
1934 /* Determine tau_relax from linear interpolation */
1935 tau_rlx = t[i] - t[0] + (e_1 - ct[i]) * (t[i + 1] - t[i]) / (ct[i + 1] - ct[i]);
1936 printf("Relaxation %10.3f %8.3f %s%10.3f\n",
1940 calc_dg(tau_rlx, temp));
1945 printf("Correlation functions too short to compute thermodynamics\n");
1949 void compute_derivative(int nn, const real x[], const real y[], real dydx[])
1953 /* Compute k(t) = dc(t)/dt */
1954 for (j = 1; (j < nn - 1); j++)
1956 dydx[j] = (y[j + 1] - y[j - 1]) / (x[j + 1] - x[j - 1]);
1958 /* Extrapolate endpoints */
1959 dydx[0] = 2 * dydx[1] - dydx[2];
1960 dydx[nn - 1] = 2 * dydx[nn - 2] - dydx[nn - 3];
1963 static void normalizeACF(real* ct, real* gt, int nhb, int len)
1965 real ct_fac, gt_fac = 0;
1968 /* Xu and Berne use the same normalization constant */
1970 ct_fac = 1.0 / ct[0];
1976 printf("Normalization for c(t) = %g for gh(t) = %g\n", ct_fac, gt_fac);
1977 for (i = 0; i < len; i++)
1987 static void do_hbac(const char* fn,
1995 const gmx_output_env_t* oenv,
1999 int i, j, k, m, ihb, idist, n2, nn;
2001 const char* legLuzar[] = { "Ac\\sfin sys\\v{}\\z{}(t)",
2003 "Cc\\scontact,hb\\v{}\\z{}(t)",
2004 "-dAc\\sfs\\v{}\\z{}/dt" };
2005 gmx_bool bNorm = FALSE;
2007 real * rhbex = nullptr, *ht, *gt, *ght, *dght, *kt;
2008 real * ct, tail, tail2, dtail, *cct;
2009 const real tol = 1e-3;
2010 int nframes = hb->nframes;
2011 unsigned int **h = nullptr, **g = nullptr;
2012 int nh, nhbonds, nhydro;
2015 int* dondata = nullptr;
2025 const bool bOMP = GMX_OPENMP;
2027 printf("Doing autocorrelation ");
2030 printf("according to the theory of Luzar and Chandler.\n");
2033 /* build hbexist matrix in reals for autocorr */
2034 /* Allocate memory for computing ACF (rhbex) and aggregating the ACF (ct) */
2036 while (n2 < nframes)
2043 if (acType != AC_NN || bOMP)
2045 snew(h, hb->maxhydro);
2046 snew(g, hb->maxhydro);
2049 /* Dump hbonds for debugging */
2050 dump_ac(hb, bMerge || bContact, nDump);
2052 /* Total number of hbonds analyzed here */
2055 if (acType != AC_LUZAR && bOMP)
2057 nThreads = std::min((nThreads <= 0) ? INT_MAX : nThreads, gmx_omp_get_max_threads());
2059 gmx_omp_set_num_threads(nThreads);
2060 snew(dondata, nThreads);
2061 for (i = 0; i < nThreads; i++)
2065 printf("ACF calculations parallelized with OpenMP using %i threads.\n"
2066 "Expect close to linear scaling over this donor-loop.\n",
2069 fprintf(stderr, "Donors: [thread no]\n");
2072 for (i = 0; i < nThreads; i++)
2074 snprintf(tmpstr, 7, "[%i]", i);
2075 fprintf(stderr, "%-7s", tmpstr);
2078 fprintf(stderr, "\n");
2083 snew(rhbex, 2 * n2);
2093 for (i = 0; (i < hb->d.nrd); i++)
2095 for (k = 0; (k < hb->a.nra); k++)
2098 hbh = hb->hbmap[i][k];
2102 if (bMerge || bContact)
2104 if (ISHB(hbh->history[0]))
2113 for (m = 0; (m < hb->maxhydro); m++)
2115 if (bContact ? ISDIST(hbh->history[m]) : ISHB(hbh->history[m]))
2117 g[nhydro] = hbh->g[m];
2118 h[nhydro] = hbh->h[m];
2124 int nf = hbh->nframes;
2125 for (nh = 0; (nh < nhydro); nh++)
2127 int nrint = bContact ? hb->nrdist : hb->nrhb;
2128 if ((((nhbonds + 1) % 10) == 0) || (nhbonds + 1 == nrint))
2130 fprintf(stderr, "\rACF %d/%d", nhbonds + 1, nrint);
2134 for (j = 0; (j < nframes); j++)
2138 ihb = static_cast<int>(is_hb(h[nh], j));
2139 idist = static_cast<int>(is_hb(g[nh], j));
2146 /* For contacts: if a second cut-off is provided, use it,
2147 * otherwise use g(t) = 1-h(t) */
2148 if (!R2 && bContact)
2154 gt[j] = idist * (1 - ihb);
2160 /* The autocorrelation function is normalized after summation only */
2161 low_do_autocorr(nullptr,
2168 hb->time[1] - hb->time[0],
2178 /* Cross correlation analysis for thermodynamics */
2179 for (j = nframes; (j < n2); j++)
2185 cross_corr(n2, ht, gt, dght);
2187 for (j = 0; (j < nn); j++)
2196 fprintf(stderr, "\n");
2199 normalizeACF(ct, ght, static_cast<int>(nhb), nn);
2201 /* Determine tail value for statistics */
2204 for (j = nn / 2; (j < nn); j++)
2207 tail2 += ct[j] * ct[j];
2209 tail /= (nn - int{ nn / 2 });
2210 tail2 /= (nn - int{ nn / 2 });
2211 dtail = std::sqrt(tail2 - tail * tail);
2213 /* Check whether the ACF is long enough */
2216 printf("\nWARNING: Correlation function is probably not long enough\n"
2217 "because the standard deviation in the tail of C(t) > %g\n"
2218 "Tail value (average C(t) over second half of acf): %g +/- %g\n",
2223 for (j = 0; (j < nn); j++)
2226 ct[j] = (cct[j] - tail) / (1 - tail);
2228 /* Compute negative derivative k(t) = -dc(t)/dt */
2229 compute_derivative(nn, hb->time, ct, kt);
2230 for (j = 0; (j < nn); j++)
2238 fp = xvgropen(fn, "Contact Autocorrelation", output_env_get_xvgr_tlabel(oenv), "C(t)", oenv);
2242 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation", output_env_get_xvgr_tlabel(oenv), "C(t)", oenv);
2244 xvgr_legend(fp, asize(legLuzar), legLuzar, oenv);
2247 for (j = 0; (j < nn); j++)
2249 fprintf(fp, "%10g %10g %10g %10g %10g\n", hb->time[j] - hb->time[0], ct[j], cct[j], ght[j], kt[j]);
2253 analyse_corr(nn, hb->time, ct, ght, kt, nullptr, nullptr, nullptr, fit_start, temp);
2255 do_view(oenv, fn, nullptr);
2266 static void init_hbframe(t_hbdata* hb, int nframes, real t)
2270 hb->time[nframes] = t;
2271 hb->nhb[nframes] = 0;
2272 hb->ndist[nframes] = 0;
2273 for (i = 0; (i < max_hx); i++)
2275 hb->nhx[nframes][i] = 0;
2279 static FILE* open_donor_properties_file(const char* fn, t_hbdata* hb, const gmx_output_env_t* oenv)
2282 const char* leg[] = { "Nbound", "Nfree" };
2289 fp = xvgropen(fn, "Donor properties", output_env_get_xvgr_tlabel(oenv), "Number", oenv);
2290 xvgr_legend(fp, asize(leg), leg, oenv);
2295 static void analyse_donor_properties(FILE* fp, t_hbdata* hb, int nframes, real t)
2297 int i, j, k, nbound, nb, nhtot;
2305 for (i = 0; (i < hb->d.nrd); i++)
2307 for (k = 0; (k < hb->d.nhydro[i]); k++)
2311 for (j = 0; (j < hb->a.nra) && (nb == 0); j++)
2313 if (hb->hbmap[i][j] && hb->hbmap[i][j]->h[k] && is_hb(hb->hbmap[i][j]->h[k], nframes))
2321 fprintf(fp, "%10.3e %6d %6d\n", t, nbound, nhtot - nbound);
2324 static void dump_hbmap(t_hbdata* hb,
2332 const t_atoms* atoms)
2335 int ddd, hhh, aaa, i, j, k, m, grp;
2336 char ds[32], hs[32], as[32];
2339 fp = opt2FILE("-hbn", nfile, fnm, "w");
2340 if (opt2bSet("-g", nfile, fnm))
2342 fplog = gmx_ffopen(opt2fn("-g", nfile, fnm), "w");
2343 fprintf(fplog, "# %10s %12s %12s\n", "Donor", "Hydrogen", "Acceptor");
2349 for (grp = gr0; grp <= (bTwo ? gr1 : gr0); grp++)
2351 fprintf(fp, "[ %s ]", grpnames[grp]);
2352 for (i = 0; i < isize[grp]; i++)
2354 fprintf(fp, (i % 15) ? " " : "\n");
2355 fprintf(fp, " %4d", index[grp][i] + 1);
2361 fprintf(fp, "[ donors_hydrogens_%s ]\n", grpnames[grp]);
2362 for (i = 0; (i < hb->d.nrd); i++)
2364 if (hb->d.grp[i] == grp)
2366 for (j = 0; (j < hb->d.nhydro[i]); j++)
2368 fprintf(fp, " %4d %4d", hb->d.don[i] + 1, hb->d.hydro[i][j] + 1);
2374 fprintf(fp, "[ acceptors_%s ]", grpnames[grp]);
2375 for (i = 0; (i < hb->a.nra); i++)
2377 if (hb->a.grp[i] == grp)
2379 fprintf(fp, (i % 15 && !first) ? " " : "\n");
2380 fprintf(fp, " %4d", hb->a.acc[i] + 1);
2389 fprintf(fp, bContact ? "[ contacts_%s-%s ]\n" : "[ hbonds_%s-%s ]\n", grpnames[0], grpnames[1]);
2393 fprintf(fp, bContact ? "[ contacts_%s ]" : "[ hbonds_%s ]\n", grpnames[0]);
2396 for (i = 0; (i < hb->d.nrd); i++)
2399 for (k = 0; (k < hb->a.nra); k++)
2402 for (m = 0; (m < hb->d.nhydro[i]); m++)
2404 if (hb->hbmap[i][k] && ISHB(hb->hbmap[i][k]->history[m]))
2406 sprintf(ds, "%s", mkatomname(atoms, ddd));
2407 sprintf(as, "%s", mkatomname(atoms, aaa));
2410 fprintf(fp, " %6d %6d\n", ddd + 1, aaa + 1);
2413 fprintf(fplog, "%12s %12s\n", ds, as);
2418 hhh = hb->d.hydro[i][m];
2419 sprintf(hs, "%s", mkatomname(atoms, hhh));
2420 fprintf(fp, " %6d %6d %6d\n", ddd + 1, hhh + 1, aaa + 1);
2423 fprintf(fplog, "%12s %12s %12s\n", ds, hs, as);
2437 /* sync_hbdata() updates the parallel t_hbdata p_hb using hb as template.
2438 * It mimics add_frames() and init_frame() to some extent. */
2439 static void sync_hbdata(t_hbdata* p_hb, int nframes)
2441 if (nframes >= p_hb->max_frames)
2443 p_hb->max_frames += 4096;
2444 srenew(p_hb->nhb, p_hb->max_frames);
2445 srenew(p_hb->ndist, p_hb->max_frames);
2446 srenew(p_hb->n_bound, p_hb->max_frames);
2447 srenew(p_hb->nhx, p_hb->max_frames);
2450 srenew(p_hb->danr, p_hb->max_frames);
2452 std::memset(&(p_hb->nhb[nframes]), 0, sizeof(int) * (p_hb->max_frames - nframes));
2453 std::memset(&(p_hb->ndist[nframes]), 0, sizeof(int) * (p_hb->max_frames - nframes));
2454 p_hb->nhb[nframes] = 0;
2455 p_hb->ndist[nframes] = 0;
2457 p_hb->nframes = nframes;
2459 std::memset(&(p_hb->nhx[nframes]), 0, sizeof(int) * max_hx); /* zero the helix count for this frame */
2462 int gmx_hbond(int argc, char* argv[])
2464 const char* desc[] = {
2465 "[THISMODULE] computes and analyzes hydrogen bonds. Hydrogen bonds are",
2466 "determined based on cutoffs for the angle Hydrogen - Donor - Acceptor",
2467 "(zero is extended) and the distance Donor - Acceptor",
2468 "(or Hydrogen - Acceptor using [TT]-noda[tt]).",
2469 "OH and NH groups are regarded as donors, O is an acceptor always,",
2470 "N is an acceptor by default, but this can be switched using",
2471 "[TT]-nitacc[tt]. Dummy hydrogen atoms are assumed to be connected",
2472 "to the first preceding non-hydrogen atom.[PAR]",
2474 "You need to specify two groups for analysis, which must be either",
2475 "identical or non-overlapping. All hydrogen bonds between the two",
2476 "groups are analyzed.[PAR]",
2478 "If you set [TT]-shell[tt], you will be asked for an additional index group",
2479 "which should contain exactly one atom. In this case, only hydrogen",
2480 "bonds between atoms within the shell distance from the one atom are",
2483 "With option -ac, rate constants for hydrogen bonding can be derived with the",
2484 "model of Luzar and Chandler (Nature 379:55, 1996; J. Chem. Phys. 113:23, 2000).",
2485 "If contact kinetics are analyzed by using the -contact option, then",
2486 "n(t) can be defined as either all pairs that are not within contact distance r at time t",
2487 "(corresponding to leaving the -r2 option at the default value 0) or all pairs that",
2488 "are within distance r2 (corresponding to setting a second cut-off value with option -r2).",
2489 "See mentioned literature for more details and definitions.",
2492 /* "It is also possible to analyse specific hydrogen bonds with",
2493 "[TT]-sel[tt]. This index file must contain a group of atom triplets",
2494 "Donor Hydrogen Acceptor, in the following way::",
2501 "Note that the triplets need not be on separate lines.",
2502 "Each atom triplet specifies a hydrogen bond to be analyzed,",
2503 "note also that no check is made for the types of atoms.[PAR]",
2508 " * [TT]-num[tt]: number of hydrogen bonds as a function of time.",
2509 " * [TT]-ac[tt]: average over all autocorrelations of the existence",
2510 " functions (either 0 or 1) of all hydrogen bonds.",
2511 " * [TT]-dist[tt]: distance distribution of all hydrogen bonds.",
2512 " * [TT]-ang[tt]: angle distribution of all hydrogen bonds.",
2513 " * [TT]-hx[tt]: the number of n-n+i hydrogen bonds as a function of time",
2514 " where n and n+i stand for residue numbers and i ranges from 0 to 6.",
2515 " This includes the n-n+3, n-n+4 and n-n+5 hydrogen bonds associated",
2516 " with helices in proteins.",
2517 " * [TT]-hbn[tt]: all selected groups, donors, hydrogens and acceptors",
2518 " for selected groups, all hydrogen bonded atoms from all groups and",
2519 " all solvent atoms involved in insertion.",
2520 " * [TT]-hbm[tt]: existence matrix for all hydrogen bonds over all",
2521 " frames, this also contains information on solvent insertion",
2522 " into hydrogen bonds. Ordering is identical to that in [TT]-hbn[tt]",
2524 " * [TT]-dan[tt]: write out the number of donors and acceptors analyzed for",
2525 " each timeframe. This is especially useful when using [TT]-shell[tt].",
2526 " * [TT]-nhbdist[tt]: compute the number of HBonds per hydrogen in order to",
2527 " compare results to Raman Spectroscopy.",
2529 "Note: options [TT]-ac[tt], [TT]-life[tt], [TT]-hbn[tt] and [TT]-hbm[tt]",
2530 "require an amount of memory proportional to the total numbers of donors",
2531 "times the total number of acceptors in the selected group(s)."
2534 static real acut = 30, abin = 1, rcut = 0.35, r2cut = 0, rbin = 0.005, rshell = -1;
2535 static real maxnhb = 0, fit_start = 1, fit_end = 60, temp = 298.15;
2536 static gmx_bool bNitAcc = TRUE, bDA = TRUE, bMerge = TRUE;
2537 static int nDump = 0;
2538 static int nThreads = 0;
2540 static gmx_bool bContact = FALSE;
2544 { "-a", FALSE, etREAL, { &acut }, "Cutoff angle (degrees, Hydrogen - Donor - Acceptor)" },
2545 { "-r", FALSE, etREAL, { &rcut }, "Cutoff radius (nm, X - Acceptor, see next option)" },
2550 "Use distance Donor-Acceptor (if TRUE) or Hydrogen-Acceptor (FALSE)" },
2555 "Second cutoff radius. Mainly useful with [TT]-contact[tt] and [TT]-ac[tt]" },
2556 { "-abin", FALSE, etREAL, { &abin }, "Binwidth angle distribution (degrees)" },
2557 { "-rbin", FALSE, etREAL, { &rbin }, "Binwidth distance distribution (nm)" },
2558 { "-nitacc", FALSE, etBOOL, { &bNitAcc }, "Regard nitrogen atoms as acceptors" },
2563 "Do not look for hydrogen bonds, but merely for contacts within the cut-off distance" },
2568 "when > 0, only calculate hydrogen bonds within # nm shell around "
2574 "Time (ps) from which to start fitting the correlation functions in order to obtain the "
2575 "forward and backward rate constants for HB breaking and formation. With [TT]-gemfit[tt] "
2576 "we suggest [TT]-fitstart 0[tt]" },
2581 "Time (ps) to which to stop fitting the correlation functions in order to obtain the "
2582 "forward and backward rate constants for HB breaking and formation (only with "
2583 "[TT]-gemfit[tt])" },
2588 "Temperature (K) for computing the Gibbs energy corresponding to HB breaking and "
2594 "Dump the first N hydrogen bond ACFs in a single [REF].xvg[ref] file for debugging" },
2599 "Theoretical maximum number of hydrogen bonds used for normalizing HB autocorrelation "
2600 "function. Can be useful in case the program estimates it wrongly" },
2605 "H-bonds between the same donor and acceptor, but with different hydrogen are treated as "
2606 "a single H-bond. Mainly important for the ACF." },
2612 "Number of threads used for the parallel loop over autocorrelations. nThreads <= 0 means "
2613 "maximum number of threads. Requires linking with OpenMP. The number of threads is "
2614 "limited by the number of cores (before OpenMP v.3 ) or environment variable "
2615 "OMP_THREAD_LIMIT (OpenMP v.3)" },
2618 const char* bugs[] = {
2619 "The option [TT]-sel[tt] that used to work on selected hbonds is out of order, and "
2620 "therefore not available for the time being."
2622 t_filenm fnm[] = { { efTRX, "-f", nullptr, ffREAD },
2623 { efTPR, nullptr, nullptr, ffREAD },
2624 { efNDX, nullptr, nullptr, ffOPTRD },
2625 /* { efNDX, "-sel", "select", ffOPTRD },*/
2626 { efXVG, "-num", "hbnum", ffWRITE },
2627 { efLOG, "-g", "hbond", ffOPTWR },
2628 { efXVG, "-ac", "hbac", ffOPTWR },
2629 { efXVG, "-dist", "hbdist", ffOPTWR },
2630 { efXVG, "-ang", "hbang", ffOPTWR },
2631 { efXVG, "-hx", "hbhelix", ffOPTWR },
2632 { efNDX, "-hbn", "hbond", ffOPTWR },
2633 { efXPM, "-hbm", "hbmap", ffOPTWR },
2634 { efXVG, "-don", "donor", ffOPTWR },
2635 { efXVG, "-dan", "danum", ffOPTWR },
2636 { efXVG, "-life", "hblife", ffOPTWR },
2637 { efXVG, "-nhbdist", "nhbdist", ffOPTWR }
2640 #define NFILE asize(fnm)
2642 char hbmap[HB_NR] = { ' ', 'o', '-', '*' };
2643 const char* hbdesc[HB_NR] = { "None", "Present", "Inserted", "Present & Inserted" };
2644 t_rgb hbrgb[HB_NR] = { { 1, 1, 1 }, { 1, 0, 0 }, { 0, 0, 1 }, { 1, 0, 1 } };
2646 t_trxstatus* status;
2647 bool trrStatus = true;
2650 int npargs, natoms, nframes = 0, shatom;
2656 real t, ccut, dist = 0.0, ang = 0.0;
2657 double max_nhb, aver_nhb, aver_dist;
2658 int h = 0, i = 0, j, k = 0, ogrp, nsel;
2659 int xi = 0, yi, zi, ai;
2660 int xj, yj, zj, aj, xjj, yjj, zjj;
2661 gmx_bool bSelected, bHBmap, bStop, bTwo, bBox, bTric;
2662 int * adist, *rdist;
2663 int grp, nabin, nrbin, resdist, ihb;
2666 FILE * fp, *fpnhb = nullptr, *donor_properties = nullptr;
2668 t_ncell * icell, *jcell;
2670 unsigned char* datable;
2671 gmx_output_env_t* oenv;
2672 int ii, hh, actual_nThreads;
2675 gmx_bool bEdge_yjj, bEdge_xjj;
2677 t_hbdata** p_hb = nullptr; /* one per thread, then merge after the frame loop */
2678 int ** p_adist = nullptr, **p_rdist = nullptr; /* a histogram for each thread. */
2680 const bool bOMP = GMX_OPENMP;
2683 ppa = add_acf_pargs(&npargs, pa);
2685 if (!parse_common_args(
2686 &argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT, NFILE, fnm, npargs, ppa, asize(desc), desc, asize(bugs), bugs, &oenv))
2694 ccut = std::cos(acut * gmx::c_deg2Rad);
2700 gmx_fatal(FARGS, "Can not analyze selected contacts.");
2704 gmx_fatal(FARGS, "Can not analyze contact between H and A: turn off -noda");
2708 /* Initiate main data structure! */
2709 bHBmap = (opt2bSet("-ac", NFILE, fnm) || opt2bSet("-life", NFILE, fnm)
2710 || opt2bSet("-hbn", NFILE, fnm) || opt2bSet("-hbm", NFILE, fnm));
2712 if (opt2bSet("-nhbdist", NFILE, fnm))
2714 const char* leg[MAXHH + 1] = { "0 HBs", "1 HB", "2 HBs", "3 HBs", "Total" };
2715 fpnhb = xvgropen(opt2fn("-nhbdist", NFILE, fnm),
2716 "Number of donor-H with N HBs",
2717 output_env_get_xvgr_tlabel(oenv),
2720 xvgr_legend(fpnhb, asize(leg), leg, oenv);
2723 hb = mk_hbdata(bHBmap, opt2bSet("-dan", NFILE, fnm), bMerge || bContact);
2726 t_inputrec irInstance;
2727 t_inputrec* ir = &irInstance;
2728 read_tpx_top(ftp2fn(efTPR, NFILE, fnm), ir, box, &natoms, nullptr, nullptr, &top);
2730 snew(grpnames, grNR);
2733 /* Make Donor-Acceptor table */
2734 snew(datable, top.atoms.nr);
2738 /* analyze selected hydrogen bonds */
2739 printf("Select group with selected atoms:\n");
2740 get_index(&(top.atoms), opt2fn("-sel", NFILE, fnm), 1, &nsel, index, grpnames);
2744 "Number of atoms in group '%s' not a multiple of 3\n"
2745 "and therefore cannot contain triplets of "
2746 "Donor-Hydrogen-Acceptor",
2751 for (i = 0; (i < nsel); i += 3)
2753 int dd = index[0][i];
2754 int aa = index[0][i + 2];
2755 /* int */ hh = index[0][i + 1];
2756 add_dh(&hb->d, dd, hh, i, datable);
2757 add_acc(&hb->a, aa, i);
2758 /* Should this be here ? */
2759 snew(hb->d.dptr, top.atoms.nr);
2760 snew(hb->a.aptr, top.atoms.nr);
2761 add_hbond(hb, dd, aa, hh, gr0, gr0, 0, bMerge, 0, bContact);
2763 printf("Analyzing %d selected hydrogen bonds from '%s'\n", isize[0], grpnames[0]);
2767 /* analyze all hydrogen bonds: get group(s) */
2768 printf("Specify 2 groups to analyze:\n");
2769 get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm), 2, isize, index, grpnames);
2771 /* check if we have two identical or two non-overlapping groups */
2772 bTwo = isize[0] != isize[1];
2773 for (i = 0; (i < isize[0]) && !bTwo; i++)
2775 bTwo = index[0][i] != index[1][i];
2779 printf("Checking for overlap in atoms between %s and %s\n", grpnames[0], grpnames[1]);
2781 gen_datable(index[0], isize[0], datable, top.atoms.nr);
2783 for (i = 0; i < isize[1]; i++)
2785 if (ISINGRP(datable[index[1][i]]))
2787 gmx_fatal(FARGS, "Partial overlap between groups '%s' and '%s'", grpnames[0], grpnames[1]);
2793 printf("Calculating %s "
2794 "between %s (%d atoms) and %s (%d atoms)\n",
2795 bContact ? "contacts" : "hydrogen bonds",
2804 "Calculating %s in %s (%d atoms)\n",
2805 bContact ? "contacts" : "hydrogen bonds",
2812 /* search donors and acceptors in groups */
2813 snew(datable, top.atoms.nr);
2814 for (i = 0; (i < grNR); i++)
2816 if (((i == gr0) && !bSelected) || ((i == gr1) && bTwo))
2818 gen_datable(index[i], isize[i], datable, top.atoms.nr);
2822 &top, isize[i], index[i], &hb->a, i, bNitAcc, TRUE, (bTwo && (i == gr0)) || !bTwo, datable);
2823 search_donors(&top, isize[i], index[i], &hb->d, i, TRUE, (bTwo && (i == gr1)) || !bTwo, datable);
2827 search_acceptors(&top, isize[i], index[i], &hb->a, i, bNitAcc, FALSE, TRUE, datable);
2828 search_donors(&top, isize[i], index[i], &hb->d, i, FALSE, TRUE, datable);
2832 clear_datable_grp(datable, top.atoms.nr);
2837 printf("Found %d donors and %d acceptors\n", hb->d.nrd, hb->a.nra);
2839 snew(donors[gr0D], dons[gr0D].nrd);*/
2841 donor_properties = open_donor_properties_file(opt2fn_null("-don", NFILE, fnm), hb, oenv);
2845 printf("Making hbmap structure...");
2846 /* Generate hbond data structure */
2853 if (hb->d.nrd + hb->a.nra == 0)
2855 printf("No Donors or Acceptors found\n");
2862 printf("No Donors found\n");
2867 printf("No Acceptors found\n");
2873 gmx_fatal(FARGS, "Nothing to be done");
2882 /* get index group with atom for shell */
2885 printf("Select atom for shell (1 atom):\n");
2886 get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm), 1, &shisz, &shidx, &shgrpnm);
2889 printf("group contains %d atoms, should be 1 (one)\n", shisz);
2891 } while (shisz != 1);
2893 printf("Will calculate hydrogen bonds within a shell "
2894 "of %g nm around atom %i\n",
2899 /* Analyze trajectory */
2900 natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
2901 if (natoms > top.atoms.nr)
2903 gmx_fatal(FARGS, "Topology (%d atoms) does not match trajectory (%d atoms)", top.atoms.nr, natoms);
2906 bBox = (ir->pbcType != PbcType::No);
2907 grid = init_grid(bBox, box, (rcut > r2cut) ? rcut : r2cut, ngrid);
2908 nabin = static_cast<int>(acut / abin);
2909 nrbin = static_cast<int>(rcut / rbin);
2910 snew(adist, nabin + 1);
2911 snew(rdist, nrbin + 1);
2914 # define __ADIST adist // NOLINT(bugprone-reserved-identifier)
2915 # define __RDIST rdist // NOLINT(bugprone-reserved-identifier)
2916 # define __HBDATA hb // NOLINT(bugprone-reserved-identifier)
2917 #else /* GMX_OPENMP ================================================== \
2918 * Set up the OpenMP stuff, | \
2919 * like the number of threads and such | \
2920 * Also start the parallel loop. | \
2922 # define __ADIST p_adist[threadNr] // NOLINT(bugprone-reserved-identifier)
2923 # define __RDIST p_rdist[threadNr] // NOLINT(bugprone-reserved-identifier)
2924 # define __HBDATA p_hb[threadNr] // NOLINT(bugprone-reserved-identifier)
2928 bParallel = !bSelected;
2932 actual_nThreads = std::min((nThreads <= 0) ? INT_MAX : nThreads, gmx_omp_get_max_threads());
2934 gmx_omp_set_num_threads(actual_nThreads);
2935 printf("Frame loop parallelized with OpenMP using %i threads.\n", actual_nThreads);
2940 actual_nThreads = 1;
2943 snew(p_hb, actual_nThreads);
2944 snew(p_adist, actual_nThreads);
2945 snew(p_rdist, actual_nThreads);
2946 for (i = 0; i < actual_nThreads; i++)
2949 snew(p_adist[i], nabin + 1);
2950 snew(p_rdist[i], nrbin + 1);
2952 p_hb[i]->max_frames = 0;
2953 p_hb[i]->nhb = nullptr;
2954 p_hb[i]->ndist = nullptr;
2955 p_hb[i]->n_bound = nullptr;
2956 p_hb[i]->time = nullptr;
2957 p_hb[i]->nhx = nullptr;
2959 p_hb[i]->bHBmap = hb->bHBmap;
2960 p_hb[i]->bDAnr = hb->bDAnr;
2961 p_hb[i]->wordlen = hb->wordlen;
2962 p_hb[i]->nframes = hb->nframes;
2963 p_hb[i]->maxhydro = hb->maxhydro;
2964 p_hb[i]->danr = hb->danr;
2967 p_hb[i]->hbmap = hb->hbmap;
2968 p_hb[i]->time = hb->time; /* This may need re-syncing at every frame. */
2971 p_hb[i]->nrdist = 0;
2975 /* Make a thread pool here,
2976 * instead of forking anew at every frame. */
2978 #pragma omp parallel firstprivate(i) private(j, \
3005 bEdge_yjj) default(shared)
3006 { /* Start of parallel region */
3009 threadNr = gmx_omp_get_thread_num();
3014 bTric = bBox && TRICLINIC(box);
3020 sync_hbdata(p_hb[threadNr], nframes);
3022 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
3028 build_grid(hb, x, x[shatom], bBox, box, hbox, (rcut > r2cut) ? rcut : r2cut, rshell, ngrid, grid);
3029 reset_nhbonds(&(hb->d));
3031 if (debug && bDebug)
3033 dump_grid(debug, ngrid, grid);
3036 add_frames(hb, nframes);
3037 init_hbframe(hb, nframes, output_env_conv_time(oenv, t));
3041 count_da_grid(ngrid, grid, hb->danr[nframes]);
3044 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
3049 p_hb[threadNr]->time = hb->time; /* This pointer may have changed. */
3059 /* Do not parallelize this just yet. */
3061 for (ii = 0; (ii < nsel); ii++)
3063 int dd = index[0][i];
3064 int aa = index[0][i + 2];
3065 /* int */ hh = index[0][i + 1];
3067 hb, ii, ii, dd, aa, rcut, r2cut, ccut, x, bBox, box, hbox, &dist, &ang, bDA, &h, bContact, bMerge);
3071 /* add to index if not already there */
3073 add_hbond(hb, dd, aa, hh, ii, ii, nframes, bMerge, ihb, bContact);
3077 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
3079 } /* if (bSelected) */
3082 /* The outer grid loop will have to do for now. */
3083 #pragma omp for schedule(dynamic)
3084 for (xi = 0; xi < ngrid[XX]; xi++)
3088 for (yi = 0; (yi < ngrid[YY]); yi++)
3090 for (zi = 0; (zi < ngrid[ZZ]); zi++)
3093 /* loop over donor groups gr0 (always) and gr1 (if necessary) */
3094 for (grp = gr0; (grp <= (bTwo ? gr1 : gr0)); grp++)
3096 icell = &(grid[zi][yi][xi].d[grp]);
3107 /* loop over all hydrogen atoms from group (grp)
3108 * in this gridcell (icell)
3110 for (ai = 0; (ai < icell->nr); ai++)
3112 i = icell->atoms[ai];
3114 /* loop over all adjacent gridcells (xj,yj,zj) */
3115 for (zjj = grid_loop_begin(ngrid[ZZ], zi, bTric, FALSE);
3116 zjj <= grid_loop_end(ngrid[ZZ], zi, bTric, FALSE);
3119 zj = grid_mod(zjj, ngrid[ZZ]);
3120 bEdge_yjj = (zj == 0) || (zj == ngrid[ZZ] - 1);
3121 for (yjj = grid_loop_begin(ngrid[YY], yi, bTric, bEdge_yjj);
3122 yjj <= grid_loop_end(ngrid[YY], yi, bTric, bEdge_yjj);
3125 yj = grid_mod(yjj, ngrid[YY]);
3126 bEdge_xjj = (yj == 0) || (yj == ngrid[YY] - 1)
3127 || (zj == 0) || (zj == ngrid[ZZ] - 1);
3128 for (xjj = grid_loop_begin(ngrid[XX], xi, bTric, bEdge_xjj);
3129 xjj <= grid_loop_end(ngrid[XX], xi, bTric, bEdge_xjj);
3132 xj = grid_mod(xjj, ngrid[XX]);
3133 jcell = &(grid[zj][yj][xj].a[ogrp]);
3134 /* loop over acceptor atoms from other group
3135 * (ogrp) in this adjacent gridcell (jcell)
3137 for (aj = 0; (aj < jcell->nr); aj++)
3139 j = jcell->atoms[aj];
3141 /* check if this once was a h-bond */
3142 ihb = is_hbond(__HBDATA,
3163 /* add to index if not already there */
3165 add_hbond(__HBDATA, i, j, h, grp, ogrp, nframes, bMerge, ihb, bContact);
3167 /* make angle and distance distributions */
3168 if (ihb == hbHB && !bContact)
3174 "distance is higher "
3175 "than what is allowed "
3179 ang *= gmx::c_rad2Deg;
3180 __ADIST[static_cast<int>(ang / abin)]++;
3181 __RDIST[static_cast<int>(dist / rbin)]++;
3184 if (donor_index(&hb->d, grp, i) == NOTSET)
3191 if (acceptor_index(&hb->a, ogrp, j)
3200 top.atoms.atom[i].resind
3201 - top.atoms.atom[j].resind);
3202 if (resdist >= max_hx)
3204 resdist = max_hx - 1;
3206 __HBDATA->nhx[nframes][resdist]++;
3216 } /* for xi,yi,zi */
3219 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
3221 } /* if (bSelected) {...} else */
3224 /* Better wait for all threads to finnish using x[] before updating it. */
3227 #pragma omp critical
3231 /* Sum up histograms and counts from p_hb[] into hb */
3234 hb->nhb[k] += p_hb[threadNr]->nhb[k];
3235 hb->ndist[k] += p_hb[threadNr]->ndist[k];
3236 for (j = 0; j < max_hx; j++)
3238 hb->nhx[k][j] += p_hb[threadNr]->nhx[k][j];
3242 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
3245 /* Here are a handful of single constructs
3246 * to share the workload a bit. The most
3247 * important one is of course the last one,
3248 * where there's a potential bottleneck in form
3255 analyse_donor_properties(donor_properties, hb, k, t);
3257 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
3266 do_nhb_dist(fpnhb, hb, t);
3269 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
3276 trrStatus = (read_next_x(oenv, status, &t, x, box));
3279 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
3283 } while (trrStatus);
3287 #pragma omp critical
3289 hb->nrhb += p_hb[threadNr]->nrhb;
3290 hb->nrdist += p_hb[threadNr]->nrdist;
3293 /* Free parallel datastructures */
3294 sfree(p_hb[threadNr]->nhb);
3295 sfree(p_hb[threadNr]->ndist);
3296 sfree(p_hb[threadNr]->nhx);
3299 for (i = 0; i < nabin; i++)
3303 for (j = 0; j < actual_nThreads; j++)
3306 adist[i] += p_adist[j][i];
3309 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
3312 for (i = 0; i <= nrbin; i++)
3316 for (j = 0; j < actual_nThreads; j++)
3318 rdist[i] += p_rdist[j][i];
3321 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
3324 sfree(p_adist[threadNr]);
3325 sfree(p_rdist[threadNr]);
3327 } /* End of parallel region */
3334 if (nframes < 2 && (opt2bSet("-ac", NFILE, fnm) || opt2bSet("-life", NFILE, fnm)))
3336 gmx_fatal(FARGS, "Cannot calculate autocorrelation of life times with less than two frames");
3339 free_grid(ngrid, &grid);
3343 if (donor_properties)
3345 xvgrclose(donor_properties);
3353 /* Compute maximum possible number of different hbonds */
3360 max_nhb = 0.5 * (hb->d.nrd * hb->a.nra);
3367 printf("No %s found!!\n", bContact ? "contacts" : "hydrogen bonds");
3371 printf("Found %d different %s in trajectory\n"
3372 "Found %d different atom-pairs within %s distance\n",
3374 bContact ? "contacts" : "hydrogen bonds",
3376 (r2cut > 0) ? "second cut-off" : "hydrogen bonding");
3380 merge_hb(hb, bTwo, bContact);
3383 if (opt2bSet("-hbn", NFILE, fnm))
3385 dump_hbmap(hb, NFILE, fnm, bTwo, bContact, isize, index, grpnames, &top.atoms);
3388 /* Moved the call to merge_hb() to a line BEFORE dump_hbmap
3389 * to make the -hbn and -hmb output match eachother.
3390 * - Erik Marklund, May 30, 2006 */
3393 /* Print out number of hbonds and distances */
3396 fp = xvgropen(opt2fn("-num", NFILE, fnm),
3397 bContact ? "Contacts" : "Hydrogen Bonds",
3398 output_env_get_xvgr_tlabel(oenv),
3402 snew(leg[0], STRLEN);
3403 snew(leg[1], STRLEN);
3404 sprintf(leg[0], "%s", bContact ? "Contacts" : "Hydrogen bonds");
3405 sprintf(leg[1], "Pairs within %g nm", (r2cut > 0) ? r2cut : rcut);
3406 xvgr_legend(fp, 2, leg, oenv);
3410 for (i = 0; (i < nframes); i++)
3412 fprintf(fp, "%10g %10d %10d\n", hb->time[i], hb->nhb[i], hb->ndist[i]);
3413 aver_nhb += hb->nhb[i];
3414 aver_dist += hb->ndist[i];
3417 aver_nhb /= nframes;
3418 aver_dist /= nframes;
3419 /* Print HB distance distribution */
3420 if (opt2bSet("-dist", NFILE, fnm))
3425 for (i = 0; i < nrbin; i++)
3430 fp = xvgropen(opt2fn("-dist", NFILE, fnm),
3431 "Hydrogen Bond Distribution",
3432 bDA ? "Donor - Acceptor Distance (nm)" : "Hydrogen - Acceptor Distance (nm)",
3435 for (i = 0; i < nrbin; i++)
3437 fprintf(fp, "%10g %10g\n", (i + 0.5) * rbin, rdist[i] / (rbin * sum));
3442 /* Print HB angle distribution */
3443 if (opt2bSet("-ang", NFILE, fnm))
3448 for (i = 0; i < nabin; i++)
3453 fp = xvgropen(opt2fn("-ang", NFILE, fnm),
3454 "Hydrogen Bond Distribution",
3455 "Hydrogen - Donor - Acceptor Angle (\\SO\\N)",
3458 for (i = 0; i < nabin; i++)
3460 fprintf(fp, "%10g %10g\n", (i + 0.5) * abin, adist[i] / (abin * sum));
3465 /* Print HB in alpha-helix */
3466 if (opt2bSet("-hx", NFILE, fnm))
3468 fp = xvgropen(opt2fn("-hx", NFILE, fnm),
3470 output_env_get_xvgr_tlabel(oenv),
3473 xvgr_legend(fp, NRHXTYPES, hxtypenames, oenv);
3474 for (i = 0; i < nframes; i++)
3476 fprintf(fp, "%10g", hb->time[i]);
3477 for (j = 0; j < max_hx; j++)
3479 fprintf(fp, " %6d", hb->nhx[i][j]);
3486 printf("Average number of %s per timeframe %.3f out of %g possible\n",
3487 bContact ? "contacts" : "hbonds",
3488 bContact ? aver_dist : aver_nhb,
3491 /* Do Autocorrelation etc. */
3495 Added support for -contact in ac and hbm calculations below.
3496 - Erik Marklund, May 29, 2006
3498 if (opt2bSet("-ac", NFILE, fnm) || opt2bSet("-life", NFILE, fnm))
3500 please_cite(stdout, "Spoel2006b");
3502 if (opt2bSet("-ac", NFILE, fnm))
3504 do_hbac(opt2fn("-ac", NFILE, fnm), hb, nDump, bMerge, bContact, fit_start, temp, r2cut > 0, oenv, nThreads);
3506 if (opt2bSet("-life", NFILE, fnm))
3508 do_hblife(opt2fn("-life", NFILE, fnm), hb, bMerge, bContact, oenv);
3510 if (opt2bSet("-hbm", NFILE, fnm))
3513 int id, ia, hh, x, y;
3516 if ((nframes > 0) && (hb->nrhb > 0))
3521 mat.matrix.resize(mat.nx, mat.ny);
3522 mat.axis_x.resize(mat.nx);
3523 for (auto& value : mat.matrix.toArrayRef())
3528 for (id = 0; (id < hb->d.nrd); id++)
3530 for (ia = 0; (ia < hb->a.nra); ia++)
3532 for (hh = 0; (hh < hb->maxhydro); hh++)
3534 if (hb->hbmap[id][ia])
3536 if (ISHB(hb->hbmap[id][ia]->history[hh]))
3538 for (x = 0; (x <= hb->hbmap[id][ia]->nframes); x++)
3540 int nn0 = hb->hbmap[id][ia]->n0;
3541 range_check(y, 0, mat.ny);
3542 mat.matrix(x + nn0, y) = static_cast<t_matelmt>(
3543 is_hb(hb->hbmap[id][ia]->h[hh], x));
3551 std::copy(hb->time, hb->time + mat.nx, mat.axis_x.begin());
3552 mat.axis_y.resize(mat.ny);
3553 std::iota(mat.axis_y.begin(), mat.axis_y.end(), 0);
3554 mat.title = (bContact ? "Contact Existence Map" : "Hydrogen Bond Existence Map");
3555 mat.legend = bContact ? "Contacts" : "Hydrogen Bonds";
3556 mat.label_x = output_env_get_xvgr_tlabel(oenv);
3557 mat.label_y = bContact ? "Contact Index" : "Hydrogen Bond Index";
3558 mat.bDiscrete = true;
3562 for (auto& m : mat.map)
3564 m.code.c1 = hbmap[i];
3570 fp = opt2FILE("-hbm", NFILE, fnm, "w");
3571 write_xpm_m(fp, mat);
3577 "No hydrogen bonds/contacts found. No hydrogen bond map will be "
3589 #define USE_THIS_GROUP(j) (((j) == gr0) || (bTwo && ((j) == gr1)))
3591 fp = xvgropen(opt2fn("-dan", NFILE, fnm),
3592 "Donors and Acceptors",
3593 output_env_get_xvgr_tlabel(oenv),
3596 nleg = (bTwo ? 2 : 1) * 2;
3597 snew(legnames, nleg);
3599 for (j = 0; j < grNR; j++)
3601 if (USE_THIS_GROUP(j))
3603 sprintf(buf, "Donors %s", grpnames[j]);
3604 legnames[i++] = gmx_strdup(buf);
3605 sprintf(buf, "Acceptors %s", grpnames[j]);
3606 legnames[i++] = gmx_strdup(buf);
3611 gmx_incons("number of legend entries");
3613 xvgr_legend(fp, nleg, legnames, oenv);
3614 for (i = 0; i < nframes; i++)
3616 fprintf(fp, "%10g", hb->time[i]);
3617 for (j = 0; (j < grNR); j++)
3619 if (USE_THIS_GROUP(j))
3621 fprintf(fp, " %6d", hb->danr[i][j]);