Remove unused function generate_excls and make clean_excls static
[gromacs.git] / src / gromacs / gmxana / gmx_hbond.cpp
blobf4dc3d3701e4c978b067362b1ec5ee5389ca5f0e
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-2008, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
37 #include "gmxpre.h"
39 #include "config.h"
41 #include <cmath>
42 #include <cstdlib>
43 #include <cstring>
45 #include <algorithm>
46 #include <numeric>
48 #include "gromacs/commandline/pargs.h"
49 #include "gromacs/commandline/viewit.h"
50 #include "gromacs/correlationfunctions/autocorr.h"
51 #include "gromacs/correlationfunctions/crosscorr.h"
52 #include "gromacs/correlationfunctions/expfit.h"
53 #include "gromacs/correlationfunctions/integrate.h"
54 #include "gromacs/fileio/matio.h"
55 #include "gromacs/fileio/tpxio.h"
56 #include "gromacs/fileio/trxio.h"
57 #include "gromacs/fileio/xvgr.h"
58 #include "gromacs/gmxana/gmx_ana.h"
59 #include "gromacs/gmxana/gstat.h"
60 #include "gromacs/math/functions.h"
61 #include "gromacs/math/units.h"
62 #include "gromacs/math/vec.h"
63 #include "gromacs/mdtypes/inputrec.h"
64 #include "gromacs/pbcutil/pbc.h"
65 #include "gromacs/topology/ifunc.h"
66 #include "gromacs/topology/index.h"
67 #include "gromacs/topology/topology.h"
68 #include "gromacs/utility/arraysize.h"
69 #include "gromacs/utility/cstringutil.h"
70 #include "gromacs/utility/exceptions.h"
71 #include "gromacs/utility/fatalerror.h"
72 #include "gromacs/utility/futil.h"
73 #include "gromacs/utility/gmxomp.h"
74 #include "gromacs/utility/pleasecite.h"
75 #include "gromacs/utility/programcontext.h"
76 #include "gromacs/utility/smalloc.h"
77 #include "gromacs/utility/snprintf.h"
79 #define max_hx 7
80 typedef int t_hx[max_hx];
81 #define NRHXTYPES max_hx
82 static const char *hxtypenames[NRHXTYPES] =
83 {"n-n", "n-n+1", "n-n+2", "n-n+3", "n-n+4", "n-n+5", "n-n>6"};
84 #define MAXHH 4
86 static const int NOTSET = -49297;
88 /* -----------------------------------------*/
90 enum {
91 gr0, gr1, grI, grNR
93 enum {
94 hbNo, hbDist, hbHB, hbNR, hbR2
96 static const unsigned char c_acceptorMask = (1 << 0);
97 static const unsigned char c_donorMask = (1 << 1);
98 static const unsigned char c_inGroupMask = (1 << 2);
101 static const char *grpnames[grNR] = {"0", "1", "I" };
103 static gmx_bool bDebug = FALSE;
105 #define HB_NO 0
106 #define HB_YES 1<<0
107 #define HB_INS 1<<1
108 #define HB_YESINS (HB_YES|HB_INS)
109 #define HB_NR (1<<2)
110 #define MAXHYDRO 4
112 #define ISHB(h) ((h) & 2)
113 #define ISDIST(h) ((h) & 1)
114 #define ISDIST2(h) ((h) & 4)
115 #define ISACC(h) ((h) & c_acceptorMask)
116 #define ISDON(h) ((h) & c_donorMask)
117 #define ISINGRP(h) ((h) & c_inGroupMask)
119 typedef struct {
120 int nr;
121 int maxnr;
122 int *atoms;
123 } t_ncell;
125 typedef struct {
126 t_ncell d[grNR];
127 t_ncell a[grNR];
128 } t_gridcell;
130 typedef int t_icell[grNR];
131 typedef int h_id[MAXHYDRO];
133 typedef struct {
134 int history[MAXHYDRO];
135 /* Has this hbond existed ever? If so as hbDist or hbHB or both.
136 * Result is stored as a bitmap (1 = hbDist) || (2 = hbHB)
138 /* Bitmask array which tells whether a hbond is present
139 * at a given time. Either of these may be NULL
141 int n0; /* First frame a HB was found */
142 int nframes, maxframes; /* Amount of frames in this hbond */
143 unsigned int **h;
144 unsigned int **g;
145 /* See Xu and Berne, JPCB 105 (2001), p. 11929. We define the
146 * function g(t) = [1-h(t)] H(t) where H(t) is one when the donor-
147 * acceptor distance is less than the user-specified distance (typically
148 * 0.35 nm).
150 } t_hbond;
152 typedef struct {
153 int nra, max_nra;
154 int *acc; /* Atom numbers of the acceptors */
155 int *grp; /* Group index */
156 int *aptr; /* Map atom number to acceptor index */
157 } t_acceptors;
159 typedef struct {
160 int nrd, max_nrd;
161 int *don; /* Atom numbers of the donors */
162 int *grp; /* Group index */
163 int *dptr; /* Map atom number to donor index */
164 int *nhydro; /* Number of hydrogens for each donor */
165 h_id *hydro; /* The atom numbers of the hydrogens */
166 h_id *nhbonds; /* The number of HBs per H at current */
167 } t_donors;
169 typedef struct {
170 gmx_bool bHBmap, bDAnr;
171 int wordlen;
172 /* The following arrays are nframes long */
173 int nframes, max_frames, maxhydro;
174 int *nhb, *ndist;
175 h_id *n_bound;
176 real *time;
177 t_icell *danr;
178 t_hx *nhx;
179 /* These structures are initialized from the topology at start up */
180 t_donors d;
181 t_acceptors a;
182 /* This holds a matrix with all possible hydrogen bonds */
183 int nrhb, nrdist;
184 t_hbond ***hbmap;
185 } t_hbdata;
187 /* Changed argument 'bMerge' into 'oneHB' below,
188 * since -contact should cause maxhydro to be 1,
189 * not just -merge.
190 * - Erik Marklund May 29, 2006
193 static t_hbdata *mk_hbdata(gmx_bool bHBmap, gmx_bool bDAnr, gmx_bool oneHB)
195 t_hbdata *hb;
197 snew(hb, 1);
198 hb->wordlen = 8*sizeof(unsigned int);
199 hb->bHBmap = bHBmap;
200 hb->bDAnr = bDAnr;
201 if (oneHB)
203 hb->maxhydro = 1;
205 else
207 hb->maxhydro = MAXHYDRO;
209 return hb;
212 static void mk_hbmap(t_hbdata *hb)
214 int i, j;
216 snew(hb->hbmap, hb->d.nrd);
217 for (i = 0; (i < hb->d.nrd); i++)
219 snew(hb->hbmap[i], hb->a.nra);
220 if (hb->hbmap[i] == nullptr)
222 gmx_fatal(FARGS, "Could not allocate enough memory for hbmap");
224 for (j = 0; j < hb->a.nra; j++)
226 hb->hbmap[i][j] = nullptr;
231 static void add_frames(t_hbdata *hb, int nframes)
233 if (nframes >= hb->max_frames)
235 hb->max_frames += 4096;
236 srenew(hb->time, hb->max_frames);
237 srenew(hb->nhb, hb->max_frames);
238 srenew(hb->ndist, hb->max_frames);
239 srenew(hb->n_bound, hb->max_frames);
240 srenew(hb->nhx, hb->max_frames);
241 if (hb->bDAnr)
243 srenew(hb->danr, hb->max_frames);
246 hb->nframes = nframes;
249 #define OFFSET(frame) ((frame) / 32)
250 #define MASK(frame) (1 << ((frame) % 32))
252 static void _set_hb(unsigned int hbexist[], unsigned int frame, gmx_bool bValue)
254 if (bValue)
256 hbexist[OFFSET(frame)] |= MASK(frame);
258 else
260 hbexist[OFFSET(frame)] &= ~MASK(frame);
264 static gmx_bool is_hb(const unsigned int hbexist[], int frame)
266 return (hbexist[OFFSET(frame)] & MASK(frame)) != 0;
269 static void set_hb(t_hbdata *hb, int id, int ih, int ia, int frame, int ihb)
271 unsigned int *ghptr = nullptr;
273 if (ihb == hbHB)
275 ghptr = hb->hbmap[id][ia]->h[ih];
277 else if (ihb == hbDist)
279 ghptr = hb->hbmap[id][ia]->g[ih];
281 else
283 gmx_fatal(FARGS, "Incomprehensible iValue %d in set_hb", ihb);
286 _set_hb(ghptr, frame-hb->hbmap[id][ia]->n0, TRUE);
289 static void add_ff(t_hbdata *hbd, int id, int h, int ia, int frame, int ihb)
291 int i, j, n;
292 t_hbond *hb = hbd->hbmap[id][ia];
293 int maxhydro = std::min(hbd->maxhydro, hbd->d.nhydro[id]);
294 int wlen = hbd->wordlen;
295 int delta = 32*wlen;
297 if (!hb->h[0])
299 hb->n0 = frame;
300 hb->maxframes = delta;
301 for (i = 0; (i < maxhydro); i++)
303 snew(hb->h[i], hb->maxframes/wlen);
304 snew(hb->g[i], hb->maxframes/wlen);
307 else
309 hb->nframes = frame-hb->n0;
310 /* We need a while loop here because hbonds may be returning
311 * after a long time.
313 while (hb->nframes >= hb->maxframes)
315 n = hb->maxframes + delta;
316 for (i = 0; (i < maxhydro); i++)
318 srenew(hb->h[i], n/wlen);
319 srenew(hb->g[i], n/wlen);
320 for (j = hb->maxframes/wlen; (j < n/wlen); j++)
322 hb->h[i][j] = 0;
323 hb->g[i][j] = 0;
327 hb->maxframes = n;
330 if (frame >= 0)
332 set_hb(hbd, id, h, ia, frame, ihb);
337 static void inc_nhbonds(t_donors *ddd, int d, int h)
339 int j;
340 int dptr = ddd->dptr[d];
342 for (j = 0; (j < ddd->nhydro[dptr]); j++)
344 if (ddd->hydro[dptr][j] == h)
346 ddd->nhbonds[dptr][j]++;
347 break;
350 if (j == ddd->nhydro[dptr])
352 gmx_fatal(FARGS, "No such hydrogen %d on donor %d\n", h+1, d+1);
356 static int _acceptor_index(t_acceptors *a, int grp, int i,
357 const char *file, int line)
359 int ai = a->aptr[i];
361 if (a->grp[ai] != grp)
363 if (debug && bDebug)
365 fprintf(debug, "Acc. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
366 ai, a->grp[ai], grp, file, line);
368 return NOTSET;
370 else
372 return ai;
375 #define acceptor_index(a, grp, i) _acceptor_index(a, grp, i, __FILE__, __LINE__)
377 static int _donor_index(t_donors *d, int grp, int i, const char *file, int line)
379 int di = d->dptr[i];
381 if (di == NOTSET)
383 return NOTSET;
386 if (d->grp[di] != grp)
388 if (debug && bDebug)
390 fprintf(debug, "Don. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
391 di, d->grp[di], grp, file, line);
393 return NOTSET;
395 else
397 return di;
400 #define donor_index(d, grp, i) _donor_index(d, grp, i, __FILE__, __LINE__)
402 static gmx_bool isInterchangable(t_hbdata *hb, int d, int a, int grpa, int grpd)
404 /* g_hbond doesn't allow overlapping groups */
405 if (grpa != grpd)
407 return FALSE;
409 return
410 donor_index(&hb->d, grpd, a) != NOTSET
411 && acceptor_index(&hb->a, grpa, d) != NOTSET;
415 static void add_hbond(t_hbdata *hb, int d, int a, int h, int grpd, int grpa,
416 int frame, gmx_bool bMerge, int ihb, gmx_bool bContact)
418 int k, id, ia, hh;
419 gmx_bool daSwap = FALSE;
421 if ((id = hb->d.dptr[d]) == NOTSET)
423 gmx_fatal(FARGS, "No donor atom %d", d+1);
425 else if (grpd != hb->d.grp[id])
427 gmx_fatal(FARGS, "Inconsistent donor groups, %d instead of %d, atom %d",
428 grpd, hb->d.grp[id], d+1);
430 if ((ia = hb->a.aptr[a]) == NOTSET)
432 gmx_fatal(FARGS, "No acceptor atom %d", a+1);
434 else if (grpa != hb->a.grp[ia])
436 gmx_fatal(FARGS, "Inconsistent acceptor groups, %d instead of %d, atom %d",
437 grpa, hb->a.grp[ia], a+1);
440 if (bMerge)
443 if (isInterchangable(hb, d, a, grpd, grpa) && d > a)
444 /* Then swap identity so that the id of d is lower then that of a.
446 * This should really be redundant by now, as is_hbond() now ought to return
447 * hbNo in the cases where this conditional is TRUE. */
449 daSwap = TRUE;
450 k = d;
451 d = a;
452 a = k;
454 /* Now repeat donor/acc check. */
455 if ((id = hb->d.dptr[d]) == NOTSET)
457 gmx_fatal(FARGS, "No donor atom %d", d+1);
459 else if (grpd != hb->d.grp[id])
461 gmx_fatal(FARGS, "Inconsistent donor groups, %d instead of %d, atom %d",
462 grpd, hb->d.grp[id], d+1);
464 if ((ia = hb->a.aptr[a]) == NOTSET)
466 gmx_fatal(FARGS, "No acceptor atom %d", a+1);
468 else if (grpa != hb->a.grp[ia])
470 gmx_fatal(FARGS, "Inconsistent acceptor groups, %d instead of %d, atom %d",
471 grpa, hb->a.grp[ia], a+1);
476 if (hb->hbmap)
478 /* Loop over hydrogens to find which hydrogen is in this particular HB */
479 if ((ihb == hbHB) && !bMerge && !bContact)
481 for (k = 0; (k < hb->d.nhydro[id]); k++)
483 if (hb->d.hydro[id][k] == h)
485 break;
488 if (k == hb->d.nhydro[id])
490 gmx_fatal(FARGS, "Donor %d does not have hydrogen %d (a = %d)",
491 d+1, h+1, a+1);
494 else
496 k = 0;
499 if (hb->bHBmap)
502 #pragma omp critical
506 if (hb->hbmap[id][ia] == nullptr)
508 snew(hb->hbmap[id][ia], 1);
509 snew(hb->hbmap[id][ia]->h, hb->maxhydro);
510 snew(hb->hbmap[id][ia]->g, hb->maxhydro);
512 add_ff(hb, id, k, ia, frame, ihb);
514 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
518 /* Strange construction with frame >=0 is a relic from old code
519 * for selected hbond analysis. It may be necessary again if that
520 * is made to work again.
522 if (frame >= 0)
524 hh = hb->hbmap[id][ia]->history[k];
525 if (ihb == hbHB)
527 hb->nhb[frame]++;
528 if (!(ISHB(hh)))
530 hb->hbmap[id][ia]->history[k] = hh | 2;
531 hb->nrhb++;
534 else
536 if (ihb == hbDist)
538 hb->ndist[frame]++;
539 if (!(ISDIST(hh)))
541 hb->hbmap[id][ia]->history[k] = hh | 1;
542 hb->nrdist++;
548 else
550 if (frame >= 0)
552 if (ihb == hbHB)
554 hb->nhb[frame]++;
556 else
558 if (ihb == hbDist)
560 hb->ndist[frame]++;
565 if (bMerge && daSwap)
567 h = hb->d.hydro[id][0];
569 /* Increment number if HBonds per H */
570 if (ihb == hbHB && !bContact)
572 inc_nhbonds(&(hb->d), d, h);
576 static char *mkatomname(const t_atoms *atoms, int i)
578 static char buf[32];
579 int rnr;
581 rnr = atoms->atom[i].resind;
582 sprintf(buf, "%4s%d%-4s",
583 *atoms->resinfo[rnr].name, atoms->resinfo[rnr].nr, *atoms->atomname[i]);
585 return buf;
588 static void gen_datable(int *index, int isize, unsigned char *datable, int natoms)
590 /* Generates table of all atoms and sets the ingroup bit for atoms in index[] */
591 int i;
593 for (i = 0; i < isize; i++)
595 if (index[i] >= natoms)
597 gmx_fatal(FARGS, "Atom has index %d larger than number of atoms %d.", index[i], natoms);
599 datable[index[i]] |= c_inGroupMask;
603 static void clear_datable_grp(unsigned char *datable, int size)
605 /* Clears group information from the table */
606 int i;
607 if (size > 0)
609 for (i = 0; i < size; i++)
611 datable[i] &= ~c_inGroupMask;
616 static void add_acc(t_acceptors *a, int ia, int grp)
618 if (a->nra >= a->max_nra)
620 a->max_nra += 16;
621 srenew(a->acc, a->max_nra);
622 srenew(a->grp, a->max_nra);
624 a->grp[a->nra] = grp;
625 a->acc[a->nra++] = ia;
628 static void search_acceptors(const t_topology *top, int isize,
629 const int *index, t_acceptors *a, int grp,
630 gmx_bool bNitAcc,
631 gmx_bool bContact, gmx_bool bDoIt, unsigned char *datable)
633 int i, n;
635 if (bDoIt)
637 for (i = 0; (i < isize); i++)
639 n = index[i];
640 if ((bContact ||
641 (((*top->atoms.atomname[n])[0] == 'O') ||
642 (bNitAcc && ((*top->atoms.atomname[n])[0] == 'N')))) &&
643 ISINGRP(datable[n]))
645 datable[n] |= c_acceptorMask;
646 add_acc(a, n, grp);
650 snew(a->aptr, top->atoms.nr);
651 for (i = 0; (i < top->atoms.nr); i++)
653 a->aptr[i] = NOTSET;
655 for (i = 0; (i < a->nra); i++)
657 a->aptr[a->acc[i]] = i;
661 static void add_h2d(int id, int ih, t_donors *ddd)
663 int i;
665 for (i = 0; (i < ddd->nhydro[id]); i++)
667 if (ddd->hydro[id][i] == ih)
669 printf("Hm. This isn't the first time I found this donor (%d,%d)\n",
670 ddd->don[id], ih);
671 break;
674 if (i == ddd->nhydro[id])
676 if (ddd->nhydro[id] >= MAXHYDRO)
678 gmx_fatal(FARGS, "Donor %d has more than %d hydrogens!",
679 ddd->don[id], MAXHYDRO);
681 ddd->hydro[id][i] = ih;
682 ddd->nhydro[id]++;
686 static void add_dh(t_donors *ddd, int id, int ih, int grp, const unsigned char *datable)
688 int i;
690 if (!datable || ISDON(datable[id]))
692 if (ddd->dptr[id] == NOTSET) /* New donor */
694 i = ddd->nrd;
695 ddd->dptr[id] = i;
697 else
699 i = ddd->dptr[id];
702 if (i == ddd->nrd)
704 if (ddd->nrd >= ddd->max_nrd)
706 ddd->max_nrd += 128;
707 srenew(ddd->don, ddd->max_nrd);
708 srenew(ddd->nhydro, ddd->max_nrd);
709 srenew(ddd->hydro, ddd->max_nrd);
710 srenew(ddd->nhbonds, ddd->max_nrd);
711 srenew(ddd->grp, ddd->max_nrd);
713 ddd->don[ddd->nrd] = id;
714 ddd->nhydro[ddd->nrd] = 0;
715 ddd->grp[ddd->nrd] = grp;
716 ddd->nrd++;
718 else
720 ddd->don[i] = id;
722 add_h2d(i, ih, ddd);
724 else
725 if (datable)
727 printf("Warning: Atom %d is not in the d/a-table!\n", id);
731 static void search_donors(const t_topology *top, int isize, const int *index,
732 t_donors *ddd, int grp, gmx_bool bContact, gmx_bool bDoIt,
733 unsigned char *datable)
735 int i, j;
736 t_functype func_type;
737 int nr1, nr2, nr3;
739 if (!ddd->dptr)
741 snew(ddd->dptr, top->atoms.nr);
742 for (i = 0; (i < top->atoms.nr); i++)
744 ddd->dptr[i] = NOTSET;
748 if (bContact)
750 if (bDoIt)
752 for (i = 0; (i < isize); i++)
754 datable[index[i]] |= c_donorMask;
755 add_dh(ddd, index[i], -1, grp, datable);
759 else
761 for (func_type = 0; (func_type < F_NRE); func_type++)
763 const t_ilist *interaction = &(top->idef.il[func_type]);
764 if (func_type == F_POSRES || func_type == F_FBPOSRES)
766 /* The ilist looks strange for posre. Bug in grompp?
767 * We don't need posre interactions for hbonds anyway.*/
768 continue;
770 for (i = 0; i < interaction->nr;
771 i += interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms+1)
773 /* next function */
774 if (func_type != top->idef.functype[interaction->iatoms[i]])
776 fprintf(stderr, "Error in func_type %s",
777 interaction_function[func_type].longname);
778 continue;
781 /* check out this functype */
782 if (func_type == F_SETTLE)
784 nr1 = interaction->iatoms[i+1];
785 nr2 = interaction->iatoms[i+2];
786 nr3 = interaction->iatoms[i+3];
788 if (ISINGRP(datable[nr1]))
790 if (ISINGRP(datable[nr2]))
792 datable[nr1] |= c_donorMask;
793 add_dh(ddd, nr1, nr1+1, grp, datable);
795 if (ISINGRP(datable[nr3]))
797 datable[nr1] |= c_donorMask;
798 add_dh(ddd, nr1, nr1+2, grp, datable);
802 else if (IS_CHEMBOND(func_type))
804 for (j = 0; j < 2; j++)
806 nr1 = interaction->iatoms[i+1+j];
807 nr2 = interaction->iatoms[i+2-j];
808 if ((*top->atoms.atomname[nr1][0] == 'H') &&
809 ((*top->atoms.atomname[nr2][0] == 'O') ||
810 (*top->atoms.atomname[nr2][0] == 'N')) &&
811 ISINGRP(datable[nr1]) && ISINGRP(datable[nr2]))
813 datable[nr2] |= c_donorMask;
814 add_dh(ddd, nr2, nr1, grp, datable);
820 #ifdef SAFEVSITES
821 for (func_type = 0; func_type < F_NRE; func_type++)
823 interaction = &top->idef.il[func_type];
824 for (i = 0; i < interaction->nr;
825 i += interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms+1)
827 /* next function */
828 if (func_type != top->idef.functype[interaction->iatoms[i]])
830 gmx_incons("function type in search_donors");
833 if (interaction_function[func_type].flags & IF_VSITE)
835 nr1 = interaction->iatoms[i+1];
836 if (*top->atoms.atomname[nr1][0] == 'H')
838 nr2 = nr1-1;
839 stop = FALSE;
840 while (!stop && ( *top->atoms.atomname[nr2][0] == 'H'))
842 if (nr2)
844 nr2--;
846 else
848 stop = TRUE;
851 if (!stop && ( ( *top->atoms.atomname[nr2][0] == 'O') ||
852 ( *top->atoms.atomname[nr2][0] == 'N') ) &&
853 ISINGRP(datable[nr1]) && ISINGRP(datable[nr2]))
855 datable[nr2] |= c_donorMask;
856 add_dh(ddd, nr2, nr1, grp, datable);
862 #endif
866 static t_gridcell ***init_grid(gmx_bool bBox, rvec box[], real rcut, ivec ngrid)
868 t_gridcell ***grid;
869 int i, y, z;
871 if (bBox)
873 for (i = 0; i < DIM; i++)
875 ngrid[i] = static_cast<int>(box[i][i]/(1.2*rcut));
879 if (!bBox || (ngrid[XX] < 3) || (ngrid[YY] < 3) || (ngrid[ZZ] < 3) )
881 for (i = 0; i < DIM; i++)
883 ngrid[i] = 1;
886 else
888 printf("\nWill do grid-search on %dx%dx%d grid, rcut=%3.8f\n",
889 ngrid[XX], ngrid[YY], ngrid[ZZ], rcut);
891 if (((ngrid[XX]*ngrid[YY]*ngrid[ZZ]) * sizeof(grid)) > INT_MAX)
893 gmx_fatal(FARGS, "Failed to allocate memory for %d x %d x %d grid points, which is larger than the maximum of %zu. "
894 "You are likely either using a box that is too large (box dimensions are %3.8f nm x%3.8f nm x%3.8f nm) or a cutoff (%3.8f nm) that is too small.",
895 ngrid[XX], ngrid[YY], ngrid[ZZ], INT_MAX/sizeof(grid), box[XX][XX], box[YY][YY], box[ZZ][ZZ], rcut);
897 snew(grid, ngrid[ZZ]);
898 for (z = 0; z < ngrid[ZZ]; z++)
900 snew((grid)[z], ngrid[YY]);
901 for (y = 0; y < ngrid[YY]; y++)
903 snew((grid)[z][y], ngrid[XX]);
906 return grid;
909 static void reset_nhbonds(t_donors *ddd)
911 int i, j;
913 for (i = 0; (i < ddd->nrd); i++)
915 for (j = 0; (j < MAXHH); j++)
917 ddd->nhbonds[i][j] = 0;
922 static void pbc_correct_gem(rvec dx, matrix box, const rvec hbox);
923 static void pbc_in_gridbox(rvec dx, matrix box);
925 static void build_grid(t_hbdata *hb, rvec x[], rvec xshell,
926 gmx_bool bBox, matrix box, rvec hbox,
927 real rcut, real rshell,
928 ivec ngrid, t_gridcell ***grid)
930 int i, m, gr, xi, yi, zi, nr;
931 int *ad;
932 ivec grididx;
933 rvec invdelta, dshell;
934 t_ncell *newgrid;
935 gmx_bool bDoRshell, bInShell;
936 real rshell2 = 0;
937 int gx, gy, gz;
938 int dum = -1;
940 bDoRshell = (rshell > 0);
941 rshell2 = gmx::square(rshell);
942 bInShell = TRUE;
944 #define DBB(x) if (debug && bDebug) fprintf(debug, "build_grid, line %d, %s = %d\n", __LINE__,#x, x)
945 DBB(dum);
946 for (m = 0; m < DIM; m++)
948 hbox[m] = box[m][m]*0.5;
949 if (bBox)
951 invdelta[m] = ngrid[m]/box[m][m];
952 if (1/invdelta[m] < rcut)
954 gmx_fatal(FARGS, "Your computational box has shrunk too much.\n"
955 "%s can not handle this situation, sorry.\n",
956 gmx::getProgramContext().displayName());
959 else
961 invdelta[m] = 0;
964 grididx[XX] = 0;
965 grididx[YY] = 0;
966 grididx[ZZ] = 0;
967 DBB(dum);
968 /* resetting atom counts */
969 for (gr = 0; (gr < grNR); gr++)
971 for (zi = 0; zi < ngrid[ZZ]; zi++)
973 for (yi = 0; yi < ngrid[YY]; yi++)
975 for (xi = 0; xi < ngrid[XX]; xi++)
977 grid[zi][yi][xi].d[gr].nr = 0;
978 grid[zi][yi][xi].a[gr].nr = 0;
982 DBB(dum);
984 /* put atoms in grid cells */
985 for (int acc = 0; acc < 2; acc++)
987 if (acc == 1)
989 nr = hb->a.nra;
990 ad = hb->a.acc;
992 else
994 nr = hb->d.nrd;
995 ad = hb->d.don;
997 DBB(acc);
998 for (i = 0; (i < nr); i++)
1000 /* check if we are inside the shell */
1001 /* if bDoRshell=FALSE then bInShell=TRUE always */
1002 DBB(i);
1003 if (bDoRshell)
1005 bInShell = TRUE;
1006 rvec_sub(x[ad[i]], xshell, dshell);
1007 if (bBox)
1009 gmx_bool bDone = FALSE;
1010 while (!bDone)
1012 bDone = TRUE;
1013 for (m = DIM-1; m >= 0 && bInShell; m--)
1015 if (dshell[m] < -hbox[m])
1017 bDone = FALSE;
1018 rvec_inc(dshell, box[m]);
1020 if (dshell[m] >= hbox[m])
1022 bDone = FALSE;
1023 dshell[m] -= 2*hbox[m];
1027 for (m = DIM-1; m >= 0 && bInShell; m--)
1029 /* if we're outside the cube, we're outside the sphere also! */
1030 if ( (dshell[m] > rshell) || (-dshell[m] > rshell) )
1032 bInShell = FALSE;
1036 /* if we're inside the cube, check if we're inside the sphere */
1037 if (bInShell)
1039 bInShell = norm2(dshell) < rshell2;
1042 DBB(i);
1043 if (bInShell)
1045 if (bBox)
1047 pbc_in_gridbox(x[ad[i]], box);
1049 for (m = DIM-1; m >= 0; m--)
1050 { /* determine grid index of atom */
1051 grididx[m] = static_cast<int>(x[ad[i]][m]*invdelta[m]);
1052 grididx[m] = (grididx[m]+ngrid[m]) % ngrid[m];
1056 gx = grididx[XX];
1057 gy = grididx[YY];
1058 gz = grididx[ZZ];
1059 range_check(gx, 0, ngrid[XX]);
1060 range_check(gy, 0, ngrid[YY]);
1061 range_check(gz, 0, ngrid[ZZ]);
1062 DBB(gx);
1063 DBB(gy);
1064 DBB(gz);
1065 /* add atom to grid cell */
1066 if (acc == 1)
1068 newgrid = &(grid[gz][gy][gx].a[gr]);
1070 else
1072 newgrid = &(grid[gz][gy][gx].d[gr]);
1074 if (newgrid->nr >= newgrid->maxnr)
1076 newgrid->maxnr += 10;
1077 DBB(newgrid->maxnr);
1078 srenew(newgrid->atoms, newgrid->maxnr);
1080 DBB(newgrid->nr);
1081 newgrid->atoms[newgrid->nr] = ad[i];
1082 newgrid->nr++;
1089 static void count_da_grid(const ivec ngrid, t_gridcell ***grid, t_icell danr)
1091 int gr, xi, yi, zi;
1093 for (gr = 0; (gr < grNR); gr++)
1095 danr[gr] = 0;
1096 for (zi = 0; zi < ngrid[ZZ]; zi++)
1098 for (yi = 0; yi < ngrid[YY]; yi++)
1100 for (xi = 0; xi < ngrid[XX]; xi++)
1102 danr[gr] += grid[zi][yi][xi].d[gr].nr;
1109 /* The grid loop.
1110 * Without a box, the grid is 1x1x1, so all loops are 1 long.
1111 * With a rectangular box (bTric==FALSE) all loops are 3 long.
1112 * With a triclinic box all loops are 3 long, except when a cell is
1113 * located next to one of the box edges which is not parallel to the
1114 * x/y-plane, in that case all cells in a line or layer are searched.
1115 * This could be implemented slightly more efficient, but the code
1116 * would get much more complicated.
1118 static inline int grid_loop_begin(int n, int x, gmx_bool bTric, gmx_bool bEdge)
1120 return ((n == 1) ? x : bTric && bEdge ? 0 : (x-1));
1122 static inline int grid_loop_end(int n, int x, gmx_bool bTric, gmx_bool bEdge)
1124 return ((n == 1) ? x : bTric && bEdge ? (n-1) : (x+1));
1126 static inline int grid_mod(int j, int n)
1128 return (j+n) % (n);
1131 static void dump_grid(FILE *fp, ivec ngrid, t_gridcell ***grid)
1133 int gr, x, y, z, sum[grNR];
1135 fprintf(fp, "grid %dx%dx%d\n", ngrid[XX], ngrid[YY], ngrid[ZZ]);
1136 for (gr = 0; gr < grNR; gr++)
1138 sum[gr] = 0;
1139 fprintf(fp, "GROUP %d (%s)\n", gr, grpnames[gr]);
1140 for (z = 0; z < ngrid[ZZ]; z += 2)
1142 fprintf(fp, "Z=%d,%d\n", z, z+1);
1143 for (y = 0; y < ngrid[YY]; y++)
1145 for (x = 0; x < ngrid[XX]; x++)
1147 fprintf(fp, "%3d", grid[x][y][z].d[gr].nr);
1148 sum[gr] += grid[z][y][x].d[gr].nr;
1149 fprintf(fp, "%3d", grid[x][y][z].a[gr].nr);
1150 sum[gr] += grid[z][y][x].a[gr].nr;
1153 fprintf(fp, " | ");
1154 if ( (z+1) < ngrid[ZZ])
1156 for (x = 0; x < ngrid[XX]; x++)
1158 fprintf(fp, "%3d", grid[z+1][y][x].d[gr].nr);
1159 sum[gr] += grid[z+1][y][x].d[gr].nr;
1160 fprintf(fp, "%3d", grid[z+1][y][x].a[gr].nr);
1161 sum[gr] += grid[z+1][y][x].a[gr].nr;
1164 fprintf(fp, "\n");
1168 fprintf(fp, "TOTALS:");
1169 for (gr = 0; gr < grNR; gr++)
1171 fprintf(fp, " %d=%d", gr, sum[gr]);
1173 fprintf(fp, "\n");
1176 /* New GMX record! 5 * in a row. Congratulations!
1177 * Sorry, only four left.
1179 static void free_grid(const ivec ngrid, t_gridcell ****grid)
1181 int y, z;
1182 t_gridcell ***g = *grid;
1184 for (z = 0; z < ngrid[ZZ]; z++)
1186 for (y = 0; y < ngrid[YY]; y++)
1188 sfree(g[z][y]);
1190 sfree(g[z]);
1192 sfree(g);
1193 g = nullptr;
1196 static void pbc_correct_gem(rvec dx, matrix box, const rvec hbox)
1198 int m;
1199 gmx_bool bDone = FALSE;
1200 while (!bDone)
1202 bDone = TRUE;
1203 for (m = DIM-1; m >= 0; m--)
1205 if (dx[m] < -hbox[m])
1207 bDone = FALSE;
1208 rvec_inc(dx, box[m]);
1210 if (dx[m] >= hbox[m])
1212 bDone = FALSE;
1213 rvec_dec(dx, box[m]);
1219 static void pbc_in_gridbox(rvec dx, matrix box)
1221 int m;
1222 gmx_bool bDone = FALSE;
1223 while (!bDone)
1225 bDone = TRUE;
1226 for (m = DIM-1; m >= 0; m--)
1228 if (dx[m] < 0)
1230 bDone = FALSE;
1231 rvec_inc(dx, box[m]);
1233 if (dx[m] >= box[m][m])
1235 bDone = FALSE;
1236 rvec_dec(dx, box[m]);
1242 /* Added argument r2cut, changed contact and implemented
1243 * use of second cut-off.
1244 * - Erik Marklund, June 29, 2006
1246 static int is_hbond(t_hbdata *hb, int grpd, int grpa, int d, int a,
1247 real rcut, real r2cut, real ccut,
1248 rvec x[], gmx_bool bBox, matrix box, rvec hbox,
1249 real *d_ha, real *ang, gmx_bool bDA, int *hhh,
1250 gmx_bool bContact, gmx_bool bMerge)
1252 int h, hh, id;
1253 rvec r_da, r_ha, r_dh;
1254 real rc2, r2c2, rda2, rha2, ca;
1255 gmx_bool HAinrange = FALSE; /* If !bDA. Needed for returning hbDist in a correct way. */
1256 gmx_bool daSwap = FALSE;
1258 if (d == a)
1260 return hbNo;
1263 if (((id = donor_index(&hb->d, grpd, d)) == NOTSET) ||
1264 (acceptor_index(&hb->a, grpa, a) == NOTSET))
1266 return hbNo;
1269 rc2 = rcut*rcut;
1270 r2c2 = r2cut*r2cut;
1272 rvec_sub(x[d], x[a], r_da);
1273 /* Insert projection code here */
1275 if (bMerge && d > a && isInterchangable(hb, d, a, grpd, grpa))
1277 /* Then this hbond/contact will be found again, or it has already been found. */
1278 /*return hbNo;*/
1280 if (bBox)
1282 if (d > a && bMerge && isInterchangable(hb, d, a, grpd, grpa)) /* acceptor is also a donor and vice versa? */
1283 { /* return hbNo; */
1284 daSwap = TRUE; /* If so, then their history should be filed with donor and acceptor swapped. */
1286 pbc_correct_gem(r_da, box, hbox);
1288 rda2 = iprod(r_da, r_da);
1290 if (bContact)
1292 if (daSwap && grpa == grpd)
1294 return hbNo;
1296 if (rda2 <= rc2)
1298 return hbHB;
1300 else if (rda2 < r2c2)
1302 return hbDist;
1304 else
1306 return hbNo;
1309 *hhh = NOTSET;
1311 if (bDA && (rda2 > rc2))
1313 return hbNo;
1316 for (h = 0; (h < hb->d.nhydro[id]); h++)
1318 hh = hb->d.hydro[id][h];
1319 rha2 = rc2+1;
1320 if (!bDA)
1322 rvec_sub(x[hh], x[a], r_ha);
1323 if (bBox)
1325 pbc_correct_gem(r_ha, box, hbox);
1327 rha2 = iprod(r_ha, r_ha);
1330 if (bDA || (rha2 <= rc2))
1332 rvec_sub(x[d], x[hh], r_dh);
1333 if (bBox)
1335 pbc_correct_gem(r_dh, box, hbox);
1338 if (!bDA)
1340 HAinrange = TRUE;
1342 ca = cos_angle(r_dh, r_da);
1343 /* if angle is smaller, cos is larger */
1344 if (ca >= ccut)
1346 *hhh = hh;
1347 *d_ha = std::sqrt(bDA ? rda2 : rha2);
1348 *ang = std::acos(ca);
1349 return hbHB;
1353 if (bDA || HAinrange)
1355 return hbDist;
1357 else
1359 return hbNo;
1363 /* Merging is now done on the fly, so do_merge is most likely obsolete now.
1364 * Will do some more testing before removing the function entirely.
1365 * - Erik Marklund, MAY 10 2010 */
1366 static void do_merge(t_hbdata *hb, int ntmp,
1367 bool htmp[], bool gtmp[],
1368 t_hbond *hb0, t_hbond *hb1)
1370 /* Here we need to make sure we're treating periodicity in
1371 * the right way for the geminate recombination kinetics. */
1373 int m, mm, n00, n01, nn0, nnframes;
1375 /* Decide where to start from when merging */
1376 n00 = hb0->n0;
1377 n01 = hb1->n0;
1378 nn0 = std::min(n00, n01);
1379 nnframes = std::max(n00 + hb0->nframes, n01 + hb1->nframes) - nn0;
1380 /* Initiate tmp arrays */
1381 for (m = 0; (m < ntmp); m++)
1383 htmp[m] = false;
1384 gtmp[m] = false;
1386 /* Fill tmp arrays with values due to first HB */
1387 /* Once again '<' had to be replaced with '<='
1388 to catch the last frame in which the hbond
1389 appears.
1390 - Erik Marklund, June 1, 2006 */
1391 for (m = 0; (m <= hb0->nframes); m++)
1393 mm = m+n00-nn0;
1394 htmp[mm] = is_hb(hb0->h[0], m);
1396 for (m = 0; (m <= hb0->nframes); m++)
1398 mm = m+n00-nn0;
1399 gtmp[mm] = is_hb(hb0->g[0], m);
1401 /* Next HB */
1402 for (m = 0; (m <= hb1->nframes); m++)
1404 mm = m+n01-nn0;
1405 htmp[mm] = htmp[mm] || is_hb(hb1->h[0], m);
1406 gtmp[mm] = gtmp[mm] || is_hb(hb1->g[0], m);
1408 /* Reallocate target array */
1409 if (nnframes > hb0->maxframes)
1411 srenew(hb0->h[0], 4+nnframes/hb->wordlen);
1412 srenew(hb0->g[0], 4+nnframes/hb->wordlen);
1415 /* Copy temp array to target array */
1416 for (m = 0; (m <= nnframes); m++)
1418 _set_hb(hb0->h[0], m, htmp[m]);
1419 _set_hb(hb0->g[0], m, gtmp[m]);
1422 /* Set scalar variables */
1423 hb0->n0 = nn0;
1424 hb0->maxframes = nnframes;
1427 static void merge_hb(t_hbdata *hb, gmx_bool bTwo, gmx_bool bContact)
1429 int i, inrnew, indnew, j, ii, jj, id, ia, ntmp;
1430 bool *htmp, *gtmp;
1431 t_hbond *hb0, *hb1;
1433 inrnew = hb->nrhb;
1434 indnew = hb->nrdist;
1436 /* Check whether donors are also acceptors */
1437 printf("Merging hbonds with Acceptor and Donor swapped\n");
1439 ntmp = 2*hb->max_frames;
1440 snew(gtmp, ntmp);
1441 snew(htmp, ntmp);
1442 for (i = 0; (i < hb->d.nrd); i++)
1444 fprintf(stderr, "\r%d/%d", i+1, hb->d.nrd);
1445 fflush(stderr);
1446 id = hb->d.don[i];
1447 ii = hb->a.aptr[id];
1448 for (j = 0; (j < hb->a.nra); j++)
1450 ia = hb->a.acc[j];
1451 jj = hb->d.dptr[ia];
1452 if ((id != ia) && (ii != NOTSET) && (jj != NOTSET) &&
1453 (!bTwo || (hb->d.grp[i] != hb->a.grp[j])))
1455 hb0 = hb->hbmap[i][j];
1456 hb1 = hb->hbmap[jj][ii];
1457 if (hb0 && hb1 && ISHB(hb0->history[0]) && ISHB(hb1->history[0]))
1459 do_merge(hb, ntmp, htmp, gtmp, hb0, hb1);
1460 if (ISHB(hb1->history[0]))
1462 inrnew--;
1464 else if (ISDIST(hb1->history[0]))
1466 indnew--;
1468 else
1469 if (bContact)
1471 gmx_incons("No contact history");
1473 else
1475 gmx_incons("Neither hydrogen bond nor distance");
1477 sfree(hb1->h[0]);
1478 sfree(hb1->g[0]);
1479 hb1->h[0] = nullptr;
1480 hb1->g[0] = nullptr;
1481 hb1->history[0] = hbNo;
1486 fprintf(stderr, "\n");
1487 printf("- Reduced number of hbonds from %d to %d\n", hb->nrhb, inrnew);
1488 printf("- Reduced number of distances from %d to %d\n", hb->nrdist, indnew);
1489 hb->nrhb = inrnew;
1490 hb->nrdist = indnew;
1491 sfree(gtmp);
1492 sfree(htmp);
1495 static void do_nhb_dist(FILE *fp, t_hbdata *hb, real t)
1497 int i, j, k, n_bound[MAXHH], nbtot;
1499 /* Set array to 0 */
1500 for (k = 0; (k < MAXHH); k++)
1502 n_bound[k] = 0;
1504 /* Loop over possible donors */
1505 for (i = 0; (i < hb->d.nrd); i++)
1507 for (j = 0; (j < hb->d.nhydro[i]); j++)
1509 n_bound[hb->d.nhbonds[i][j]]++;
1512 fprintf(fp, "%12.5e", t);
1513 nbtot = 0;
1514 for (k = 0; (k < MAXHH); k++)
1516 fprintf(fp, " %8d", n_bound[k]);
1517 nbtot += n_bound[k]*k;
1519 fprintf(fp, " %8d\n", nbtot);
1522 static void do_hblife(const char *fn, t_hbdata *hb, gmx_bool bMerge, gmx_bool bContact,
1523 const gmx_output_env_t *oenv)
1525 FILE *fp;
1526 const char *leg[] = { "p(t)", "t p(t)" };
1527 int *histo;
1528 int i, j, j0, k, m, nh, ihb, ohb, nhydro, ndump = 0;
1529 int nframes = hb->nframes;
1530 unsigned int **h;
1531 real t, x1, dt;
1532 double sum, integral;
1533 t_hbond *hbh;
1535 snew(h, hb->maxhydro);
1536 snew(histo, nframes+1);
1537 /* Total number of hbonds analyzed here */
1538 for (i = 0; (i < hb->d.nrd); i++)
1540 for (k = 0; (k < hb->a.nra); k++)
1542 hbh = hb->hbmap[i][k];
1543 if (hbh)
1545 if (bMerge)
1547 if (hbh->h[0])
1549 h[0] = hbh->h[0];
1550 nhydro = 1;
1552 else
1554 nhydro = 0;
1557 else
1559 nhydro = 0;
1560 for (m = 0; (m < hb->maxhydro); m++)
1562 if (hbh->h[m])
1564 h[nhydro++] = bContact ? hbh->g[m] : hbh->h[m];
1568 for (nh = 0; (nh < nhydro); nh++)
1570 ohb = 0;
1571 j0 = 0;
1573 for (j = 0; (j <= hbh->nframes); j++)
1575 ihb = static_cast<int>(is_hb(h[nh], j));
1576 if (debug && (ndump < 10))
1578 fprintf(debug, "%5d %5d\n", j, ihb);
1580 if (ihb != ohb)
1582 if (ihb)
1584 j0 = j;
1586 else
1588 histo[j-j0]++;
1590 ohb = ihb;
1593 ndump++;
1598 fprintf(stderr, "\n");
1599 if (bContact)
1601 fp = xvgropen(fn, "Uninterrupted contact lifetime", output_env_get_xvgr_tlabel(oenv), "()", oenv);
1603 else
1605 fp = xvgropen(fn, "Uninterrupted hydrogen bond lifetime", output_env_get_xvgr_tlabel(oenv), "()",
1606 oenv);
1609 xvgr_legend(fp, asize(leg), leg, oenv);
1610 j0 = nframes-1;
1611 while ((j0 > 0) && (histo[j0] == 0))
1613 j0--;
1615 sum = 0;
1616 for (i = 0; (i <= j0); i++)
1618 sum += histo[i];
1620 dt = hb->time[1]-hb->time[0];
1621 sum = dt*sum;
1622 integral = 0;
1623 for (i = 1; (i <= j0); i++)
1625 t = hb->time[i] - hb->time[0] - 0.5*dt;
1626 x1 = t*histo[i]/sum;
1627 fprintf(fp, "%8.3f %10.3e %10.3e\n", t, histo[i]/sum, x1);
1628 integral += x1;
1630 integral *= dt;
1631 xvgrclose(fp);
1632 printf("%s lifetime = %.2f ps\n", bContact ? "Contact" : "HB", integral);
1633 printf("Note that the lifetime obtained in this manner is close to useless\n");
1634 printf("Use the -ac option instead and check the Forward lifetime\n");
1635 please_cite(stdout, "Spoel2006b");
1636 sfree(h);
1637 sfree(histo);
1640 static void dump_ac(t_hbdata *hb, gmx_bool oneHB, int nDump)
1642 FILE *fp;
1643 int i, j, k, m, nd, ihb, idist;
1644 int nframes = hb->nframes;
1645 gmx_bool bPrint;
1646 t_hbond *hbh;
1648 if (nDump <= 0)
1650 return;
1652 fp = gmx_ffopen("debug-ac.xvg", "w");
1653 for (j = 0; (j < nframes); j++)
1655 fprintf(fp, "%10.3f", hb->time[j]);
1656 for (i = nd = 0; (i < hb->d.nrd) && (nd < nDump); i++)
1658 for (k = 0; (k < hb->a.nra) && (nd < nDump); k++)
1660 bPrint = FALSE;
1661 ihb = idist = 0;
1662 hbh = hb->hbmap[i][k];
1663 if (oneHB)
1665 if (hbh->h[0])
1667 ihb = static_cast<int>(is_hb(hbh->h[0], j));
1668 idist = static_cast<int>(is_hb(hbh->g[0], j));
1669 bPrint = TRUE;
1672 else
1674 for (m = 0; (m < hb->maxhydro) && !ihb; m++)
1676 ihb = static_cast<int>((ihb != 0) || (((hbh->h[m]) != nullptr) && is_hb(hbh->h[m], j)));
1677 idist = static_cast<int>((idist != 0) || (((hbh->g[m]) != nullptr) && is_hb(hbh->g[m], j)));
1679 /* This is not correct! */
1680 /* What isn't correct? -Erik M */
1681 bPrint = TRUE;
1683 if (bPrint)
1685 fprintf(fp, " %1d-%1d", ihb, idist);
1686 nd++;
1690 fprintf(fp, "\n");
1692 gmx_ffclose(fp);
1695 static real calc_dg(real tau, real temp)
1697 real kbt;
1699 kbt = BOLTZ*temp;
1700 if (tau <= 0)
1702 return -666;
1704 else
1706 return kbt*std::log(kbt*tau/PLANCK);
1710 typedef struct {
1711 int n0, n1, nparams, ndelta;
1712 real kkk[2];
1713 real *t, *ct, *nt, *kt, *sigma_ct, *sigma_nt, *sigma_kt;
1714 } t_luzar;
1716 static real compute_weighted_rates(int n, real t[], real ct[], real nt[],
1717 real kt[], real sigma_ct[], real sigma_nt[],
1718 real sigma_kt[], real *k, real *kp,
1719 real *sigma_k, real *sigma_kp,
1720 real fit_start)
1722 #define NK 10
1723 int i, j;
1724 t_luzar tl;
1725 real kkk = 0, kkp = 0, kk2 = 0, kp2 = 0, chi2;
1727 *sigma_k = 0;
1728 *sigma_kp = 0;
1730 for (i = 0; (i < n); i++)
1732 if (t[i] >= fit_start)
1734 break;
1737 tl.n0 = i;
1738 tl.n1 = n;
1739 tl.nparams = 2;
1740 tl.ndelta = 1;
1741 tl.t = t;
1742 tl.ct = ct;
1743 tl.nt = nt;
1744 tl.kt = kt;
1745 tl.sigma_ct = sigma_ct;
1746 tl.sigma_nt = sigma_nt;
1747 tl.sigma_kt = sigma_kt;
1748 tl.kkk[0] = *k;
1749 tl.kkk[1] = *kp;
1751 chi2 = 0; /*optimize_luzar_parameters(debug, &tl, 1000, 1e-3); */
1752 *k = tl.kkk[0];
1753 *kp = tl.kkk[1] = *kp;
1754 tl.ndelta = NK;
1755 for (j = 0; (j < NK); j++)
1757 kkk += tl.kkk[0];
1758 kkp += tl.kkk[1];
1759 kk2 += gmx::square(tl.kkk[0]);
1760 kp2 += gmx::square(tl.kkk[1]);
1761 tl.n0++;
1763 *sigma_k = std::sqrt(kk2/NK - gmx::square(kkk/NK));
1764 *sigma_kp = std::sqrt(kp2/NK - gmx::square(kkp/NK));
1766 return chi2;
1769 void analyse_corr(int n, real t[], real ct[], real nt[], real kt[],
1770 real sigma_ct[], real sigma_nt[], real sigma_kt[],
1771 real fit_start, real temp)
1773 int i0, i;
1774 real k = 1, kp = 1, kow = 1;
1775 real Q = 0, chi2, tau_hb, dtau, tau_rlx, e_1, sigma_k, sigma_kp, ddg;
1776 double tmp, sn2 = 0, sc2 = 0, sk2 = 0, scn = 0, sck = 0, snk = 0;
1777 gmx_bool bError = (sigma_ct != nullptr) && (sigma_nt != nullptr) && (sigma_kt != nullptr);
1779 for (i0 = 0; (i0 < n-2) && ((t[i0]-t[0]) < fit_start); i0++)
1783 if (i0 < n-2)
1785 for (i = i0; (i < n); i++)
1787 sc2 += gmx::square(ct[i]);
1788 sn2 += gmx::square(nt[i]);
1789 sk2 += gmx::square(kt[i]);
1790 sck += ct[i]*kt[i];
1791 snk += nt[i]*kt[i];
1792 scn += ct[i]*nt[i];
1794 printf("Hydrogen bond thermodynamics at T = %g K\n", temp);
1795 tmp = (sn2*sc2-gmx::square(scn));
1796 if ((tmp > 0) && (sn2 > 0))
1798 k = (sn2*sck-scn*snk)/tmp;
1799 kp = (k*scn-snk)/sn2;
1800 if (bError)
1802 chi2 = 0;
1803 for (i = i0; (i < n); i++)
1805 chi2 += gmx::square(k*ct[i]-kp*nt[i]-kt[i]);
1807 compute_weighted_rates(n, t, ct, nt, kt, sigma_ct, sigma_nt,
1808 sigma_kt, &k, &kp,
1809 &sigma_k, &sigma_kp, fit_start);
1810 Q = 0; /* quality_of_fit(chi2, 2);*/
1811 ddg = BOLTZ*temp*sigma_k/k;
1812 printf("Fitting paramaters chi^2 = %10g, Quality of fit = %10g\n",
1813 chi2, Q);
1814 printf("The Rate and Delta G are followed by an error estimate\n");
1815 printf("----------------------------------------------------------\n"
1816 "Type Rate (1/ps) Sigma Time (ps) DG (kJ/mol) Sigma\n");
1817 printf("Forward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
1818 k, sigma_k, 1/k, calc_dg(1/k, temp), ddg);
1819 ddg = BOLTZ*temp*sigma_kp/kp;
1820 printf("Backward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
1821 kp, sigma_kp, 1/kp, calc_dg(1/kp, temp), ddg);
1823 else
1825 chi2 = 0;
1826 for (i = i0; (i < n); i++)
1828 chi2 += gmx::square(k*ct[i]-kp*nt[i]-kt[i]);
1830 printf("Fitting parameters chi^2 = %10g\nQ = %10g\n",
1831 chi2, Q);
1832 printf("--------------------------------------------------\n"
1833 "Type Rate (1/ps) Time (ps) DG (kJ/mol) Chi^2\n");
1834 printf("Forward %10.3f %8.3f %10.3f %10g\n",
1835 k, 1/k, calc_dg(1/k, temp), chi2);
1836 printf("Backward %10.3f %8.3f %10.3f\n",
1837 kp, 1/kp, calc_dg(1/kp, temp));
1840 if (sc2 > 0)
1842 kow = 2*sck/sc2;
1843 printf("One-way %10.3f %s%8.3f %10.3f\n",
1844 kow, bError ? " " : "", 1/kow, calc_dg(1/kow, temp));
1846 else
1848 printf(" - Numerical problems computing HB thermodynamics:\n"
1849 "sc2 = %g sn2 = %g sk2 = %g sck = %g snk = %g scn = %g\n",
1850 sc2, sn2, sk2, sck, snk, scn);
1852 /* Determine integral of the correlation function */
1853 tau_hb = evaluate_integral(n, t, ct, nullptr, (t[n-1]-t[0])/2, &dtau);
1854 printf("Integral %10.3f %s%8.3f %10.3f\n", 1/tau_hb,
1855 bError ? " " : "", tau_hb, calc_dg(tau_hb, temp));
1856 e_1 = std::exp(-1.0);
1857 for (i = 0; (i < n-2); i++)
1859 if ((ct[i] > e_1) && (ct[i+1] <= e_1))
1861 break;
1864 if (i < n-2)
1866 /* Determine tau_relax from linear interpolation */
1867 tau_rlx = t[i]-t[0] + (e_1-ct[i])*(t[i+1]-t[i])/(ct[i+1]-ct[i]);
1868 printf("Relaxation %10.3f %8.3f %s%10.3f\n", 1/tau_rlx,
1869 tau_rlx, bError ? " " : "",
1870 calc_dg(tau_rlx, temp));
1873 else
1875 printf("Correlation functions too short to compute thermodynamics\n");
1879 void compute_derivative(int nn, const real x[], const real y[], real dydx[])
1881 int j;
1883 /* Compute k(t) = dc(t)/dt */
1884 for (j = 1; (j < nn-1); j++)
1886 dydx[j] = (y[j+1]-y[j-1])/(x[j+1]-x[j-1]);
1888 /* Extrapolate endpoints */
1889 dydx[0] = 2*dydx[1] - dydx[2];
1890 dydx[nn-1] = 2*dydx[nn-2] - dydx[nn-3];
1893 static void normalizeACF(real *ct, real *gt, int nhb, int len)
1895 real ct_fac, gt_fac = 0;
1896 int i;
1898 /* Xu and Berne use the same normalization constant */
1900 ct_fac = 1.0/ct[0];
1901 if (nhb != 0)
1903 gt_fac = 1.0/nhb;
1906 printf("Normalization for c(t) = %g for gh(t) = %g\n", ct_fac, gt_fac);
1907 for (i = 0; i < len; i++)
1909 ct[i] *= ct_fac;
1910 if (gt != nullptr)
1912 gt[i] *= gt_fac;
1917 static void do_hbac(const char *fn, t_hbdata *hb,
1918 int nDump, gmx_bool bMerge, gmx_bool bContact, real fit_start,
1919 real temp, gmx_bool R2, const gmx_output_env_t *oenv,
1920 int nThreads)
1922 FILE *fp;
1923 int i, j, k, m, ihb, idist, n2, nn;
1925 const char *legLuzar[] = {
1926 "Ac\\sfin sys\\v{}\\z{}(t)",
1927 "Ac(t)",
1928 "Cc\\scontact,hb\\v{}\\z{}(t)",
1929 "-dAc\\sfs\\v{}\\z{}/dt"
1931 gmx_bool bNorm = FALSE;
1932 double nhb = 0;
1933 real *rhbex = nullptr, *ht, *gt, *ght, *dght, *kt;
1934 real *ct, tail, tail2, dtail, *cct;
1935 const real tol = 1e-3;
1936 int nframes = hb->nframes;
1937 unsigned int **h = nullptr, **g = nullptr;
1938 int nh, nhbonds, nhydro;
1939 t_hbond *hbh;
1940 int acType;
1941 int *dondata = nullptr;
1943 enum {
1944 AC_NONE, AC_NN, AC_GEM, AC_LUZAR
1947 const bool bOMP = GMX_OPENMP;
1949 printf("Doing autocorrelation ");
1951 acType = AC_LUZAR;
1952 printf("according to the theory of Luzar and Chandler.\n");
1953 fflush(stdout);
1955 /* build hbexist matrix in reals for autocorr */
1956 /* Allocate memory for computing ACF (rhbex) and aggregating the ACF (ct) */
1957 n2 = 1;
1958 while (n2 < nframes)
1960 n2 *= 2;
1963 nn = nframes/2;
1965 if (acType != AC_NN || bOMP)
1967 snew(h, hb->maxhydro);
1968 snew(g, hb->maxhydro);
1971 /* Dump hbonds for debugging */
1972 dump_ac(hb, bMerge || bContact, nDump);
1974 /* Total number of hbonds analyzed here */
1975 nhbonds = 0;
1977 if (acType != AC_LUZAR && bOMP)
1979 nThreads = std::min((nThreads <= 0) ? INT_MAX : nThreads, gmx_omp_get_max_threads());
1981 gmx_omp_set_num_threads(nThreads);
1982 snew(dondata, nThreads);
1983 for (i = 0; i < nThreads; i++)
1985 dondata[i] = -1;
1987 printf("ACF calculations parallelized with OpenMP using %i threads.\n"
1988 "Expect close to linear scaling over this donor-loop.\n", nThreads);
1989 fflush(stdout);
1990 fprintf(stderr, "Donors: [thread no]\n");
1992 char tmpstr[7];
1993 for (i = 0; i < nThreads; i++)
1995 snprintf(tmpstr, 7, "[%i]", i);
1996 fprintf(stderr, "%-7s", tmpstr);
1999 fprintf(stderr, "\n");
2003 /* Build the ACF */
2004 snew(rhbex, 2*n2);
2005 snew(ct, 2*n2);
2006 snew(gt, 2*n2);
2007 snew(ht, 2*n2);
2008 snew(ght, 2*n2);
2009 snew(dght, 2*n2);
2011 snew(kt, nn);
2012 snew(cct, nn);
2014 for (i = 0; (i < hb->d.nrd); i++)
2016 for (k = 0; (k < hb->a.nra); k++)
2018 nhydro = 0;
2019 hbh = hb->hbmap[i][k];
2021 if (hbh)
2023 if (bMerge || bContact)
2025 if (ISHB(hbh->history[0]))
2027 h[0] = hbh->h[0];
2028 g[0] = hbh->g[0];
2029 nhydro = 1;
2032 else
2034 for (m = 0; (m < hb->maxhydro); m++)
2036 if (bContact ? ISDIST(hbh->history[m]) : ISHB(hbh->history[m]))
2038 g[nhydro] = hbh->g[m];
2039 h[nhydro] = hbh->h[m];
2040 nhydro++;
2045 int nf = hbh->nframes;
2046 for (nh = 0; (nh < nhydro); nh++)
2048 int nrint = bContact ? hb->nrdist : hb->nrhb;
2049 if ((((nhbonds+1) % 10) == 0) || (nhbonds+1 == nrint))
2051 fprintf(stderr, "\rACF %d/%d", nhbonds+1, nrint);
2052 fflush(stderr);
2054 nhbonds++;
2055 for (j = 0; (j < nframes); j++)
2057 if (j <= nf)
2059 ihb = static_cast<int>(is_hb(h[nh], j));
2060 idist = static_cast<int>(is_hb(g[nh], j));
2062 else
2064 ihb = idist = 0;
2066 rhbex[j] = ihb;
2067 /* For contacts: if a second cut-off is provided, use it,
2068 * otherwise use g(t) = 1-h(t) */
2069 if (!R2 && bContact)
2071 gt[j] = 1-ihb;
2073 else
2075 gt[j] = idist*(1-ihb);
2077 ht[j] = rhbex[j];
2078 nhb += ihb;
2081 /* The autocorrelation function is normalized after summation only */
2082 low_do_autocorr(nullptr, oenv, nullptr, nframes, 1, -1, &rhbex, hb->time[1]-hb->time[0],
2083 eacNormal, 1, FALSE, bNorm, FALSE, 0, -1, 0);
2085 /* Cross correlation analysis for thermodynamics */
2086 for (j = nframes; (j < n2); j++)
2088 ht[j] = 0;
2089 gt[j] = 0;
2092 cross_corr(n2, ht, gt, dght);
2094 for (j = 0; (j < nn); j++)
2096 ct[j] += rhbex[j];
2097 ght[j] += dght[j];
2103 fprintf(stderr, "\n");
2104 sfree(h);
2105 sfree(g);
2106 normalizeACF(ct, ght, static_cast<int>(nhb), nn);
2108 /* Determine tail value for statistics */
2109 tail = 0;
2110 tail2 = 0;
2111 for (j = nn/2; (j < nn); j++)
2113 tail += ct[j];
2114 tail2 += ct[j]*ct[j];
2116 tail /= (nn - int{nn/2});
2117 tail2 /= (nn - int{nn/2});
2118 dtail = std::sqrt(tail2-tail*tail);
2120 /* Check whether the ACF is long enough */
2121 if (dtail > tol)
2123 printf("\nWARNING: Correlation function is probably not long enough\n"
2124 "because the standard deviation in the tail of C(t) > %g\n"
2125 "Tail value (average C(t) over second half of acf): %g +/- %g\n",
2126 tol, tail, dtail);
2128 for (j = 0; (j < nn); j++)
2130 cct[j] = ct[j];
2131 ct[j] = (cct[j]-tail)/(1-tail);
2133 /* Compute negative derivative k(t) = -dc(t)/dt */
2134 compute_derivative(nn, hb->time, ct, kt);
2135 for (j = 0; (j < nn); j++)
2137 kt[j] = -kt[j];
2141 if (bContact)
2143 fp = xvgropen(fn, "Contact Autocorrelation", output_env_get_xvgr_tlabel(oenv), "C(t)", oenv);
2145 else
2147 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation", output_env_get_xvgr_tlabel(oenv), "C(t)", oenv);
2149 xvgr_legend(fp, asize(legLuzar), legLuzar, oenv);
2152 for (j = 0; (j < nn); j++)
2154 fprintf(fp, "%10g %10g %10g %10g %10g\n",
2155 hb->time[j]-hb->time[0], ct[j], cct[j], ght[j], kt[j]);
2157 xvgrclose(fp);
2159 analyse_corr(nn, hb->time, ct, ght, kt, nullptr, nullptr, nullptr,
2160 fit_start, temp);
2162 do_view(oenv, fn, nullptr);
2163 sfree(rhbex);
2164 sfree(ct);
2165 sfree(gt);
2166 sfree(ht);
2167 sfree(ght);
2168 sfree(dght);
2169 sfree(cct);
2170 sfree(kt);
2173 static void init_hbframe(t_hbdata *hb, int nframes, real t)
2175 int i;
2177 hb->time[nframes] = t;
2178 hb->nhb[nframes] = 0;
2179 hb->ndist[nframes] = 0;
2180 for (i = 0; (i < max_hx); i++)
2182 hb->nhx[nframes][i] = 0;
2186 static FILE *open_donor_properties_file(const char *fn,
2187 t_hbdata *hb,
2188 const gmx_output_env_t *oenv)
2190 FILE *fp = nullptr;
2191 const char *leg[] = { "Nbound", "Nfree" };
2193 if (!fn || !hb)
2195 return nullptr;
2198 fp = xvgropen(fn, "Donor properties", output_env_get_xvgr_tlabel(oenv), "Number", oenv);
2199 xvgr_legend(fp, asize(leg), leg, oenv);
2201 return fp;
2204 static void analyse_donor_properties(FILE *fp, t_hbdata *hb, int nframes, real t)
2206 int i, j, k, nbound, nb, nhtot;
2208 if (!fp || !hb)
2210 return;
2212 nbound = 0;
2213 nhtot = 0;
2214 for (i = 0; (i < hb->d.nrd); i++)
2216 for (k = 0; (k < hb->d.nhydro[i]); k++)
2218 nb = 0;
2219 nhtot++;
2220 for (j = 0; (j < hb->a.nra) && (nb == 0); j++)
2222 if (hb->hbmap[i][j] && hb->hbmap[i][j]->h[k] &&
2223 is_hb(hb->hbmap[i][j]->h[k], nframes))
2225 nb = 1;
2228 nbound += nb;
2231 fprintf(fp, "%10.3e %6d %6d\n", t, nbound, nhtot-nbound);
2234 static void dump_hbmap(t_hbdata *hb,
2235 int nfile, t_filenm fnm[], gmx_bool bTwo,
2236 gmx_bool bContact, const int isize[], int *index[], char *grpnames[],
2237 const t_atoms *atoms)
2239 FILE *fp, *fplog;
2240 int ddd, hhh, aaa, i, j, k, m, grp;
2241 char ds[32], hs[32], as[32];
2242 gmx_bool first;
2244 fp = opt2FILE("-hbn", nfile, fnm, "w");
2245 if (opt2bSet("-g", nfile, fnm))
2247 fplog = gmx_ffopen(opt2fn("-g", nfile, fnm), "w");
2248 fprintf(fplog, "# %10s %12s %12s\n", "Donor", "Hydrogen", "Acceptor");
2250 else
2252 fplog = nullptr;
2254 for (grp = gr0; grp <= (bTwo ? gr1 : gr0); grp++)
2256 fprintf(fp, "[ %s ]", grpnames[grp]);
2257 for (i = 0; i < isize[grp]; i++)
2259 fprintf(fp, (i%15) ? " " : "\n");
2260 fprintf(fp, " %4d", index[grp][i]+1);
2262 fprintf(fp, "\n");
2264 if (!bContact)
2266 fprintf(fp, "[ donors_hydrogens_%s ]\n", grpnames[grp]);
2267 for (i = 0; (i < hb->d.nrd); i++)
2269 if (hb->d.grp[i] == grp)
2271 for (j = 0; (j < hb->d.nhydro[i]); j++)
2273 fprintf(fp, " %4d %4d", hb->d.don[i]+1,
2274 hb->d.hydro[i][j]+1);
2276 fprintf(fp, "\n");
2279 first = TRUE;
2280 fprintf(fp, "[ acceptors_%s ]", grpnames[grp]);
2281 for (i = 0; (i < hb->a.nra); i++)
2283 if (hb->a.grp[i] == grp)
2285 fprintf(fp, (i%15 && !first) ? " " : "\n");
2286 fprintf(fp, " %4d", hb->a.acc[i]+1);
2287 first = FALSE;
2290 fprintf(fp, "\n");
2293 if (bTwo)
2295 fprintf(fp, bContact ? "[ contacts_%s-%s ]\n" :
2296 "[ hbonds_%s-%s ]\n", grpnames[0], grpnames[1]);
2298 else
2300 fprintf(fp, bContact ? "[ contacts_%s ]" : "[ hbonds_%s ]\n", grpnames[0]);
2303 for (i = 0; (i < hb->d.nrd); i++)
2305 ddd = hb->d.don[i];
2306 for (k = 0; (k < hb->a.nra); k++)
2308 aaa = hb->a.acc[k];
2309 for (m = 0; (m < hb->d.nhydro[i]); m++)
2311 if (hb->hbmap[i][k] && ISHB(hb->hbmap[i][k]->history[m]))
2313 sprintf(ds, "%s", mkatomname(atoms, ddd));
2314 sprintf(as, "%s", mkatomname(atoms, aaa));
2315 if (bContact)
2317 fprintf(fp, " %6d %6d\n", ddd+1, aaa+1);
2318 if (fplog)
2320 fprintf(fplog, "%12s %12s\n", ds, as);
2323 else
2325 hhh = hb->d.hydro[i][m];
2326 sprintf(hs, "%s", mkatomname(atoms, hhh));
2327 fprintf(fp, " %6d %6d %6d\n", ddd+1, hhh+1, aaa+1);
2328 if (fplog)
2330 fprintf(fplog, "%12s %12s %12s\n", ds, hs, as);
2337 gmx_ffclose(fp);
2338 if (fplog)
2340 gmx_ffclose(fplog);
2344 /* sync_hbdata() updates the parallel t_hbdata p_hb using hb as template.
2345 * It mimics add_frames() and init_frame() to some extent. */
2346 static void sync_hbdata(t_hbdata *p_hb, int nframes)
2348 if (nframes >= p_hb->max_frames)
2350 p_hb->max_frames += 4096;
2351 srenew(p_hb->nhb, p_hb->max_frames);
2352 srenew(p_hb->ndist, p_hb->max_frames);
2353 srenew(p_hb->n_bound, p_hb->max_frames);
2354 srenew(p_hb->nhx, p_hb->max_frames);
2355 if (p_hb->bDAnr)
2357 srenew(p_hb->danr, p_hb->max_frames);
2359 std::memset(&(p_hb->nhb[nframes]), 0, sizeof(int) * (p_hb->max_frames-nframes));
2360 std::memset(&(p_hb->ndist[nframes]), 0, sizeof(int) * (p_hb->max_frames-nframes));
2361 p_hb->nhb[nframes] = 0;
2362 p_hb->ndist[nframes] = 0;
2365 p_hb->nframes = nframes;
2367 std::memset(&(p_hb->nhx[nframes]), 0, sizeof(int)*max_hx); /* zero the helix count for this frame */
2370 int gmx_hbond(int argc, char *argv[])
2372 const char *desc[] = {
2373 "[THISMODULE] computes and analyzes hydrogen bonds. Hydrogen bonds are",
2374 "determined based on cutoffs for the angle Hydrogen - Donor - Acceptor",
2375 "(zero is extended) and the distance Donor - Acceptor",
2376 "(or Hydrogen - Acceptor using [TT]-noda[tt]).",
2377 "OH and NH groups are regarded as donors, O is an acceptor always,",
2378 "N is an acceptor by default, but this can be switched using",
2379 "[TT]-nitacc[tt]. Dummy hydrogen atoms are assumed to be connected",
2380 "to the first preceding non-hydrogen atom.[PAR]",
2382 "You need to specify two groups for analysis, which must be either",
2383 "identical or non-overlapping. All hydrogen bonds between the two",
2384 "groups are analyzed.[PAR]",
2386 "If you set [TT]-shell[tt], you will be asked for an additional index group",
2387 "which should contain exactly one atom. In this case, only hydrogen",
2388 "bonds between atoms within the shell distance from the one atom are",
2389 "considered.[PAR]",
2391 "With option -ac, rate constants for hydrogen bonding can be derived with the",
2392 "model of Luzar and Chandler (Nature 379:55, 1996; J. Chem. Phys. 113:23, 2000).",
2393 "If contact kinetics are analyzed by using the -contact option, then",
2394 "n(t) can be defined as either all pairs that are not within contact distance r at time t",
2395 "(corresponding to leaving the -r2 option at the default value 0) or all pairs that",
2396 "are within distance r2 (corresponding to setting a second cut-off value with option -r2).",
2397 "See mentioned literature for more details and definitions.",
2398 "[PAR]",
2400 /* "It is also possible to analyse specific hydrogen bonds with",
2401 "[TT]-sel[tt]. This index file must contain a group of atom triplets",
2402 "Donor Hydrogen Acceptor, in the following way::",
2404 "[ selected ]",
2405 " 20 21 24",
2406 " 25 26 29",
2407 " 1 3 6",
2409 "Note that the triplets need not be on separate lines.",
2410 "Each atom triplet specifies a hydrogen bond to be analyzed,",
2411 "note also that no check is made for the types of atoms.[PAR]",
2414 "[BB]Output:[bb]",
2416 " * [TT]-num[tt]: number of hydrogen bonds as a function of time.",
2417 " * [TT]-ac[tt]: average over all autocorrelations of the existence",
2418 " functions (either 0 or 1) of all hydrogen bonds.",
2419 " * [TT]-dist[tt]: distance distribution of all hydrogen bonds.",
2420 " * [TT]-ang[tt]: angle distribution of all hydrogen bonds.",
2421 " * [TT]-hx[tt]: the number of n-n+i hydrogen bonds as a function of time",
2422 " where n and n+i stand for residue numbers and i ranges from 0 to 6.",
2423 " This includes the n-n+3, n-n+4 and n-n+5 hydrogen bonds associated",
2424 " with helices in proteins.",
2425 " * [TT]-hbn[tt]: all selected groups, donors, hydrogens and acceptors",
2426 " for selected groups, all hydrogen bonded atoms from all groups and",
2427 " all solvent atoms involved in insertion.",
2428 " * [TT]-hbm[tt]: existence matrix for all hydrogen bonds over all",
2429 " frames, this also contains information on solvent insertion",
2430 " into hydrogen bonds. Ordering is identical to that in [TT]-hbn[tt]",
2431 " index file.",
2432 " * [TT]-dan[tt]: write out the number of donors and acceptors analyzed for",
2433 " each timeframe. This is especially useful when using [TT]-shell[tt].",
2434 " * [TT]-nhbdist[tt]: compute the number of HBonds per hydrogen in order to",
2435 " compare results to Raman Spectroscopy.",
2437 "Note: options [TT]-ac[tt], [TT]-life[tt], [TT]-hbn[tt] and [TT]-hbm[tt]",
2438 "require an amount of memory proportional to the total numbers of donors",
2439 "times the total number of acceptors in the selected group(s)."
2442 static real acut = 30, abin = 1, rcut = 0.35, r2cut = 0, rbin = 0.005, rshell = -1;
2443 static real maxnhb = 0, fit_start = 1, fit_end = 60, temp = 298.15;
2444 static gmx_bool bNitAcc = TRUE, bDA = TRUE, bMerge = TRUE;
2445 static int nDump = 0;
2446 static int nThreads = 0;
2448 static gmx_bool bContact = FALSE;
2450 /* options */
2451 t_pargs pa [] = {
2452 { "-a", FALSE, etREAL, {&acut},
2453 "Cutoff angle (degrees, Hydrogen - Donor - Acceptor)" },
2454 { "-r", FALSE, etREAL, {&rcut},
2455 "Cutoff radius (nm, X - Acceptor, see next option)" },
2456 { "-da", FALSE, etBOOL, {&bDA},
2457 "Use distance Donor-Acceptor (if TRUE) or Hydrogen-Acceptor (FALSE)" },
2458 { "-r2", FALSE, etREAL, {&r2cut},
2459 "Second cutoff radius. Mainly useful with [TT]-contact[tt] and [TT]-ac[tt]"},
2460 { "-abin", FALSE, etREAL, {&abin},
2461 "Binwidth angle distribution (degrees)" },
2462 { "-rbin", FALSE, etREAL, {&rbin},
2463 "Binwidth distance distribution (nm)" },
2464 { "-nitacc", FALSE, etBOOL, {&bNitAcc},
2465 "Regard nitrogen atoms as acceptors" },
2466 { "-contact", FALSE, etBOOL, {&bContact},
2467 "Do not look for hydrogen bonds, but merely for contacts within the cut-off distance" },
2468 { "-shell", FALSE, etREAL, {&rshell},
2469 "when > 0, only calculate hydrogen bonds within # nm shell around "
2470 "one particle" },
2471 { "-fitstart", FALSE, etREAL, {&fit_start},
2472 "Time (ps) from which to start fitting the correlation functions in order to obtain the forward and backward rate constants for HB breaking and formation. With [TT]-gemfit[tt] we suggest [TT]-fitstart 0[tt]" },
2473 { "-fitend", FALSE, etREAL, {&fit_end},
2474 "Time (ps) to which to stop fitting the correlation functions in order to obtain the forward and backward rate constants for HB breaking and formation (only with [TT]-gemfit[tt])" },
2475 { "-temp", FALSE, etREAL, {&temp},
2476 "Temperature (K) for computing the Gibbs energy corresponding to HB breaking and reforming" },
2477 { "-dump", FALSE, etINT, {&nDump},
2478 "Dump the first N hydrogen bond ACFs in a single [REF].xvg[ref] file for debugging" },
2479 { "-max_hb", FALSE, etREAL, {&maxnhb},
2480 "Theoretical maximum number of hydrogen bonds used for normalizing HB autocorrelation function. Can be useful in case the program estimates it wrongly" },
2481 { "-merge", FALSE, etBOOL, {&bMerge},
2482 "H-bonds between the same donor and acceptor, but with different hydrogen are treated as a single H-bond. Mainly important for the ACF." },
2483 #if GMX_OPENMP
2484 { "-nthreads", FALSE, etINT, {&nThreads},
2485 "Number of threads used for the parallel loop over autocorrelations. nThreads <= 0 means maximum number of threads. Requires linking with OpenMP. The number of threads is limited by the number of cores (before OpenMP v.3 ) or environment variable OMP_THREAD_LIMIT (OpenMP v.3)"},
2486 #endif
2488 const char *bugs[] = {
2489 "The option [TT]-sel[tt] that used to work on selected hbonds is out of order, and therefore not available for the time being."
2491 t_filenm fnm[] = {
2492 { efTRX, "-f", nullptr, ffREAD },
2493 { efTPR, nullptr, nullptr, ffREAD },
2494 { efNDX, nullptr, nullptr, ffOPTRD },
2495 /* { efNDX, "-sel", "select", ffOPTRD },*/
2496 { efXVG, "-num", "hbnum", ffWRITE },
2497 { efLOG, "-g", "hbond", ffOPTWR },
2498 { efXVG, "-ac", "hbac", ffOPTWR },
2499 { efXVG, "-dist", "hbdist", ffOPTWR },
2500 { efXVG, "-ang", "hbang", ffOPTWR },
2501 { efXVG, "-hx", "hbhelix", ffOPTWR },
2502 { efNDX, "-hbn", "hbond", ffOPTWR },
2503 { efXPM, "-hbm", "hbmap", ffOPTWR },
2504 { efXVG, "-don", "donor", ffOPTWR },
2505 { efXVG, "-dan", "danum", ffOPTWR },
2506 { efXVG, "-life", "hblife", ffOPTWR },
2507 { efXVG, "-nhbdist", "nhbdist", ffOPTWR }
2510 #define NFILE asize(fnm)
2512 char hbmap [HB_NR] = { ' ', 'o', '-', '*' };
2513 const char *hbdesc[HB_NR] = { "None", "Present", "Inserted", "Present & Inserted" };
2514 t_rgb hbrgb [HB_NR] = { {1, 1, 1}, {1, 0, 0}, {0, 0, 1}, {1, 0, 1} };
2516 t_trxstatus *status;
2517 bool trrStatus = true;
2518 t_topology top;
2519 t_pargs *ppa;
2520 int npargs, natoms, nframes = 0, shatom;
2521 int *isize;
2522 char **grpnames;
2523 int **index;
2524 rvec *x, hbox;
2525 matrix box;
2526 real t, ccut, dist = 0.0, ang = 0.0;
2527 double max_nhb, aver_nhb, aver_dist;
2528 int h = 0, i = 0, j, k = 0, ogrp, nsel;
2529 int xi = 0, yi, zi, ai;
2530 int xj, yj, zj, aj, xjj, yjj, zjj;
2531 gmx_bool bSelected, bHBmap, bStop, bTwo, bBox, bTric;
2532 int *adist, *rdist;
2533 int grp, nabin, nrbin, resdist, ihb;
2534 char **leg;
2535 t_hbdata *hb;
2536 FILE *fp, *fpnhb = nullptr, *donor_properties = nullptr;
2537 t_gridcell ***grid;
2538 t_ncell *icell, *jcell;
2539 ivec ngrid;
2540 unsigned char *datable;
2541 gmx_output_env_t *oenv;
2542 int ii, hh, actual_nThreads;
2543 int threadNr = 0;
2544 gmx_bool bParallel;
2545 gmx_bool bEdge_yjj, bEdge_xjj;
2547 t_hbdata **p_hb = nullptr; /* one per thread, then merge after the frame loop */
2548 int **p_adist = nullptr, **p_rdist = nullptr; /* a histogram for each thread. */
2550 const bool bOMP = GMX_OPENMP;
2552 npargs = asize(pa);
2553 ppa = add_acf_pargs(&npargs, pa);
2555 if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_TIME_UNIT, NFILE, fnm, npargs,
2556 ppa, asize(desc), desc, asize(bugs), bugs, &oenv))
2558 sfree(ppa);
2559 return 0;
2562 /* process input */
2563 bSelected = FALSE;
2564 ccut = std::cos(acut*DEG2RAD);
2566 if (bContact)
2568 if (bSelected)
2570 gmx_fatal(FARGS, "Can not analyze selected contacts.");
2572 if (!bDA)
2574 gmx_fatal(FARGS, "Can not analyze contact between H and A: turn off -noda");
2578 /* Initiate main data structure! */
2579 bHBmap = (opt2bSet("-ac", NFILE, fnm) ||
2580 opt2bSet("-life", NFILE, fnm) ||
2581 opt2bSet("-hbn", NFILE, fnm) ||
2582 opt2bSet("-hbm", NFILE, fnm));
2584 if (opt2bSet("-nhbdist", NFILE, fnm))
2586 const char *leg[MAXHH+1] = { "0 HBs", "1 HB", "2 HBs", "3 HBs", "Total" };
2587 fpnhb = xvgropen(opt2fn("-nhbdist", NFILE, fnm),
2588 "Number of donor-H with N HBs", output_env_get_xvgr_tlabel(oenv), "N", oenv);
2589 xvgr_legend(fpnhb, asize(leg), leg, oenv);
2592 hb = mk_hbdata(bHBmap, opt2bSet("-dan", NFILE, fnm), bMerge || bContact);
2594 /* get topology */
2595 t_inputrec irInstance;
2596 t_inputrec *ir = &irInstance;
2597 read_tpx_top(ftp2fn(efTPR, NFILE, fnm), ir, box, &natoms, nullptr, nullptr, &top);
2599 snew(grpnames, grNR);
2600 snew(index, grNR);
2601 snew(isize, grNR);
2602 /* Make Donor-Acceptor table */
2603 snew(datable, top.atoms.nr);
2605 if (bSelected)
2607 /* analyze selected hydrogen bonds */
2608 printf("Select group with selected atoms:\n");
2609 get_index(&(top.atoms), opt2fn("-sel", NFILE, fnm),
2610 1, &nsel, index, grpnames);
2611 if (nsel % 3)
2613 gmx_fatal(FARGS, "Number of atoms in group '%s' not a multiple of 3\n"
2614 "and therefore cannot contain triplets of "
2615 "Donor-Hydrogen-Acceptor", grpnames[0]);
2617 bTwo = FALSE;
2619 for (i = 0; (i < nsel); i += 3)
2621 int dd = index[0][i];
2622 int aa = index[0][i+2];
2623 /* int */ hh = index[0][i+1];
2624 add_dh (&hb->d, dd, hh, i, datable);
2625 add_acc(&hb->a, aa, i);
2626 /* Should this be here ? */
2627 snew(hb->d.dptr, top.atoms.nr);
2628 snew(hb->a.aptr, top.atoms.nr);
2629 add_hbond(hb, dd, aa, hh, gr0, gr0, 0, bMerge, 0, bContact);
2631 printf("Analyzing %d selected hydrogen bonds from '%s'\n",
2632 isize[0], grpnames[0]);
2634 else
2636 /* analyze all hydrogen bonds: get group(s) */
2637 printf("Specify 2 groups to analyze:\n");
2638 get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm),
2639 2, isize, index, grpnames);
2641 /* check if we have two identical or two non-overlapping groups */
2642 bTwo = isize[0] != isize[1];
2643 for (i = 0; (i < isize[0]) && !bTwo; i++)
2645 bTwo = index[0][i] != index[1][i];
2647 if (bTwo)
2649 printf("Checking for overlap in atoms between %s and %s\n",
2650 grpnames[0], grpnames[1]);
2652 gen_datable(index[0], isize[0], datable, top.atoms.nr);
2654 for (i = 0; i < isize[1]; i++)
2656 if (ISINGRP(datable[index[1][i]]))
2658 gmx_fatal(FARGS, "Partial overlap between groups '%s' and '%s'",
2659 grpnames[0], grpnames[1]);
2663 if (bTwo)
2665 printf("Calculating %s "
2666 "between %s (%d atoms) and %s (%d atoms)\n",
2667 bContact ? "contacts" : "hydrogen bonds",
2668 grpnames[0], isize[0], grpnames[1], isize[1]);
2670 else
2672 fprintf(stderr, "Calculating %s in %s (%d atoms)\n",
2673 bContact ? "contacts" : "hydrogen bonds", grpnames[0], isize[0]);
2676 sfree(datable);
2678 /* search donors and acceptors in groups */
2679 snew(datable, top.atoms.nr);
2680 for (i = 0; (i < grNR); i++)
2682 if ( ((i == gr0) && !bSelected ) ||
2683 ((i == gr1) && bTwo ))
2685 gen_datable(index[i], isize[i], datable, top.atoms.nr);
2686 if (bContact)
2688 search_acceptors(&top, isize[i], index[i], &hb->a, i,
2689 bNitAcc, TRUE, (bTwo && (i == gr0)) || !bTwo, datable);
2690 search_donors (&top, isize[i], index[i], &hb->d, i,
2691 TRUE, (bTwo && (i == gr1)) || !bTwo, datable);
2693 else
2695 search_acceptors(&top, isize[i], index[i], &hb->a, i, bNitAcc, FALSE, TRUE, datable);
2696 search_donors (&top, isize[i], index[i], &hb->d, i, FALSE, TRUE, datable);
2698 if (bTwo)
2700 clear_datable_grp(datable, top.atoms.nr);
2704 sfree(datable);
2705 printf("Found %d donors and %d acceptors\n", hb->d.nrd, hb->a.nra);
2706 /*if (bSelected)
2707 snew(donors[gr0D], dons[gr0D].nrd);*/
2709 donor_properties = open_donor_properties_file(opt2fn_null("-don", NFILE, fnm), hb, oenv);
2711 if (bHBmap)
2713 printf("Making hbmap structure...");
2714 /* Generate hbond data structure */
2715 mk_hbmap(hb);
2716 printf("done.\n");
2719 /* check input */
2720 bStop = FALSE;
2721 if (hb->d.nrd + hb->a.nra == 0)
2723 printf("No Donors or Acceptors found\n");
2724 bStop = TRUE;
2726 if (!bStop)
2728 if (hb->d.nrd == 0)
2730 printf("No Donors found\n");
2731 bStop = TRUE;
2733 if (hb->a.nra == 0)
2735 printf("No Acceptors found\n");
2736 bStop = TRUE;
2739 if (bStop)
2741 gmx_fatal(FARGS, "Nothing to be done");
2744 shatom = 0;
2745 if (rshell > 0)
2747 int shisz;
2748 int *shidx;
2749 char *shgrpnm;
2750 /* get index group with atom for shell */
2753 printf("Select atom for shell (1 atom):\n");
2754 get_index(&(top.atoms), ftp2fn_null(efNDX, NFILE, fnm),
2755 1, &shisz, &shidx, &shgrpnm);
2756 if (shisz != 1)
2758 printf("group contains %d atoms, should be 1 (one)\n", shisz);
2761 while (shisz != 1);
2762 shatom = shidx[0];
2763 printf("Will calculate hydrogen bonds within a shell "
2764 "of %g nm around atom %i\n", rshell, shatom+1);
2767 /* Analyze trajectory */
2768 natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
2769 if (natoms > top.atoms.nr)
2771 gmx_fatal(FARGS, "Topology (%d atoms) does not match trajectory (%d atoms)",
2772 top.atoms.nr, natoms);
2775 bBox = (ir->ePBC != epbcNONE);
2776 grid = init_grid(bBox, box, (rcut > r2cut) ? rcut : r2cut, ngrid);
2777 nabin = static_cast<int>(acut/abin);
2778 nrbin = static_cast<int>(rcut/rbin);
2779 snew(adist, nabin+1);
2780 snew(rdist, nrbin+1);
2782 #if !GMX_OPENMP
2783 #define __ADIST adist
2784 #define __RDIST rdist
2785 #define __HBDATA hb
2786 #else /* GMX_OPENMP ================================================== \
2787 * Set up the OpenMP stuff, |
2788 * like the number of threads and such |
2789 * Also start the parallel loop. |
2791 #define __ADIST p_adist[threadNr]
2792 #define __RDIST p_rdist[threadNr]
2793 #define __HBDATA p_hb[threadNr]
2794 #endif
2795 if (bOMP)
2797 bParallel = !bSelected;
2799 if (bParallel)
2801 actual_nThreads = std::min((nThreads <= 0) ? INT_MAX : nThreads, gmx_omp_get_max_threads());
2803 gmx_omp_set_num_threads(actual_nThreads);
2804 printf("Frame loop parallelized with OpenMP using %i threads.\n", actual_nThreads);
2805 fflush(stdout);
2807 else
2809 actual_nThreads = 1;
2812 snew(p_hb, actual_nThreads);
2813 snew(p_adist, actual_nThreads);
2814 snew(p_rdist, actual_nThreads);
2815 for (i = 0; i < actual_nThreads; i++)
2817 snew(p_hb[i], 1);
2818 snew(p_adist[i], nabin+1);
2819 snew(p_rdist[i], nrbin+1);
2821 p_hb[i]->max_frames = 0;
2822 p_hb[i]->nhb = nullptr;
2823 p_hb[i]->ndist = nullptr;
2824 p_hb[i]->n_bound = nullptr;
2825 p_hb[i]->time = nullptr;
2826 p_hb[i]->nhx = nullptr;
2828 p_hb[i]->bHBmap = hb->bHBmap;
2829 p_hb[i]->bDAnr = hb->bDAnr;
2830 p_hb[i]->wordlen = hb->wordlen;
2831 p_hb[i]->nframes = hb->nframes;
2832 p_hb[i]->maxhydro = hb->maxhydro;
2833 p_hb[i]->danr = hb->danr;
2834 p_hb[i]->d = hb->d;
2835 p_hb[i]->a = hb->a;
2836 p_hb[i]->hbmap = hb->hbmap;
2837 p_hb[i]->time = hb->time; /* This may need re-syncing at every frame. */
2839 p_hb[i]->nrhb = 0;
2840 p_hb[i]->nrdist = 0;
2844 /* Make a thread pool here,
2845 * instead of forking anew at every frame. */
2847 #pragma omp parallel \
2848 firstprivate(i) \
2849 private(j, h, ii, hh, \
2850 xi, yi, zi, xj, yj, zj, threadNr, \
2851 dist, ang, icell, jcell, \
2852 grp, ogrp, ai, aj, xjj, yjj, zjj, \
2853 ihb, resdist, \
2854 k, bTric, \
2855 bEdge_xjj, bEdge_yjj) \
2856 default(shared)
2857 { /* Start of parallel region */
2858 if (bOMP)
2860 threadNr = gmx_omp_get_thread_num();
2865 bTric = bBox && TRICLINIC(box);
2867 if (bOMP)
2871 sync_hbdata(p_hb[threadNr], nframes);
2873 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
2875 #pragma omp single
2879 build_grid(hb, x, x[shatom], bBox, box, hbox, (rcut > r2cut) ? rcut : r2cut,
2880 rshell, ngrid, grid);
2881 reset_nhbonds(&(hb->d));
2883 if (debug && bDebug)
2885 dump_grid(debug, ngrid, grid);
2888 add_frames(hb, nframes);
2889 init_hbframe(hb, nframes, output_env_conv_time(oenv, t));
2891 if (hb->bDAnr)
2893 count_da_grid(ngrid, grid, hb->danr[nframes]);
2896 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
2897 } /* omp single */
2899 if (bOMP)
2901 p_hb[threadNr]->time = hb->time; /* This pointer may have changed. */
2904 if (bSelected)
2907 #pragma omp single
2911 /* Do not parallelize this just yet. */
2912 /* int ii; */
2913 for (ii = 0; (ii < nsel); ii++)
2915 int dd = index[0][i];
2916 int aa = index[0][i+2];
2917 /* int */ hh = index[0][i+1];
2918 ihb = is_hbond(hb, ii, ii, dd, aa, rcut, r2cut, ccut, x, bBox, box,
2919 hbox, &dist, &ang, bDA, &h, bContact, bMerge);
2921 if (ihb)
2923 /* add to index if not already there */
2924 /* Add a hbond */
2925 add_hbond(hb, dd, aa, hh, ii, ii, nframes, bMerge, ihb, bContact);
2929 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
2930 } /* omp single */
2931 } /* if (bSelected) */
2932 else
2934 /* The outer grid loop will have to do for now. */
2935 #pragma omp for schedule(dynamic)
2936 for (xi = 0; xi < ngrid[XX]; xi++)
2940 for (yi = 0; (yi < ngrid[YY]); yi++)
2942 for (zi = 0; (zi < ngrid[ZZ]); zi++)
2945 /* loop over donor groups gr0 (always) and gr1 (if necessary) */
2946 for (grp = gr0; (grp <= (bTwo ? gr1 : gr0)); grp++)
2948 icell = &(grid[zi][yi][xi].d[grp]);
2950 if (bTwo)
2952 ogrp = 1-grp;
2954 else
2956 ogrp = grp;
2959 /* loop over all hydrogen atoms from group (grp)
2960 * in this gridcell (icell)
2962 for (ai = 0; (ai < icell->nr); ai++)
2964 i = icell->atoms[ai];
2966 /* loop over all adjacent gridcells (xj,yj,zj) */
2967 for (zjj = grid_loop_begin(ngrid[ZZ], zi, bTric, FALSE);
2968 zjj <= grid_loop_end(ngrid[ZZ], zi, bTric, FALSE);
2969 zjj++)
2971 zj = grid_mod(zjj, ngrid[ZZ]);
2972 bEdge_yjj = (zj == 0) || (zj == ngrid[ZZ] - 1);
2973 for (yjj = grid_loop_begin(ngrid[YY], yi, bTric, bEdge_yjj);
2974 yjj <= grid_loop_end(ngrid[YY], yi, bTric, bEdge_yjj);
2975 yjj++)
2977 yj = grid_mod(yjj, ngrid[YY]);
2978 bEdge_xjj =
2979 (yj == 0) || (yj == ngrid[YY] - 1) ||
2980 (zj == 0) || (zj == ngrid[ZZ] - 1);
2981 for (xjj = grid_loop_begin(ngrid[XX], xi, bTric, bEdge_xjj);
2982 xjj <= grid_loop_end(ngrid[XX], xi, bTric, bEdge_xjj);
2983 xjj++)
2985 xj = grid_mod(xjj, ngrid[XX]);
2986 jcell = &(grid[zj][yj][xj].a[ogrp]);
2987 /* loop over acceptor atoms from other group (ogrp)
2988 * in this adjacent gridcell (jcell)
2990 for (aj = 0; (aj < jcell->nr); aj++)
2992 j = jcell->atoms[aj];
2994 /* check if this once was a h-bond */
2995 ihb = is_hbond(__HBDATA, grp, ogrp, i, j, rcut, r2cut, ccut, x, bBox, box,
2996 hbox, &dist, &ang, bDA, &h, bContact, bMerge);
2998 if (ihb)
3000 /* add to index if not already there */
3001 /* Add a hbond */
3002 add_hbond(__HBDATA, i, j, h, grp, ogrp, nframes, bMerge, ihb, bContact);
3004 /* make angle and distance distributions */
3005 if (ihb == hbHB && !bContact)
3007 if (dist > rcut)
3009 gmx_fatal(FARGS, "distance is higher than what is allowed for an hbond: %f", dist);
3011 ang *= RAD2DEG;
3012 __ADIST[static_cast<int>( ang/abin)]++;
3013 __RDIST[static_cast<int>(dist/rbin)]++;
3014 if (!bTwo)
3016 if (donor_index(&hb->d, grp, i) == NOTSET)
3018 gmx_fatal(FARGS, "Invalid donor %d", i);
3020 if (acceptor_index(&hb->a, ogrp, j) == NOTSET)
3022 gmx_fatal(FARGS, "Invalid acceptor %d", j);
3024 resdist = std::abs(top.atoms.atom[i].resind-top.atoms.atom[j].resind);
3025 if (resdist >= max_hx)
3027 resdist = max_hx-1;
3029 __HBDATA->nhx[nframes][resdist]++;
3034 } /* for aj */
3035 } /* for xjj */
3036 } /* for yjj */
3037 } /* for zjj */
3038 } /* for ai */
3039 } /* for grp */
3040 } /* for xi,yi,zi */
3043 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
3045 } /* if (bSelected) {...} else */
3048 /* Better wait for all threads to finnish using x[] before updating it. */
3049 k = nframes;
3050 #pragma omp barrier
3051 #pragma omp critical
3055 /* Sum up histograms and counts from p_hb[] into hb */
3056 if (bOMP)
3058 hb->nhb[k] += p_hb[threadNr]->nhb[k];
3059 hb->ndist[k] += p_hb[threadNr]->ndist[k];
3060 for (j = 0; j < max_hx; j++)
3062 hb->nhx[k][j] += p_hb[threadNr]->nhx[k][j];
3066 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
3069 /* Here are a handful of single constructs
3070 * to share the workload a bit. The most
3071 * important one is of course the last one,
3072 * where there's a potential bottleneck in form
3073 * of slow I/O. */
3074 #pragma omp barrier
3075 #pragma omp single
3079 analyse_donor_properties(donor_properties, hb, k, t);
3081 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
3084 #pragma omp single
3088 if (fpnhb)
3090 do_nhb_dist(fpnhb, hb, t);
3093 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
3096 #pragma omp single
3100 trrStatus = (read_next_x(oenv, status, &t, x, box));
3101 nframes++;
3103 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
3106 #pragma omp barrier
3108 while (trrStatus);
3110 if (bOMP)
3112 #pragma omp critical
3114 hb->nrhb += p_hb[threadNr]->nrhb;
3115 hb->nrdist += p_hb[threadNr]->nrdist;
3118 /* Free parallel datastructures */
3119 sfree(p_hb[threadNr]->nhb);
3120 sfree(p_hb[threadNr]->ndist);
3121 sfree(p_hb[threadNr]->nhx);
3123 #pragma omp for
3124 for (i = 0; i < nabin; i++)
3128 for (j = 0; j < actual_nThreads; j++)
3131 adist[i] += p_adist[j][i];
3134 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
3136 #pragma omp for
3137 for (i = 0; i <= nrbin; i++)
3141 for (j = 0; j < actual_nThreads; j++)
3143 rdist[i] += p_rdist[j][i];
3146 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
3149 sfree(p_adist[threadNr]);
3150 sfree(p_rdist[threadNr]);
3152 } /* End of parallel region */
3153 if (bOMP)
3155 sfree(p_adist);
3156 sfree(p_rdist);
3159 if (nframes < 2 && (opt2bSet("-ac", NFILE, fnm) || opt2bSet("-life", NFILE, fnm)))
3161 gmx_fatal(FARGS, "Cannot calculate autocorrelation of life times with less than two frames");
3164 free_grid(ngrid, &grid);
3166 close_trx(status);
3168 if (donor_properties)
3170 xvgrclose(donor_properties);
3173 if (fpnhb)
3175 xvgrclose(fpnhb);
3178 /* Compute maximum possible number of different hbonds */
3179 if (maxnhb > 0)
3181 max_nhb = maxnhb;
3183 else
3185 max_nhb = 0.5*(hb->d.nrd*hb->a.nra);
3188 if (bHBmap)
3190 if (hb->nrhb == 0)
3192 printf("No %s found!!\n", bContact ? "contacts" : "hydrogen bonds");
3194 else
3196 printf("Found %d different %s in trajectory\n"
3197 "Found %d different atom-pairs within %s distance\n",
3198 hb->nrhb, bContact ? "contacts" : "hydrogen bonds",
3199 hb->nrdist, (r2cut > 0) ? "second cut-off" : "hydrogen bonding");
3201 if (bMerge)
3203 merge_hb(hb, bTwo, bContact);
3206 if (opt2bSet("-hbn", NFILE, fnm))
3208 dump_hbmap(hb, NFILE, fnm, bTwo, bContact, isize, index, grpnames, &top.atoms);
3211 /* Moved the call to merge_hb() to a line BEFORE dump_hbmap
3212 * to make the -hbn and -hmb output match eachother.
3213 * - Erik Marklund, May 30, 2006 */
3216 /* Print out number of hbonds and distances */
3217 aver_nhb = 0;
3218 aver_dist = 0;
3219 fp = xvgropen(opt2fn("-num", NFILE, fnm), bContact ? "Contacts" :
3220 "Hydrogen Bonds", output_env_get_xvgr_tlabel(oenv), "Number", oenv);
3221 snew(leg, 2);
3222 snew(leg[0], STRLEN);
3223 snew(leg[1], STRLEN);
3224 sprintf(leg[0], "%s", bContact ? "Contacts" : "Hydrogen bonds");
3225 sprintf(leg[1], "Pairs within %g nm", (r2cut > 0) ? r2cut : rcut);
3226 xvgr_legend(fp, 2, leg, oenv);
3227 sfree(leg[1]);
3228 sfree(leg[0]);
3229 sfree(leg);
3230 for (i = 0; (i < nframes); i++)
3232 fprintf(fp, "%10g %10d %10d\n", hb->time[i], hb->nhb[i], hb->ndist[i]);
3233 aver_nhb += hb->nhb[i];
3234 aver_dist += hb->ndist[i];
3236 xvgrclose(fp);
3237 aver_nhb /= nframes;
3238 aver_dist /= nframes;
3239 /* Print HB distance distribution */
3240 if (opt2bSet("-dist", NFILE, fnm))
3242 int sum;
3244 sum = 0;
3245 for (i = 0; i < nrbin; i++)
3247 sum += rdist[i];
3250 fp = xvgropen(opt2fn("-dist", NFILE, fnm),
3251 "Hydrogen Bond Distribution",
3252 bDA ?
3253 "Donor - Acceptor Distance (nm)" :
3254 "Hydrogen - Acceptor Distance (nm)", "", oenv);
3255 for (i = 0; i < nrbin; i++)
3257 fprintf(fp, "%10g %10g\n", (i+0.5)*rbin, rdist[i]/(rbin*sum));
3259 xvgrclose(fp);
3262 /* Print HB angle distribution */
3263 if (opt2bSet("-ang", NFILE, fnm))
3265 long sum;
3267 sum = 0;
3268 for (i = 0; i < nabin; i++)
3270 sum += adist[i];
3273 fp = xvgropen(opt2fn("-ang", NFILE, fnm),
3274 "Hydrogen Bond Distribution",
3275 "Hydrogen - Donor - Acceptor Angle (\\SO\\N)", "", oenv);
3276 for (i = 0; i < nabin; i++)
3278 fprintf(fp, "%10g %10g\n", (i+0.5)*abin, adist[i]/(abin*sum));
3280 xvgrclose(fp);
3283 /* Print HB in alpha-helix */
3284 if (opt2bSet("-hx", NFILE, fnm))
3286 fp = xvgropen(opt2fn("-hx", NFILE, fnm),
3287 "Hydrogen Bonds", output_env_get_xvgr_tlabel(oenv), "Count", oenv);
3288 xvgr_legend(fp, NRHXTYPES, hxtypenames, oenv);
3289 for (i = 0; i < nframes; i++)
3291 fprintf(fp, "%10g", hb->time[i]);
3292 for (j = 0; j < max_hx; j++)
3294 fprintf(fp, " %6d", hb->nhx[i][j]);
3296 fprintf(fp, "\n");
3298 xvgrclose(fp);
3301 printf("Average number of %s per timeframe %.3f out of %g possible\n",
3302 bContact ? "contacts" : "hbonds",
3303 bContact ? aver_dist : aver_nhb, max_nhb);
3305 /* Do Autocorrelation etc. */
3306 if (hb->bHBmap)
3309 Added support for -contact in ac and hbm calculations below.
3310 - Erik Marklund, May 29, 2006
3312 if (opt2bSet("-ac", NFILE, fnm) || opt2bSet("-life", NFILE, fnm))
3314 please_cite(stdout, "Spoel2006b");
3316 if (opt2bSet("-ac", NFILE, fnm))
3318 do_hbac(opt2fn("-ac", NFILE, fnm), hb, nDump,
3319 bMerge, bContact, fit_start, temp, r2cut > 0, oenv,
3320 nThreads);
3322 if (opt2bSet("-life", NFILE, fnm))
3324 do_hblife(opt2fn("-life", NFILE, fnm), hb, bMerge, bContact, oenv);
3326 if (opt2bSet("-hbm", NFILE, fnm))
3328 t_matrix mat;
3329 int id, ia, hh, x, y;
3330 mat.flags = 0;
3332 if ((nframes > 0) && (hb->nrhb > 0))
3334 mat.nx = nframes;
3335 mat.ny = hb->nrhb;
3337 mat.matrix.resize(mat.nx, mat.ny);
3338 for (auto &value : mat.matrix.toArrayRef())
3340 value = 0;
3342 y = 0;
3343 for (id = 0; (id < hb->d.nrd); id++)
3345 for (ia = 0; (ia < hb->a.nra); ia++)
3347 for (hh = 0; (hh < hb->maxhydro); hh++)
3349 if (hb->hbmap[id][ia])
3351 if (ISHB(hb->hbmap[id][ia]->history[hh]))
3353 for (x = 0; (x <= hb->hbmap[id][ia]->nframes); x++)
3355 int nn0 = hb->hbmap[id][ia]->n0;
3356 range_check(y, 0, mat.ny);
3357 mat.matrix(x+nn0, y) = static_cast<t_matelmt>(is_hb(hb->hbmap[id][ia]->h[hh], x));
3359 y++;
3365 std::copy(hb->time, hb->time + mat.nx, mat.axis_x.begin());
3366 mat.axis_y.resize(mat.ny);
3367 std::iota(mat.axis_y.begin(), mat.axis_y.end(), 0);
3368 mat.title = (bContact ? "Contact Existence Map" :
3369 "Hydrogen Bond Existence Map");
3370 mat.legend = bContact ? "Contacts" : "Hydrogen Bonds";
3371 mat.label_x = output_env_get_xvgr_tlabel(oenv);
3372 mat.label_y = bContact ? "Contact Index" : "Hydrogen Bond Index";
3373 mat.bDiscrete = true;
3374 mat.map.resize(2);
3375 for (auto &m : mat.map)
3377 m.code.c1 = hbmap[i];
3378 m.desc = hbdesc[i];
3379 m.rgb = hbrgb[i];
3381 fp = opt2FILE("-hbm", NFILE, fnm, "w");
3382 write_xpm_m(fp, mat);
3383 gmx_ffclose(fp);
3385 else
3387 fprintf(stderr, "No hydrogen bonds/contacts found. No hydrogen bond map will be printed.\n");
3392 if (hb->bDAnr)
3394 int i, j, nleg;
3395 char **legnames;
3396 char buf[STRLEN];
3398 #define USE_THIS_GROUP(j) ( ((j) == gr0) || (bTwo && ((j) == gr1)) )
3400 fp = xvgropen(opt2fn("-dan", NFILE, fnm),
3401 "Donors and Acceptors", output_env_get_xvgr_tlabel(oenv), "Count", oenv);
3402 nleg = (bTwo ? 2 : 1)*2;
3403 snew(legnames, nleg);
3404 i = 0;
3405 for (j = 0; j < grNR; j++)
3407 if (USE_THIS_GROUP(j) )
3409 sprintf(buf, "Donors %s", grpnames[j]);
3410 legnames[i++] = gmx_strdup(buf);
3411 sprintf(buf, "Acceptors %s", grpnames[j]);
3412 legnames[i++] = gmx_strdup(buf);
3415 if (i != nleg)
3417 gmx_incons("number of legend entries");
3419 xvgr_legend(fp, nleg, legnames, oenv);
3420 for (i = 0; i < nframes; i++)
3422 fprintf(fp, "%10g", hb->time[i]);
3423 for (j = 0; (j < grNR); j++)
3425 if (USE_THIS_GROUP(j) )
3427 fprintf(fp, " %6d", hb->danr[i][j]);
3430 fprintf(fp, "\n");
3432 xvgrclose(fp);
3435 return 0;