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, 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.
47 #include "gromacs/commandline/pargs.h"
48 #include "gromacs/commandline/viewit.h"
49 #include "gromacs/correlationfunctions/autocorr.h"
50 #include "gromacs/correlationfunctions/crosscorr.h"
51 #include "gromacs/correlationfunctions/expfit.h"
52 #include "gromacs/correlationfunctions/integrate.h"
53 #include "gromacs/fileio/matio.h"
54 #include "gromacs/fileio/tpxio.h"
55 #include "gromacs/fileio/trxio.h"
56 #include "gromacs/fileio/xvgr.h"
57 #include "gromacs/gmxana/gmx_ana.h"
58 #include "gromacs/gmxana/gstat.h"
59 #include "gromacs/math/functions.h"
60 #include "gromacs/math/units.h"
61 #include "gromacs/math/vec.h"
62 #include "gromacs/mdtypes/inputrec.h"
63 #include "gromacs/pbcutil/pbc.h"
64 #include "gromacs/topology/ifunc.h"
65 #include "gromacs/topology/index.h"
66 #include "gromacs/topology/topology.h"
67 #include "gromacs/utility/arraysize.h"
68 #include "gromacs/utility/cstringutil.h"
69 #include "gromacs/utility/exceptions.h"
70 #include "gromacs/utility/fatalerror.h"
71 #include "gromacs/utility/futil.h"
72 #include "gromacs/utility/gmxomp.h"
73 #include "gromacs/utility/pleasecite.h"
74 #include "gromacs/utility/programcontext.h"
75 #include "gromacs/utility/smalloc.h"
76 #include "gromacs/utility/snprintf.h"
79 typedef int t_hx
[max_hx
];
80 #define NRHXTYPES max_hx
81 static const char *hxtypenames
[NRHXTYPES
] =
82 {"n-n", "n-n+1", "n-n+2", "n-n+3", "n-n+4", "n-n+5", "n-n>6"};
85 static const int NOTSET
= -49297;
87 /* -----------------------------------------*/
93 hbNo
, hbDist
, hbHB
, hbNR
, hbR2
95 static const unsigned char c_acceptorMask
= (1 << 0);
96 static const unsigned char c_donorMask
= (1 << 1);
97 static const unsigned char c_inGroupMask
= (1 << 2);
100 static const char *grpnames
[grNR
] = {"0", "1", "I" };
102 static gmx_bool bDebug
= FALSE
;
107 #define HB_YESINS (HB_YES|HB_INS)
111 #define ISHB(h) ((h) & 2)
112 #define ISDIST(h) ((h) & 1)
113 #define ISDIST2(h) ((h) & 4)
114 #define ISACC(h) ((h) & c_acceptorMask)
115 #define ISDON(h) ((h) & c_donorMask)
116 #define ISINGRP(h) ((h) & c_inGroupMask)
129 typedef int t_icell
[grNR
];
130 typedef int h_id
[MAXHYDRO
];
133 int history
[MAXHYDRO
];
134 /* Has this hbond existed ever? If so as hbDist or hbHB or both.
135 * Result is stored as a bitmap (1 = hbDist) || (2 = hbHB)
137 /* Bitmask array which tells whether a hbond is present
138 * at a given time. Either of these may be NULL
140 int n0
; /* First frame a HB was found */
141 int nframes
, maxframes
; /* Amount of frames in this hbond */
144 /* See Xu and Berne, JPCB 105 (2001), p. 11929. We define the
145 * function g(t) = [1-h(t)] H(t) where H(t) is one when the donor-
146 * acceptor distance is less than the user-specified distance (typically
153 int *acc
; /* Atom numbers of the acceptors */
154 int *grp
; /* Group index */
155 int *aptr
; /* Map atom number to acceptor index */
160 int *don
; /* Atom numbers of the donors */
161 int *grp
; /* Group index */
162 int *dptr
; /* Map atom number to donor index */
163 int *nhydro
; /* Number of hydrogens for each donor */
164 h_id
*hydro
; /* The atom numbers of the hydrogens */
165 h_id
*nhbonds
; /* The number of HBs per H at current */
169 gmx_bool bHBmap
, bDAnr
;
171 /* The following arrays are nframes long */
172 int nframes
, max_frames
, maxhydro
;
178 /* These structures are initialized from the topology at start up */
181 /* This holds a matrix with all possible hydrogen bonds */
186 /* Changed argument 'bMerge' into 'oneHB' below,
187 * since -contact should cause maxhydro to be 1,
189 * - Erik Marklund May 29, 2006
192 static t_hbdata
*mk_hbdata(gmx_bool bHBmap
, gmx_bool bDAnr
, gmx_bool oneHB
)
197 hb
->wordlen
= 8*sizeof(unsigned int);
206 hb
->maxhydro
= MAXHYDRO
;
211 static void mk_hbmap(t_hbdata
*hb
)
215 snew(hb
->hbmap
, hb
->d
.nrd
);
216 for (i
= 0; (i
< hb
->d
.nrd
); i
++)
218 snew(hb
->hbmap
[i
], hb
->a
.nra
);
219 if (hb
->hbmap
[i
] == nullptr)
221 gmx_fatal(FARGS
, "Could not allocate enough memory for hbmap");
223 for (j
= 0; j
< hb
->a
.nra
; j
++)
225 hb
->hbmap
[i
][j
] = nullptr;
230 static void add_frames(t_hbdata
*hb
, int nframes
)
232 if (nframes
>= hb
->max_frames
)
234 hb
->max_frames
+= 4096;
235 srenew(hb
->time
, hb
->max_frames
);
236 srenew(hb
->nhb
, hb
->max_frames
);
237 srenew(hb
->ndist
, hb
->max_frames
);
238 srenew(hb
->n_bound
, hb
->max_frames
);
239 srenew(hb
->nhx
, hb
->max_frames
);
242 srenew(hb
->danr
, hb
->max_frames
);
245 hb
->nframes
= nframes
;
248 #define OFFSET(frame) ((frame) / 32)
249 #define MASK(frame) (1 << ((frame) % 32))
251 static void _set_hb(unsigned int hbexist
[], unsigned int frame
, gmx_bool bValue
)
255 hbexist
[OFFSET(frame
)] |= MASK(frame
);
259 hbexist
[OFFSET(frame
)] &= ~MASK(frame
);
263 static gmx_bool
is_hb(const unsigned int hbexist
[], int frame
)
265 return ((hbexist
[OFFSET(frame
)] & MASK(frame
)) != 0) ? 1 : 0;
268 static void set_hb(t_hbdata
*hb
, int id
, int ih
, int ia
, int frame
, int ihb
)
270 unsigned int *ghptr
= nullptr;
274 ghptr
= hb
->hbmap
[id
][ia
]->h
[ih
];
276 else if (ihb
== hbDist
)
278 ghptr
= hb
->hbmap
[id
][ia
]->g
[ih
];
282 gmx_fatal(FARGS
, "Incomprehensible iValue %d in set_hb", ihb
);
285 _set_hb(ghptr
, frame
-hb
->hbmap
[id
][ia
]->n0
, TRUE
);
288 static void add_ff(t_hbdata
*hbd
, int id
, int h
, int ia
, int frame
, int ihb
)
291 t_hbond
*hb
= hbd
->hbmap
[id
][ia
];
292 int maxhydro
= std::min(hbd
->maxhydro
, hbd
->d
.nhydro
[id
]);
293 int wlen
= hbd
->wordlen
;
299 hb
->maxframes
= delta
;
300 for (i
= 0; (i
< maxhydro
); i
++)
302 snew(hb
->h
[i
], hb
->maxframes
/wlen
);
303 snew(hb
->g
[i
], hb
->maxframes
/wlen
);
308 hb
->nframes
= frame
-hb
->n0
;
309 /* We need a while loop here because hbonds may be returning
312 while (hb
->nframes
>= hb
->maxframes
)
314 n
= hb
->maxframes
+ delta
;
315 for (i
= 0; (i
< maxhydro
); i
++)
317 srenew(hb
->h
[i
], n
/wlen
);
318 srenew(hb
->g
[i
], n
/wlen
);
319 for (j
= hb
->maxframes
/wlen
; (j
< n
/wlen
); j
++)
331 set_hb(hbd
, id
, h
, ia
, frame
, ihb
);
336 static void inc_nhbonds(t_donors
*ddd
, int d
, int h
)
339 int dptr
= ddd
->dptr
[d
];
341 for (j
= 0; (j
< ddd
->nhydro
[dptr
]); j
++)
343 if (ddd
->hydro
[dptr
][j
] == h
)
345 ddd
->nhbonds
[dptr
][j
]++;
349 if (j
== ddd
->nhydro
[dptr
])
351 gmx_fatal(FARGS
, "No such hydrogen %d on donor %d\n", h
+1, d
+1);
355 static int _acceptor_index(t_acceptors
*a
, int grp
, int i
,
356 const char *file
, int line
)
360 if (a
->grp
[ai
] != grp
)
364 fprintf(debug
, "Acc. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
365 ai
, a
->grp
[ai
], grp
, file
, line
);
374 #define acceptor_index(a, grp, i) _acceptor_index(a, grp, i, __FILE__, __LINE__)
376 static int _donor_index(t_donors
*d
, int grp
, int i
, const char *file
, int line
)
385 if (d
->grp
[di
] != grp
)
389 fprintf(debug
, "Don. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
390 di
, d
->grp
[di
], grp
, file
, line
);
399 #define donor_index(d, grp, i) _donor_index(d, grp, i, __FILE__, __LINE__)
401 static gmx_bool
isInterchangable(t_hbdata
*hb
, int d
, int a
, int grpa
, int grpd
)
403 /* g_hbond doesn't allow overlapping groups */
409 donor_index(&hb
->d
, grpd
, a
) != NOTSET
410 && acceptor_index(&hb
->a
, grpa
, d
) != NOTSET
;
414 static void add_hbond(t_hbdata
*hb
, int d
, int a
, int h
, int grpd
, int grpa
,
415 int frame
, gmx_bool bMerge
, int ihb
, gmx_bool bContact
)
418 gmx_bool daSwap
= FALSE
;
420 if ((id
= hb
->d
.dptr
[d
]) == NOTSET
)
422 gmx_fatal(FARGS
, "No donor atom %d", d
+1);
424 else if (grpd
!= hb
->d
.grp
[id
])
426 gmx_fatal(FARGS
, "Inconsistent donor groups, %d instead of %d, atom %d",
427 grpd
, hb
->d
.grp
[id
], d
+1);
429 if ((ia
= hb
->a
.aptr
[a
]) == NOTSET
)
431 gmx_fatal(FARGS
, "No acceptor atom %d", a
+1);
433 else if (grpa
!= hb
->a
.grp
[ia
])
435 gmx_fatal(FARGS
, "Inconsistent acceptor groups, %d instead of %d, atom %d",
436 grpa
, hb
->a
.grp
[ia
], a
+1);
442 if (isInterchangable(hb
, d
, a
, grpd
, grpa
) && d
> a
)
443 /* Then swap identity so that the id of d is lower then that of a.
445 * This should really be redundant by now, as is_hbond() now ought to return
446 * hbNo in the cases where this conditional is TRUE. */
453 /* Now repeat donor/acc check. */
454 if ((id
= hb
->d
.dptr
[d
]) == NOTSET
)
456 gmx_fatal(FARGS
, "No donor atom %d", d
+1);
458 else if (grpd
!= hb
->d
.grp
[id
])
460 gmx_fatal(FARGS
, "Inconsistent donor groups, %d instead of %d, atom %d",
461 grpd
, hb
->d
.grp
[id
], d
+1);
463 if ((ia
= hb
->a
.aptr
[a
]) == NOTSET
)
465 gmx_fatal(FARGS
, "No acceptor atom %d", a
+1);
467 else if (grpa
!= hb
->a
.grp
[ia
])
469 gmx_fatal(FARGS
, "Inconsistent acceptor groups, %d instead of %d, atom %d",
470 grpa
, hb
->a
.grp
[ia
], a
+1);
477 /* Loop over hydrogens to find which hydrogen is in this particular HB */
478 if ((ihb
== hbHB
) && !bMerge
&& !bContact
)
480 for (k
= 0; (k
< hb
->d
.nhydro
[id
]); k
++)
482 if (hb
->d
.hydro
[id
][k
] == h
)
487 if (k
== hb
->d
.nhydro
[id
])
489 gmx_fatal(FARGS
, "Donor %d does not have hydrogen %d (a = %d)",
505 if (hb
->hbmap
[id
][ia
] == nullptr)
507 snew(hb
->hbmap
[id
][ia
], 1);
508 snew(hb
->hbmap
[id
][ia
]->h
, hb
->maxhydro
);
509 snew(hb
->hbmap
[id
][ia
]->g
, hb
->maxhydro
);
511 add_ff(hb
, id
, k
, ia
, frame
, ihb
);
513 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
517 /* Strange construction with frame >=0 is a relic from old code
518 * for selected hbond analysis. It may be necessary again if that
519 * is made to work again.
523 hh
= hb
->hbmap
[id
][ia
]->history
[k
];
529 hb
->hbmap
[id
][ia
]->history
[k
] = hh
| 2;
540 hb
->hbmap
[id
][ia
]->history
[k
] = hh
| 1;
564 if (bMerge
&& daSwap
)
566 h
= hb
->d
.hydro
[id
][0];
568 /* Increment number if HBonds per H */
569 if (ihb
== hbHB
&& !bContact
)
571 inc_nhbonds(&(hb
->d
), d
, h
);
575 static char *mkatomname(const t_atoms
*atoms
, int i
)
580 rnr
= atoms
->atom
[i
].resind
;
581 sprintf(buf
, "%4s%d%-4s",
582 *atoms
->resinfo
[rnr
].name
, atoms
->resinfo
[rnr
].nr
, *atoms
->atomname
[i
]);
587 static void gen_datable(int *index
, int isize
, unsigned char *datable
, int natoms
)
589 /* Generates table of all atoms and sets the ingroup bit for atoms in index[] */
592 for (i
= 0; i
< isize
; i
++)
594 if (index
[i
] >= natoms
)
596 gmx_fatal(FARGS
, "Atom has index %d larger than number of atoms %d.", index
[i
], natoms
);
598 datable
[index
[i
]] |= c_inGroupMask
;
602 static void clear_datable_grp(unsigned char *datable
, int size
)
604 /* Clears group information from the table */
608 for (i
= 0; i
< size
; i
++)
610 datable
[i
] &= ~c_inGroupMask
;
615 static void add_acc(t_acceptors
*a
, int ia
, int grp
)
617 if (a
->nra
>= a
->max_nra
)
620 srenew(a
->acc
, a
->max_nra
);
621 srenew(a
->grp
, a
->max_nra
);
623 a
->grp
[a
->nra
] = grp
;
624 a
->acc
[a
->nra
++] = ia
;
627 static void search_acceptors(const t_topology
*top
, int isize
,
628 const int *index
, t_acceptors
*a
, int grp
,
630 gmx_bool bContact
, gmx_bool bDoIt
, unsigned char *datable
)
636 for (i
= 0; (i
< isize
); i
++)
640 (((*top
->atoms
.atomname
[n
])[0] == 'O') ||
641 (bNitAcc
&& ((*top
->atoms
.atomname
[n
])[0] == 'N')))) &&
644 datable
[n
] |= c_acceptorMask
;
649 snew(a
->aptr
, top
->atoms
.nr
);
650 for (i
= 0; (i
< top
->atoms
.nr
); i
++)
654 for (i
= 0; (i
< a
->nra
); i
++)
656 a
->aptr
[a
->acc
[i
]] = i
;
660 static void add_h2d(int id
, int ih
, t_donors
*ddd
)
664 for (i
= 0; (i
< ddd
->nhydro
[id
]); i
++)
666 if (ddd
->hydro
[id
][i
] == ih
)
668 printf("Hm. This isn't the first time I found this donor (%d,%d)\n",
673 if (i
== ddd
->nhydro
[id
])
675 if (ddd
->nhydro
[id
] >= MAXHYDRO
)
677 gmx_fatal(FARGS
, "Donor %d has more than %d hydrogens!",
678 ddd
->don
[id
], MAXHYDRO
);
680 ddd
->hydro
[id
][i
] = ih
;
685 static void add_dh(t_donors
*ddd
, int id
, int ih
, int grp
, const unsigned char *datable
)
689 if (!datable
|| ISDON(datable
[id
]))
691 if (ddd
->dptr
[id
] == NOTSET
) /* New donor */
703 if (ddd
->nrd
>= ddd
->max_nrd
)
706 srenew(ddd
->don
, ddd
->max_nrd
);
707 srenew(ddd
->nhydro
, ddd
->max_nrd
);
708 srenew(ddd
->hydro
, ddd
->max_nrd
);
709 srenew(ddd
->nhbonds
, ddd
->max_nrd
);
710 srenew(ddd
->grp
, ddd
->max_nrd
);
712 ddd
->don
[ddd
->nrd
] = id
;
713 ddd
->nhydro
[ddd
->nrd
] = 0;
714 ddd
->grp
[ddd
->nrd
] = grp
;
726 printf("Warning: Atom %d is not in the d/a-table!\n", id
);
730 static void search_donors(const t_topology
*top
, int isize
, const int *index
,
731 t_donors
*ddd
, int grp
, gmx_bool bContact
, gmx_bool bDoIt
,
732 unsigned char *datable
)
735 t_functype func_type
;
740 snew(ddd
->dptr
, top
->atoms
.nr
);
741 for (i
= 0; (i
< top
->atoms
.nr
); i
++)
743 ddd
->dptr
[i
] = NOTSET
;
751 for (i
= 0; (i
< isize
); i
++)
753 datable
[index
[i
]] |= c_donorMask
;
754 add_dh(ddd
, index
[i
], -1, grp
, datable
);
760 for (func_type
= 0; (func_type
< F_NRE
); func_type
++)
762 const t_ilist
*interaction
= &(top
->idef
.il
[func_type
]);
763 if (func_type
== F_POSRES
|| func_type
== F_FBPOSRES
)
765 /* The ilist looks strange for posre. Bug in grompp?
766 * We don't need posre interactions for hbonds anyway.*/
769 for (i
= 0; i
< interaction
->nr
;
770 i
+= interaction_function
[top
->idef
.functype
[interaction
->iatoms
[i
]]].nratoms
+1)
773 if (func_type
!= top
->idef
.functype
[interaction
->iatoms
[i
]])
775 fprintf(stderr
, "Error in func_type %s",
776 interaction_function
[func_type
].longname
);
780 /* check out this functype */
781 if (func_type
== F_SETTLE
)
783 nr1
= interaction
->iatoms
[i
+1];
784 nr2
= interaction
->iatoms
[i
+2];
785 nr3
= interaction
->iatoms
[i
+3];
787 if (ISINGRP(datable
[nr1
]))
789 if (ISINGRP(datable
[nr2
]))
791 datable
[nr1
] |= c_donorMask
;
792 add_dh(ddd
, nr1
, nr1
+1, grp
, datable
);
794 if (ISINGRP(datable
[nr3
]))
796 datable
[nr1
] |= c_donorMask
;
797 add_dh(ddd
, nr1
, nr1
+2, grp
, datable
);
801 else if (IS_CHEMBOND(func_type
))
803 for (j
= 0; j
< 2; j
++)
805 nr1
= interaction
->iatoms
[i
+1+j
];
806 nr2
= interaction
->iatoms
[i
+2-j
];
807 if ((*top
->atoms
.atomname
[nr1
][0] == 'H') &&
808 ((*top
->atoms
.atomname
[nr2
][0] == 'O') ||
809 (*top
->atoms
.atomname
[nr2
][0] == 'N')) &&
810 ISINGRP(datable
[nr1
]) && ISINGRP(datable
[nr2
]))
812 datable
[nr2
] |= c_donorMask
;
813 add_dh(ddd
, nr2
, nr1
, grp
, datable
);
820 for (func_type
= 0; func_type
< F_NRE
; func_type
++)
822 interaction
= &top
->idef
.il
[func_type
];
823 for (i
= 0; i
< interaction
->nr
;
824 i
+= interaction_function
[top
->idef
.functype
[interaction
->iatoms
[i
]]].nratoms
+1)
827 if (func_type
!= top
->idef
.functype
[interaction
->iatoms
[i
]])
829 gmx_incons("function type in search_donors");
832 if (interaction_function
[func_type
].flags
& IF_VSITE
)
834 nr1
= interaction
->iatoms
[i
+1];
835 if (*top
->atoms
.atomname
[nr1
][0] == 'H')
839 while (!stop
&& ( *top
->atoms
.atomname
[nr2
][0] == 'H'))
850 if (!stop
&& ( ( *top
->atoms
.atomname
[nr2
][0] == 'O') ||
851 ( *top
->atoms
.atomname
[nr2
][0] == 'N') ) &&
852 ISINGRP(datable
[nr1
]) && ISINGRP(datable
[nr2
]))
854 datable
[nr2
] |= c_donorMask
;
855 add_dh(ddd
, nr2
, nr1
, grp
, datable
);
865 static t_gridcell
***init_grid(gmx_bool bBox
, rvec box
[], real rcut
, ivec ngrid
)
872 for (i
= 0; i
< DIM
; i
++)
874 ngrid
[i
] = static_cast<int>(box
[i
][i
]/(1.2*rcut
));
878 if (!bBox
|| (ngrid
[XX
] < 3) || (ngrid
[YY
] < 3) || (ngrid
[ZZ
] < 3) )
880 for (i
= 0; i
< DIM
; i
++)
887 printf("\nWill do grid-seach on %dx%dx%d grid, rcut=%g\n",
888 ngrid
[XX
], ngrid
[YY
], ngrid
[ZZ
], rcut
);
890 snew(grid
, ngrid
[ZZ
]);
891 for (z
= 0; z
< ngrid
[ZZ
]; z
++)
893 snew((grid
)[z
], ngrid
[YY
]);
894 for (y
= 0; y
< ngrid
[YY
]; y
++)
896 snew((grid
)[z
][y
], ngrid
[XX
]);
902 static void reset_nhbonds(t_donors
*ddd
)
906 for (i
= 0; (i
< ddd
->nrd
); i
++)
908 for (j
= 0; (j
< MAXHH
); j
++)
910 ddd
->nhbonds
[i
][j
] = 0;
915 static void pbc_correct_gem(rvec dx
, matrix box
, const rvec hbox
);
916 static void pbc_in_gridbox(rvec dx
, matrix box
);
918 static void build_grid(t_hbdata
*hb
, rvec x
[], rvec xshell
,
919 gmx_bool bBox
, matrix box
, rvec hbox
,
920 real rcut
, real rshell
,
921 ivec ngrid
, t_gridcell
***grid
)
923 int i
, m
, gr
, xi
, yi
, zi
, nr
;
926 rvec invdelta
, dshell
;
928 gmx_bool bDoRshell
, bInShell
;
933 bDoRshell
= (rshell
> 0);
934 rshell2
= gmx::square(rshell
);
937 #define DBB(x) if (debug && bDebug) fprintf(debug, "build_grid, line %d, %s = %d\n", __LINE__,#x, x)
939 for (m
= 0; m
< DIM
; m
++)
941 hbox
[m
] = box
[m
][m
]*0.5;
944 invdelta
[m
] = ngrid
[m
]/box
[m
][m
];
945 if (1/invdelta
[m
] < rcut
)
947 gmx_fatal(FARGS
, "Your computational box has shrunk too much.\n"
948 "%s can not handle this situation, sorry.\n",
949 gmx::getProgramContext().displayName());
961 /* resetting atom counts */
962 for (gr
= 0; (gr
< grNR
); gr
++)
964 for (zi
= 0; zi
< ngrid
[ZZ
]; zi
++)
966 for (yi
= 0; yi
< ngrid
[YY
]; yi
++)
968 for (xi
= 0; xi
< ngrid
[XX
]; xi
++)
970 grid
[zi
][yi
][xi
].d
[gr
].nr
= 0;
971 grid
[zi
][yi
][xi
].a
[gr
].nr
= 0;
977 /* put atoms in grid cells */
978 for (int acc
= 0; acc
< 2; acc
++)
991 for (i
= 0; (i
< nr
); i
++)
993 /* check if we are inside the shell */
994 /* if bDoRshell=FALSE then bInShell=TRUE always */
999 rvec_sub(x
[ad
[i
]], xshell
, dshell
);
1002 gmx_bool bDone
= FALSE
;
1006 for (m
= DIM
-1; m
>= 0 && bInShell
; m
--)
1008 if (dshell
[m
] < -hbox
[m
])
1011 rvec_inc(dshell
, box
[m
]);
1013 if (dshell
[m
] >= hbox
[m
])
1016 dshell
[m
] -= 2*hbox
[m
];
1020 for (m
= DIM
-1; m
>= 0 && bInShell
; m
--)
1022 /* if we're outside the cube, we're outside the sphere also! */
1023 if ( (dshell
[m
] > rshell
) || (-dshell
[m
] > rshell
) )
1029 /* if we're inside the cube, check if we're inside the sphere */
1032 bInShell
= norm2(dshell
) < rshell2
;
1040 pbc_in_gridbox(x
[ad
[i
]], box
);
1042 for (m
= DIM
-1; m
>= 0; m
--)
1043 { /* determine grid index of atom */
1044 grididx
[m
] = static_cast<int>(x
[ad
[i
]][m
]*invdelta
[m
]);
1045 grididx
[m
] = (grididx
[m
]+ngrid
[m
]) % ngrid
[m
];
1052 range_check(gx
, 0, ngrid
[XX
]);
1053 range_check(gy
, 0, ngrid
[YY
]);
1054 range_check(gz
, 0, ngrid
[ZZ
]);
1058 /* add atom to grid cell */
1061 newgrid
= &(grid
[gz
][gy
][gx
].a
[gr
]);
1065 newgrid
= &(grid
[gz
][gy
][gx
].d
[gr
]);
1067 if (newgrid
->nr
>= newgrid
->maxnr
)
1069 newgrid
->maxnr
+= 10;
1070 DBB(newgrid
->maxnr
);
1071 srenew(newgrid
->atoms
, newgrid
->maxnr
);
1074 newgrid
->atoms
[newgrid
->nr
] = ad
[i
];
1082 static void count_da_grid(const ivec ngrid
, t_gridcell
***grid
, t_icell danr
)
1086 for (gr
= 0; (gr
< grNR
); gr
++)
1089 for (zi
= 0; zi
< ngrid
[ZZ
]; zi
++)
1091 for (yi
= 0; yi
< ngrid
[YY
]; yi
++)
1093 for (xi
= 0; xi
< ngrid
[XX
]; xi
++)
1095 danr
[gr
] += grid
[zi
][yi
][xi
].d
[gr
].nr
;
1103 * Without a box, the grid is 1x1x1, so all loops are 1 long.
1104 * With a rectangular box (bTric==FALSE) all loops are 3 long.
1105 * With a triclinic box all loops are 3 long, except when a cell is
1106 * located next to one of the box edges which is not parallel to the
1107 * x/y-plane, in that case all cells in a line or layer are searched.
1108 * This could be implemented slightly more efficient, but the code
1109 * would get much more complicated.
1111 static inline int grid_loop_begin(int n
, int x
, gmx_bool bTric
, gmx_bool bEdge
)
1113 return ((n
== 1) ? x
: bTric
&& bEdge
? 0 : (x
-1));
1115 static inline int grid_loop_end(int n
, int x
, gmx_bool bTric
, gmx_bool bEdge
)
1117 return ((n
== 1) ? x
: bTric
&& bEdge
? (n
-1) : (x
+1));
1119 static inline int grid_mod(int j
, int n
)
1124 static void dump_grid(FILE *fp
, ivec ngrid
, t_gridcell
***grid
)
1126 int gr
, x
, y
, z
, sum
[grNR
];
1128 fprintf(fp
, "grid %dx%dx%d\n", ngrid
[XX
], ngrid
[YY
], ngrid
[ZZ
]);
1129 for (gr
= 0; gr
< grNR
; gr
++)
1132 fprintf(fp
, "GROUP %d (%s)\n", gr
, grpnames
[gr
]);
1133 for (z
= 0; z
< ngrid
[ZZ
]; z
+= 2)
1135 fprintf(fp
, "Z=%d,%d\n", z
, z
+1);
1136 for (y
= 0; y
< ngrid
[YY
]; y
++)
1138 for (x
= 0; x
< ngrid
[XX
]; x
++)
1140 fprintf(fp
, "%3d", grid
[x
][y
][z
].d
[gr
].nr
);
1141 sum
[gr
] += grid
[z
][y
][x
].d
[gr
].nr
;
1142 fprintf(fp
, "%3d", grid
[x
][y
][z
].a
[gr
].nr
);
1143 sum
[gr
] += grid
[z
][y
][x
].a
[gr
].nr
;
1147 if ( (z
+1) < ngrid
[ZZ
])
1149 for (x
= 0; x
< ngrid
[XX
]; x
++)
1151 fprintf(fp
, "%3d", grid
[z
+1][y
][x
].d
[gr
].nr
);
1152 sum
[gr
] += grid
[z
+1][y
][x
].d
[gr
].nr
;
1153 fprintf(fp
, "%3d", grid
[z
+1][y
][x
].a
[gr
].nr
);
1154 sum
[gr
] += grid
[z
+1][y
][x
].a
[gr
].nr
;
1161 fprintf(fp
, "TOTALS:");
1162 for (gr
= 0; gr
< grNR
; gr
++)
1164 fprintf(fp
, " %d=%d", gr
, sum
[gr
]);
1169 /* New GMX record! 5 * in a row. Congratulations!
1170 * Sorry, only four left.
1172 static void free_grid(const ivec ngrid
, t_gridcell
****grid
)
1175 t_gridcell
***g
= *grid
;
1177 for (z
= 0; z
< ngrid
[ZZ
]; z
++)
1179 for (y
= 0; y
< ngrid
[YY
]; y
++)
1189 static void pbc_correct_gem(rvec dx
, matrix box
, const rvec hbox
)
1192 gmx_bool bDone
= FALSE
;
1196 for (m
= DIM
-1; m
>= 0; m
--)
1198 if (dx
[m
] < -hbox
[m
])
1201 rvec_inc(dx
, box
[m
]);
1203 if (dx
[m
] >= hbox
[m
])
1206 rvec_dec(dx
, box
[m
]);
1212 static void pbc_in_gridbox(rvec dx
, matrix box
)
1215 gmx_bool bDone
= FALSE
;
1219 for (m
= DIM
-1; m
>= 0; m
--)
1224 rvec_inc(dx
, box
[m
]);
1226 if (dx
[m
] >= box
[m
][m
])
1229 rvec_dec(dx
, box
[m
]);
1235 /* Added argument r2cut, changed contact and implemented
1236 * use of second cut-off.
1237 * - Erik Marklund, June 29, 2006
1239 static int is_hbond(t_hbdata
*hb
, int grpd
, int grpa
, int d
, int a
,
1240 real rcut
, real r2cut
, real ccut
,
1241 rvec x
[], gmx_bool bBox
, matrix box
, rvec hbox
,
1242 real
*d_ha
, real
*ang
, gmx_bool bDA
, int *hhh
,
1243 gmx_bool bContact
, gmx_bool bMerge
)
1246 rvec r_da
, r_ha
, r_dh
;
1247 real rc2
, r2c2
, rda2
, rha2
, ca
;
1248 gmx_bool HAinrange
= FALSE
; /* If !bDA. Needed for returning hbDist in a correct way. */
1249 gmx_bool daSwap
= FALSE
;
1256 if (((id
= donor_index(&hb
->d
, grpd
, d
)) == NOTSET
) ||
1257 (acceptor_index(&hb
->a
, grpa
, a
) == NOTSET
))
1265 rvec_sub(x
[d
], x
[a
], r_da
);
1266 /* Insert projection code here */
1268 if (bMerge
&& d
> a
&& isInterchangable(hb
, d
, a
, grpd
, grpa
))
1270 /* Then this hbond/contact will be found again, or it has already been found. */
1275 if (d
> a
&& bMerge
&& isInterchangable(hb
, d
, a
, grpd
, grpa
)) /* acceptor is also a donor and vice versa? */
1276 { /* return hbNo; */
1277 daSwap
= TRUE
; /* If so, then their history should be filed with donor and acceptor swapped. */
1279 pbc_correct_gem(r_da
, box
, hbox
);
1281 rda2
= iprod(r_da
, r_da
);
1285 if (daSwap
&& grpa
== grpd
)
1293 else if (rda2
< r2c2
)
1304 if (bDA
&& (rda2
> rc2
))
1309 for (h
= 0; (h
< hb
->d
.nhydro
[id
]); h
++)
1311 hh
= hb
->d
.hydro
[id
][h
];
1315 rvec_sub(x
[hh
], x
[a
], r_ha
);
1318 pbc_correct_gem(r_ha
, box
, hbox
);
1320 rha2
= iprod(r_ha
, r_ha
);
1323 if (bDA
|| (rha2
<= rc2
))
1325 rvec_sub(x
[d
], x
[hh
], r_dh
);
1328 pbc_correct_gem(r_dh
, box
, hbox
);
1335 ca
= cos_angle(r_dh
, r_da
);
1336 /* if angle is smaller, cos is larger */
1340 *d_ha
= std::sqrt(bDA
? rda2
: rha2
);
1341 *ang
= std::acos(ca
);
1346 if (bDA
|| HAinrange
)
1356 /* Merging is now done on the fly, so do_merge is most likely obsolete now.
1357 * Will do some more testing before removing the function entirely.
1358 * - Erik Marklund, MAY 10 2010 */
1359 static void do_merge(t_hbdata
*hb
, int ntmp
,
1360 unsigned int htmp
[], unsigned int gtmp
[],
1361 t_hbond
*hb0
, t_hbond
*hb1
)
1363 /* Here we need to make sure we're treating periodicity in
1364 * the right way for the geminate recombination kinetics. */
1366 int m
, mm
, n00
, n01
, nn0
, nnframes
;
1368 /* Decide where to start from when merging */
1371 nn0
= std::min(n00
, n01
);
1372 nnframes
= std::max(n00
+ hb0
->nframes
, n01
+ hb1
->nframes
) - nn0
;
1373 /* Initiate tmp arrays */
1374 for (m
= 0; (m
< ntmp
); m
++)
1379 /* Fill tmp arrays with values due to first HB */
1380 /* Once again '<' had to be replaced with '<='
1381 to catch the last frame in which the hbond
1383 - Erik Marklund, June 1, 2006 */
1384 for (m
= 0; (m
<= hb0
->nframes
); m
++)
1387 htmp
[mm
] = is_hb(hb0
->h
[0], m
);
1389 for (m
= 0; (m
<= hb0
->nframes
); m
++)
1392 gtmp
[mm
] = is_hb(hb0
->g
[0], m
);
1395 for (m
= 0; (m
<= hb1
->nframes
); m
++)
1398 htmp
[mm
] = htmp
[mm
] || is_hb(hb1
->h
[0], m
);
1399 gtmp
[mm
] = gtmp
[mm
] || is_hb(hb1
->g
[0], m
);
1401 /* Reallocate target array */
1402 if (nnframes
> hb0
->maxframes
)
1404 srenew(hb0
->h
[0], 4+nnframes
/hb
->wordlen
);
1405 srenew(hb0
->g
[0], 4+nnframes
/hb
->wordlen
);
1408 /* Copy temp array to target array */
1409 for (m
= 0; (m
<= nnframes
); m
++)
1411 _set_hb(hb0
->h
[0], m
, htmp
[m
]);
1412 _set_hb(hb0
->g
[0], m
, gtmp
[m
]);
1415 /* Set scalar variables */
1417 hb0
->maxframes
= nnframes
;
1420 static void merge_hb(t_hbdata
*hb
, gmx_bool bTwo
, gmx_bool bContact
)
1422 int i
, inrnew
, indnew
, j
, ii
, jj
, id
, ia
, ntmp
;
1423 unsigned int *htmp
, *gtmp
;
1427 indnew
= hb
->nrdist
;
1429 /* Check whether donors are also acceptors */
1430 printf("Merging hbonds with Acceptor and Donor swapped\n");
1432 ntmp
= 2*hb
->max_frames
;
1435 for (i
= 0; (i
< hb
->d
.nrd
); i
++)
1437 fprintf(stderr
, "\r%d/%d", i
+1, hb
->d
.nrd
);
1440 ii
= hb
->a
.aptr
[id
];
1441 for (j
= 0; (j
< hb
->a
.nra
); j
++)
1444 jj
= hb
->d
.dptr
[ia
];
1445 if ((id
!= ia
) && (ii
!= NOTSET
) && (jj
!= NOTSET
) &&
1446 (!bTwo
|| (hb
->d
.grp
[i
] != hb
->a
.grp
[j
])))
1448 hb0
= hb
->hbmap
[i
][j
];
1449 hb1
= hb
->hbmap
[jj
][ii
];
1450 if (hb0
&& hb1
&& ISHB(hb0
->history
[0]) && ISHB(hb1
->history
[0]))
1452 do_merge(hb
, ntmp
, htmp
, gtmp
, hb0
, hb1
);
1453 if (ISHB(hb1
->history
[0]))
1457 else if (ISDIST(hb1
->history
[0]))
1464 gmx_incons("No contact history");
1468 gmx_incons("Neither hydrogen bond nor distance");
1472 hb1
->h
[0] = nullptr;
1473 hb1
->g
[0] = nullptr;
1474 hb1
->history
[0] = hbNo
;
1479 fprintf(stderr
, "\n");
1480 printf("- Reduced number of hbonds from %d to %d\n", hb
->nrhb
, inrnew
);
1481 printf("- Reduced number of distances from %d to %d\n", hb
->nrdist
, indnew
);
1483 hb
->nrdist
= indnew
;
1488 static void do_nhb_dist(FILE *fp
, t_hbdata
*hb
, real t
)
1490 int i
, j
, k
, n_bound
[MAXHH
], nbtot
;
1492 /* Set array to 0 */
1493 for (k
= 0; (k
< MAXHH
); k
++)
1497 /* Loop over possible donors */
1498 for (i
= 0; (i
< hb
->d
.nrd
); i
++)
1500 for (j
= 0; (j
< hb
->d
.nhydro
[i
]); j
++)
1502 n_bound
[hb
->d
.nhbonds
[i
][j
]]++;
1505 fprintf(fp
, "%12.5e", t
);
1507 for (k
= 0; (k
< MAXHH
); k
++)
1509 fprintf(fp
, " %8d", n_bound
[k
]);
1510 nbtot
+= n_bound
[k
]*k
;
1512 fprintf(fp
, " %8d\n", nbtot
);
1515 static void do_hblife(const char *fn
, t_hbdata
*hb
, gmx_bool bMerge
, gmx_bool bContact
,
1516 const gmx_output_env_t
*oenv
)
1519 const char *leg
[] = { "p(t)", "t p(t)" };
1521 int i
, j
, j0
, k
, m
, nh
, ihb
, ohb
, nhydro
, ndump
= 0;
1522 int nframes
= hb
->nframes
;
1525 double sum
, integral
;
1528 snew(h
, hb
->maxhydro
);
1529 snew(histo
, nframes
+1);
1530 /* Total number of hbonds analyzed here */
1531 for (i
= 0; (i
< hb
->d
.nrd
); i
++)
1533 for (k
= 0; (k
< hb
->a
.nra
); k
++)
1535 hbh
= hb
->hbmap
[i
][k
];
1553 for (m
= 0; (m
< hb
->maxhydro
); m
++)
1557 h
[nhydro
++] = bContact
? hbh
->g
[m
] : hbh
->h
[m
];
1561 for (nh
= 0; (nh
< nhydro
); nh
++)
1566 for (j
= 0; (j
<= hbh
->nframes
); j
++)
1568 ihb
= is_hb(h
[nh
], j
);
1569 if (debug
&& (ndump
< 10))
1571 fprintf(debug
, "%5d %5d\n", j
, ihb
);
1591 fprintf(stderr
, "\n");
1594 fp
= xvgropen(fn
, "Uninterrupted contact lifetime", output_env_get_xvgr_tlabel(oenv
), "()", oenv
);
1598 fp
= xvgropen(fn
, "Uninterrupted hydrogen bond lifetime", output_env_get_xvgr_tlabel(oenv
), "()",
1602 xvgr_legend(fp
, asize(leg
), leg
, oenv
);
1604 while ((j0
> 0) && (histo
[j0
] == 0))
1609 for (i
= 0; (i
<= j0
); i
++)
1613 dt
= hb
->time
[1]-hb
->time
[0];
1616 for (i
= 1; (i
<= j0
); i
++)
1618 t
= hb
->time
[i
] - hb
->time
[0] - 0.5*dt
;
1619 x1
= t
*histo
[i
]/sum
;
1620 fprintf(fp
, "%8.3f %10.3e %10.3e\n", t
, histo
[i
]/sum
, x1
);
1625 printf("%s lifetime = %.2f ps\n", bContact
? "Contact" : "HB", integral
);
1626 printf("Note that the lifetime obtained in this manner is close to useless\n");
1627 printf("Use the -ac option instead and check the Forward lifetime\n");
1628 please_cite(stdout
, "Spoel2006b");
1633 static void dump_ac(t_hbdata
*hb
, gmx_bool oneHB
, int nDump
)
1636 int i
, j
, k
, m
, nd
, ihb
, idist
;
1637 int nframes
= hb
->nframes
;
1645 fp
= gmx_ffopen("debug-ac.xvg", "w");
1646 for (j
= 0; (j
< nframes
); j
++)
1648 fprintf(fp
, "%10.3f", hb
->time
[j
]);
1649 for (i
= nd
= 0; (i
< hb
->d
.nrd
) && (nd
< nDump
); i
++)
1651 for (k
= 0; (k
< hb
->a
.nra
) && (nd
< nDump
); k
++)
1655 hbh
= hb
->hbmap
[i
][k
];
1660 ihb
= is_hb(hbh
->h
[0], j
);
1661 idist
= is_hb(hbh
->g
[0], j
);
1667 for (m
= 0; (m
< hb
->maxhydro
) && !ihb
; m
++)
1669 ihb
= ihb
|| ((hbh
->h
[m
]) && is_hb(hbh
->h
[m
], j
));
1670 idist
= idist
|| ((hbh
->g
[m
]) && is_hb(hbh
->g
[m
], j
));
1672 /* This is not correct! */
1673 /* What isn't correct? -Erik M */
1678 fprintf(fp
, " %1d-%1d", ihb
, idist
);
1688 static real
calc_dg(real tau
, real temp
)
1699 return kbt
*std::log(kbt
*tau
/PLANCK
);
1704 int n0
, n1
, nparams
, ndelta
;
1706 real
*t
, *ct
, *nt
, *kt
, *sigma_ct
, *sigma_nt
, *sigma_kt
;
1709 static real
compute_weighted_rates(int n
, real t
[], real ct
[], real nt
[],
1710 real kt
[], real sigma_ct
[], real sigma_nt
[],
1711 real sigma_kt
[], real
*k
, real
*kp
,
1712 real
*sigma_k
, real
*sigma_kp
,
1718 real kkk
= 0, kkp
= 0, kk2
= 0, kp2
= 0, chi2
;
1723 for (i
= 0; (i
< n
); i
++)
1725 if (t
[i
] >= fit_start
)
1738 tl
.sigma_ct
= sigma_ct
;
1739 tl
.sigma_nt
= sigma_nt
;
1740 tl
.sigma_kt
= sigma_kt
;
1744 chi2
= 0; /*optimize_luzar_parameters(debug, &tl, 1000, 1e-3); */
1746 *kp
= tl
.kkk
[1] = *kp
;
1748 for (j
= 0; (j
< NK
); j
++)
1752 kk2
+= gmx::square(tl
.kkk
[0]);
1753 kp2
+= gmx::square(tl
.kkk
[1]);
1756 *sigma_k
= std::sqrt(kk2
/NK
- gmx::square(kkk
/NK
));
1757 *sigma_kp
= std::sqrt(kp2
/NK
- gmx::square(kkp
/NK
));
1762 void analyse_corr(int n
, real t
[], real ct
[], real nt
[], real kt
[],
1763 real sigma_ct
[], real sigma_nt
[], real sigma_kt
[],
1764 real fit_start
, real temp
)
1767 real k
= 1, kp
= 1, kow
= 1;
1768 real Q
= 0, chi2
, tau_hb
, dtau
, tau_rlx
, e_1
, sigma_k
, sigma_kp
, ddg
;
1769 double tmp
, sn2
= 0, sc2
= 0, sk2
= 0, scn
= 0, sck
= 0, snk
= 0;
1770 gmx_bool bError
= (sigma_ct
!= nullptr) && (sigma_nt
!= nullptr) && (sigma_kt
!= nullptr);
1772 for (i0
= 0; (i0
< n
-2) && ((t
[i0
]-t
[0]) < fit_start
); i0
++)
1778 for (i
= i0
; (i
< n
); i
++)
1780 sc2
+= gmx::square(ct
[i
]);
1781 sn2
+= gmx::square(nt
[i
]);
1782 sk2
+= gmx::square(kt
[i
]);
1787 printf("Hydrogen bond thermodynamics at T = %g K\n", temp
);
1788 tmp
= (sn2
*sc2
-gmx::square(scn
));
1789 if ((tmp
> 0) && (sn2
> 0))
1791 k
= (sn2
*sck
-scn
*snk
)/tmp
;
1792 kp
= (k
*scn
-snk
)/sn2
;
1796 for (i
= i0
; (i
< n
); i
++)
1798 chi2
+= gmx::square(k
*ct
[i
]-kp
*nt
[i
]-kt
[i
]);
1800 compute_weighted_rates(n
, t
, ct
, nt
, kt
, sigma_ct
, sigma_nt
,
1802 &sigma_k
, &sigma_kp
, fit_start
);
1803 Q
= 0; /* quality_of_fit(chi2, 2);*/
1804 ddg
= BOLTZ
*temp
*sigma_k
/k
;
1805 printf("Fitting paramaters chi^2 = %10g, Quality of fit = %10g\n",
1807 printf("The Rate and Delta G are followed by an error estimate\n");
1808 printf("----------------------------------------------------------\n"
1809 "Type Rate (1/ps) Sigma Time (ps) DG (kJ/mol) Sigma\n");
1810 printf("Forward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
1811 k
, sigma_k
, 1/k
, calc_dg(1/k
, temp
), ddg
);
1812 ddg
= BOLTZ
*temp
*sigma_kp
/kp
;
1813 printf("Backward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
1814 kp
, sigma_kp
, 1/kp
, calc_dg(1/kp
, temp
), ddg
);
1819 for (i
= i0
; (i
< n
); i
++)
1821 chi2
+= gmx::square(k
*ct
[i
]-kp
*nt
[i
]-kt
[i
]);
1823 printf("Fitting parameters chi^2 = %10g\nQ = %10g\n",
1825 printf("--------------------------------------------------\n"
1826 "Type Rate (1/ps) Time (ps) DG (kJ/mol) Chi^2\n");
1827 printf("Forward %10.3f %8.3f %10.3f %10g\n",
1828 k
, 1/k
, calc_dg(1/k
, temp
), chi2
);
1829 printf("Backward %10.3f %8.3f %10.3f\n",
1830 kp
, 1/kp
, calc_dg(1/kp
, temp
));
1836 printf("One-way %10.3f %s%8.3f %10.3f\n",
1837 kow
, bError
? " " : "", 1/kow
, calc_dg(1/kow
, temp
));
1841 printf(" - Numerical problems computing HB thermodynamics:\n"
1842 "sc2 = %g sn2 = %g sk2 = %g sck = %g snk = %g scn = %g\n",
1843 sc2
, sn2
, sk2
, sck
, snk
, scn
);
1845 /* Determine integral of the correlation function */
1846 tau_hb
= evaluate_integral(n
, t
, ct
, nullptr, (t
[n
-1]-t
[0])/2, &dtau
);
1847 printf("Integral %10.3f %s%8.3f %10.3f\n", 1/tau_hb
,
1848 bError
? " " : "", tau_hb
, calc_dg(tau_hb
, temp
));
1849 e_1
= std::exp(-1.0);
1850 for (i
= 0; (i
< n
-2); i
++)
1852 if ((ct
[i
] > e_1
) && (ct
[i
+1] <= e_1
))
1859 /* Determine tau_relax from linear interpolation */
1860 tau_rlx
= t
[i
]-t
[0] + (e_1
-ct
[i
])*(t
[i
+1]-t
[i
])/(ct
[i
+1]-ct
[i
]);
1861 printf("Relaxation %10.3f %8.3f %s%10.3f\n", 1/tau_rlx
,
1862 tau_rlx
, bError
? " " : "",
1863 calc_dg(tau_rlx
, temp
));
1868 printf("Correlation functions too short to compute thermodynamics\n");
1872 void compute_derivative(int nn
, const real x
[], const real y
[], real dydx
[])
1876 /* Compute k(t) = dc(t)/dt */
1877 for (j
= 1; (j
< nn
-1); j
++)
1879 dydx
[j
] = (y
[j
+1]-y
[j
-1])/(x
[j
+1]-x
[j
-1]);
1881 /* Extrapolate endpoints */
1882 dydx
[0] = 2*dydx
[1] - dydx
[2];
1883 dydx
[nn
-1] = 2*dydx
[nn
-2] - dydx
[nn
-3];
1886 static void normalizeACF(real
*ct
, real
*gt
, int nhb
, int len
)
1888 real ct_fac
, gt_fac
= 0;
1891 /* Xu and Berne use the same normalization constant */
1899 printf("Normalization for c(t) = %g for gh(t) = %g\n", ct_fac
, gt_fac
);
1900 for (i
= 0; i
< len
; i
++)
1910 static void do_hbac(const char *fn
, t_hbdata
*hb
,
1911 int nDump
, gmx_bool bMerge
, gmx_bool bContact
, real fit_start
,
1912 real temp
, gmx_bool R2
, const gmx_output_env_t
*oenv
,
1916 int i
, j
, k
, m
, ihb
, idist
, n2
, nn
;
1918 const char *legLuzar
[] = {
1919 "Ac\\sfin sys\\v{}\\z{}(t)",
1921 "Cc\\scontact,hb\\v{}\\z{}(t)",
1922 "-dAc\\sfs\\v{}\\z{}/dt"
1924 gmx_bool bNorm
= FALSE
;
1926 real
*rhbex
= nullptr, *ht
, *gt
, *ght
, *dght
, *kt
;
1927 real
*ct
, tail
, tail2
, dtail
, *cct
;
1928 const real tol
= 1e-3;
1929 int nframes
= hb
->nframes
;
1930 unsigned int **h
= nullptr, **g
= nullptr;
1931 int nh
, nhbonds
, nhydro
;
1934 int *dondata
= nullptr;
1937 AC_NONE
, AC_NN
, AC_GEM
, AC_LUZAR
1940 const bool bOMP
= GMX_OPENMP
;
1942 printf("Doing autocorrelation ");
1945 printf("according to the theory of Luzar and Chandler.\n");
1948 /* build hbexist matrix in reals for autocorr */
1949 /* Allocate memory for computing ACF (rhbex) and aggregating the ACF (ct) */
1951 while (n2
< nframes
)
1958 if (acType
!= AC_NN
|| bOMP
)
1960 snew(h
, hb
->maxhydro
);
1961 snew(g
, hb
->maxhydro
);
1964 /* Dump hbonds for debugging */
1965 dump_ac(hb
, bMerge
|| bContact
, nDump
);
1967 /* Total number of hbonds analyzed here */
1970 if (acType
!= AC_LUZAR
&& bOMP
)
1972 nThreads
= std::min((nThreads
<= 0) ? INT_MAX
: nThreads
, gmx_omp_get_max_threads());
1974 gmx_omp_set_num_threads(nThreads
);
1975 snew(dondata
, nThreads
);
1976 for (i
= 0; i
< nThreads
; i
++)
1980 printf("ACF calculations parallelized with OpenMP using %i threads.\n"
1981 "Expect close to linear scaling over this donor-loop.\n", nThreads
);
1983 fprintf(stderr
, "Donors: [thread no]\n");
1986 for (i
= 0; i
< nThreads
; i
++)
1988 snprintf(tmpstr
, 7, "[%i]", i
);
1989 fprintf(stderr
, "%-7s", tmpstr
);
1992 fprintf(stderr
, "\n");
2007 for (i
= 0; (i
< hb
->d
.nrd
); i
++)
2009 for (k
= 0; (k
< hb
->a
.nra
); k
++)
2012 hbh
= hb
->hbmap
[i
][k
];
2016 if (bMerge
|| bContact
)
2018 if (ISHB(hbh
->history
[0]))
2027 for (m
= 0; (m
< hb
->maxhydro
); m
++)
2029 if (bContact
? ISDIST(hbh
->history
[m
]) : ISHB(hbh
->history
[m
]))
2031 g
[nhydro
] = hbh
->g
[m
];
2032 h
[nhydro
] = hbh
->h
[m
];
2038 int nf
= hbh
->nframes
;
2039 for (nh
= 0; (nh
< nhydro
); nh
++)
2041 int nrint
= bContact
? hb
->nrdist
: hb
->nrhb
;
2042 if ((((nhbonds
+1) % 10) == 0) || (nhbonds
+1 == nrint
))
2044 fprintf(stderr
, "\rACF %d/%d", nhbonds
+1, nrint
);
2048 for (j
= 0; (j
< nframes
); j
++)
2052 ihb
= is_hb(h
[nh
], j
);
2053 idist
= is_hb(g
[nh
], j
);
2060 /* For contacts: if a second cut-off is provided, use it,
2061 * otherwise use g(t) = 1-h(t) */
2062 if (!R2
&& bContact
)
2068 gt
[j
] = idist
*(1-ihb
);
2074 /* The autocorrelation function is normalized after summation only */
2075 low_do_autocorr(nullptr, oenv
, nullptr, nframes
, 1, -1, &rhbex
, hb
->time
[1]-hb
->time
[0],
2076 eacNormal
, 1, FALSE
, bNorm
, FALSE
, 0, -1, 0);
2078 /* Cross correlation analysis for thermodynamics */
2079 for (j
= nframes
; (j
< n2
); j
++)
2085 cross_corr(n2
, ht
, gt
, dght
);
2087 for (j
= 0; (j
< nn
); j
++)
2096 fprintf(stderr
, "\n");
2099 normalizeACF(ct
, ght
, static_cast<int>(nhb
), nn
);
2101 /* Determine tail value for statistics */
2104 for (j
= nn
/2; (j
< nn
); j
++)
2107 tail2
+= ct
[j
]*ct
[j
];
2109 tail
/= (nn
- int{nn
/2});
2110 tail2
/= (nn
- int{nn
/2});
2111 dtail
= std::sqrt(tail2
-tail
*tail
);
2113 /* Check whether the ACF is long enough */
2116 printf("\nWARNING: Correlation function is probably not long enough\n"
2117 "because the standard deviation in the tail of C(t) > %g\n"
2118 "Tail value (average C(t) over second half of acf): %g +/- %g\n",
2121 for (j
= 0; (j
< nn
); j
++)
2124 ct
[j
] = (cct
[j
]-tail
)/(1-tail
);
2126 /* Compute negative derivative k(t) = -dc(t)/dt */
2127 compute_derivative(nn
, hb
->time
, ct
, kt
);
2128 for (j
= 0; (j
< nn
); j
++)
2136 fp
= xvgropen(fn
, "Contact Autocorrelation", output_env_get_xvgr_tlabel(oenv
), "C(t)", oenv
);
2140 fp
= xvgropen(fn
, "Hydrogen Bond Autocorrelation", output_env_get_xvgr_tlabel(oenv
), "C(t)", oenv
);
2142 xvgr_legend(fp
, asize(legLuzar
), legLuzar
, oenv
);
2145 for (j
= 0; (j
< nn
); j
++)
2147 fprintf(fp
, "%10g %10g %10g %10g %10g\n",
2148 hb
->time
[j
]-hb
->time
[0], ct
[j
], cct
[j
], ght
[j
], kt
[j
]);
2152 analyse_corr(nn
, hb
->time
, ct
, ght
, kt
, nullptr, nullptr, nullptr,
2155 do_view(oenv
, fn
, nullptr);
2166 static void init_hbframe(t_hbdata
*hb
, int nframes
, real t
)
2170 hb
->time
[nframes
] = t
;
2171 hb
->nhb
[nframes
] = 0;
2172 hb
->ndist
[nframes
] = 0;
2173 for (i
= 0; (i
< max_hx
); i
++)
2175 hb
->nhx
[nframes
][i
] = 0;
2179 static FILE *open_donor_properties_file(const char *fn
,
2181 const gmx_output_env_t
*oenv
)
2184 const char *leg
[] = { "Nbound", "Nfree" };
2191 fp
= xvgropen(fn
, "Donor properties", output_env_get_xvgr_tlabel(oenv
), "Number", oenv
);
2192 xvgr_legend(fp
, asize(leg
), leg
, oenv
);
2197 static void analyse_donor_properties(FILE *fp
, t_hbdata
*hb
, int nframes
, real t
)
2199 int i
, j
, k
, nbound
, nb
, nhtot
;
2207 for (i
= 0; (i
< hb
->d
.nrd
); i
++)
2209 for (k
= 0; (k
< hb
->d
.nhydro
[i
]); k
++)
2213 for (j
= 0; (j
< hb
->a
.nra
) && (nb
== 0); j
++)
2215 if (hb
->hbmap
[i
][j
] && hb
->hbmap
[i
][j
]->h
[k
] &&
2216 is_hb(hb
->hbmap
[i
][j
]->h
[k
], nframes
))
2224 fprintf(fp
, "%10.3e %6d %6d\n", t
, nbound
, nhtot
-nbound
);
2227 static void dump_hbmap(t_hbdata
*hb
,
2228 int nfile
, t_filenm fnm
[], gmx_bool bTwo
,
2229 gmx_bool bContact
, const int isize
[], int *index
[], char *grpnames
[],
2230 const t_atoms
*atoms
)
2233 int ddd
, hhh
, aaa
, i
, j
, k
, m
, grp
;
2234 char ds
[32], hs
[32], as
[32];
2237 fp
= opt2FILE("-hbn", nfile
, fnm
, "w");
2238 if (opt2bSet("-g", nfile
, fnm
))
2240 fplog
= gmx_ffopen(opt2fn("-g", nfile
, fnm
), "w");
2241 fprintf(fplog
, "# %10s %12s %12s\n", "Donor", "Hydrogen", "Acceptor");
2247 for (grp
= gr0
; grp
<= (bTwo
? gr1
: gr0
); grp
++)
2249 fprintf(fp
, "[ %s ]", grpnames
[grp
]);
2250 for (i
= 0; i
< isize
[grp
]; i
++)
2252 fprintf(fp
, (i
%15) ? " " : "\n");
2253 fprintf(fp
, " %4d", index
[grp
][i
]+1);
2259 fprintf(fp
, "[ donors_hydrogens_%s ]\n", grpnames
[grp
]);
2260 for (i
= 0; (i
< hb
->d
.nrd
); i
++)
2262 if (hb
->d
.grp
[i
] == grp
)
2264 for (j
= 0; (j
< hb
->d
.nhydro
[i
]); j
++)
2266 fprintf(fp
, " %4d %4d", hb
->d
.don
[i
]+1,
2267 hb
->d
.hydro
[i
][j
]+1);
2273 fprintf(fp
, "[ acceptors_%s ]", grpnames
[grp
]);
2274 for (i
= 0; (i
< hb
->a
.nra
); i
++)
2276 if (hb
->a
.grp
[i
] == grp
)
2278 fprintf(fp
, (i
%15 && !first
) ? " " : "\n");
2279 fprintf(fp
, " %4d", hb
->a
.acc
[i
]+1);
2288 fprintf(fp
, bContact
? "[ contacts_%s-%s ]\n" :
2289 "[ hbonds_%s-%s ]\n", grpnames
[0], grpnames
[1]);
2293 fprintf(fp
, bContact
? "[ contacts_%s ]" : "[ hbonds_%s ]\n", grpnames
[0]);
2296 for (i
= 0; (i
< hb
->d
.nrd
); i
++)
2299 for (k
= 0; (k
< hb
->a
.nra
); k
++)
2302 for (m
= 0; (m
< hb
->d
.nhydro
[i
]); m
++)
2304 if (hb
->hbmap
[i
][k
] && ISHB(hb
->hbmap
[i
][k
]->history
[m
]))
2306 sprintf(ds
, "%s", mkatomname(atoms
, ddd
));
2307 sprintf(as
, "%s", mkatomname(atoms
, aaa
));
2310 fprintf(fp
, " %6d %6d\n", ddd
+1, aaa
+1);
2313 fprintf(fplog
, "%12s %12s\n", ds
, as
);
2318 hhh
= hb
->d
.hydro
[i
][m
];
2319 sprintf(hs
, "%s", mkatomname(atoms
, hhh
));
2320 fprintf(fp
, " %6d %6d %6d\n", ddd
+1, hhh
+1, aaa
+1);
2323 fprintf(fplog
, "%12s %12s %12s\n", ds
, hs
, as
);
2337 /* sync_hbdata() updates the parallel t_hbdata p_hb using hb as template.
2338 * It mimics add_frames() and init_frame() to some extent. */
2339 static void sync_hbdata(t_hbdata
*p_hb
, int nframes
)
2341 if (nframes
>= p_hb
->max_frames
)
2343 p_hb
->max_frames
+= 4096;
2344 srenew(p_hb
->nhb
, p_hb
->max_frames
);
2345 srenew(p_hb
->ndist
, p_hb
->max_frames
);
2346 srenew(p_hb
->n_bound
, p_hb
->max_frames
);
2347 srenew(p_hb
->nhx
, p_hb
->max_frames
);
2350 srenew(p_hb
->danr
, p_hb
->max_frames
);
2352 std::memset(&(p_hb
->nhb
[nframes
]), 0, sizeof(int) * (p_hb
->max_frames
-nframes
));
2353 std::memset(&(p_hb
->ndist
[nframes
]), 0, sizeof(int) * (p_hb
->max_frames
-nframes
));
2354 p_hb
->nhb
[nframes
] = 0;
2355 p_hb
->ndist
[nframes
] = 0;
2358 p_hb
->nframes
= nframes
;
2360 std::memset(&(p_hb
->nhx
[nframes
]), 0, sizeof(int)*max_hx
); /* zero the helix count for this frame */
2363 int gmx_hbond(int argc
, char *argv
[])
2365 const char *desc
[] = {
2366 "[THISMODULE] computes and analyzes hydrogen bonds. Hydrogen bonds are",
2367 "determined based on cutoffs for the angle Hydrogen - Donor - Acceptor",
2368 "(zero is extended) and the distance Donor - Acceptor",
2369 "(or Hydrogen - Acceptor using [TT]-noda[tt]).",
2370 "OH and NH groups are regarded as donors, O is an acceptor always,",
2371 "N is an acceptor by default, but this can be switched using",
2372 "[TT]-nitacc[tt]. Dummy hydrogen atoms are assumed to be connected",
2373 "to the first preceding non-hydrogen atom.[PAR]",
2375 "You need to specify two groups for analysis, which must be either",
2376 "identical or non-overlapping. All hydrogen bonds between the two",
2377 "groups are analyzed.[PAR]",
2379 "If you set [TT]-shell[tt], you will be asked for an additional index group",
2380 "which should contain exactly one atom. In this case, only hydrogen",
2381 "bonds between atoms within the shell distance from the one atom are",
2384 "With option -ac, rate constants for hydrogen bonding can be derived with the",
2385 "model of Luzar and Chandler (Nature 379:55, 1996; J. Chem. Phys. 113:23, 2000).",
2386 "If contact kinetics are analyzed by using the -contact option, then",
2387 "n(t) can be defined as either all pairs that are not within contact distance r at time t",
2388 "(corresponding to leaving the -r2 option at the default value 0) or all pairs that",
2389 "are within distance r2 (corresponding to setting a second cut-off value with option -r2).",
2390 "See mentioned literature for more details and definitions.",
2393 /* "It is also possible to analyse specific hydrogen bonds with",
2394 "[TT]-sel[tt]. This index file must contain a group of atom triplets",
2395 "Donor Hydrogen Acceptor, in the following way::",
2402 "Note that the triplets need not be on separate lines.",
2403 "Each atom triplet specifies a hydrogen bond to be analyzed,",
2404 "note also that no check is made for the types of atoms.[PAR]",
2409 " * [TT]-num[tt]: number of hydrogen bonds as a function of time.",
2410 " * [TT]-ac[tt]: average over all autocorrelations of the existence",
2411 " functions (either 0 or 1) of all hydrogen bonds.",
2412 " * [TT]-dist[tt]: distance distribution of all hydrogen bonds.",
2413 " * [TT]-ang[tt]: angle distribution of all hydrogen bonds.",
2414 " * [TT]-hx[tt]: the number of n-n+i hydrogen bonds as a function of time",
2415 " where n and n+i stand for residue numbers and i ranges from 0 to 6.",
2416 " This includes the n-n+3, n-n+4 and n-n+5 hydrogen bonds associated",
2417 " with helices in proteins.",
2418 " * [TT]-hbn[tt]: all selected groups, donors, hydrogens and acceptors",
2419 " for selected groups, all hydrogen bonded atoms from all groups and",
2420 " all solvent atoms involved in insertion.",
2421 " * [TT]-hbm[tt]: existence matrix for all hydrogen bonds over all",
2422 " frames, this also contains information on solvent insertion",
2423 " into hydrogen bonds. Ordering is identical to that in [TT]-hbn[tt]",
2425 " * [TT]-dan[tt]: write out the number of donors and acceptors analyzed for",
2426 " each timeframe. This is especially useful when using [TT]-shell[tt].",
2427 " * [TT]-nhbdist[tt]: compute the number of HBonds per hydrogen in order to",
2428 " compare results to Raman Spectroscopy.",
2430 "Note: options [TT]-ac[tt], [TT]-life[tt], [TT]-hbn[tt] and [TT]-hbm[tt]",
2431 "require an amount of memory proportional to the total numbers of donors",
2432 "times the total number of acceptors in the selected group(s)."
2435 static real acut
= 30, abin
= 1, rcut
= 0.35, r2cut
= 0, rbin
= 0.005, rshell
= -1;
2436 static real maxnhb
= 0, fit_start
= 1, fit_end
= 60, temp
= 298.15;
2437 static gmx_bool bNitAcc
= TRUE
, bDA
= TRUE
, bMerge
= TRUE
;
2438 static int nDump
= 0;
2439 static int nThreads
= 0;
2441 static gmx_bool bContact
= FALSE
;
2445 { "-a", FALSE
, etREAL
, {&acut
},
2446 "Cutoff angle (degrees, Hydrogen - Donor - Acceptor)" },
2447 { "-r", FALSE
, etREAL
, {&rcut
},
2448 "Cutoff radius (nm, X - Acceptor, see next option)" },
2449 { "-da", FALSE
, etBOOL
, {&bDA
},
2450 "Use distance Donor-Acceptor (if TRUE) or Hydrogen-Acceptor (FALSE)" },
2451 { "-r2", FALSE
, etREAL
, {&r2cut
},
2452 "Second cutoff radius. Mainly useful with [TT]-contact[tt] and [TT]-ac[tt]"},
2453 { "-abin", FALSE
, etREAL
, {&abin
},
2454 "Binwidth angle distribution (degrees)" },
2455 { "-rbin", FALSE
, etREAL
, {&rbin
},
2456 "Binwidth distance distribution (nm)" },
2457 { "-nitacc", FALSE
, etBOOL
, {&bNitAcc
},
2458 "Regard nitrogen atoms as acceptors" },
2459 { "-contact", FALSE
, etBOOL
, {&bContact
},
2460 "Do not look for hydrogen bonds, but merely for contacts within the cut-off distance" },
2461 { "-shell", FALSE
, etREAL
, {&rshell
},
2462 "when > 0, only calculate hydrogen bonds within # nm shell around "
2464 { "-fitstart", FALSE
, etREAL
, {&fit_start
},
2465 "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]" },
2466 { "-fitend", FALSE
, etREAL
, {&fit_end
},
2467 "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])" },
2468 { "-temp", FALSE
, etREAL
, {&temp
},
2469 "Temperature (K) for computing the Gibbs energy corresponding to HB breaking and reforming" },
2470 { "-dump", FALSE
, etINT
, {&nDump
},
2471 "Dump the first N hydrogen bond ACFs in a single [REF].xvg[ref] file for debugging" },
2472 { "-max_hb", FALSE
, etREAL
, {&maxnhb
},
2473 "Theoretical maximum number of hydrogen bonds used for normalizing HB autocorrelation function. Can be useful in case the program estimates it wrongly" },
2474 { "-merge", FALSE
, etBOOL
, {&bMerge
},
2475 "H-bonds between the same donor and acceptor, but with different hydrogen are treated as a single H-bond. Mainly important for the ACF." },
2477 { "-nthreads", FALSE
, etINT
, {&nThreads
},
2478 "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)"},
2481 const char *bugs
[] = {
2482 "The option [TT]-sel[tt] that used to work on selected hbonds is out of order, and therefore not available for the time being."
2485 { efTRX
, "-f", nullptr, ffREAD
},
2486 { efTPR
, nullptr, nullptr, ffREAD
},
2487 { efNDX
, nullptr, nullptr, ffOPTRD
},
2488 /* { efNDX, "-sel", "select", ffOPTRD },*/
2489 { efXVG
, "-num", "hbnum", ffWRITE
},
2490 { efLOG
, "-g", "hbond", ffOPTWR
},
2491 { efXVG
, "-ac", "hbac", ffOPTWR
},
2492 { efXVG
, "-dist", "hbdist", ffOPTWR
},
2493 { efXVG
, "-ang", "hbang", ffOPTWR
},
2494 { efXVG
, "-hx", "hbhelix", ffOPTWR
},
2495 { efNDX
, "-hbn", "hbond", ffOPTWR
},
2496 { efXPM
, "-hbm", "hbmap", ffOPTWR
},
2497 { efXVG
, "-don", "donor", ffOPTWR
},
2498 { efXVG
, "-dan", "danum", ffOPTWR
},
2499 { efXVG
, "-life", "hblife", ffOPTWR
},
2500 { efXVG
, "-nhbdist", "nhbdist", ffOPTWR
}
2503 #define NFILE asize(fnm)
2505 char hbmap
[HB_NR
] = { ' ', 'o', '-', '*' };
2506 const char *hbdesc
[HB_NR
] = { "None", "Present", "Inserted", "Present & Inserted" };
2507 t_rgb hbrgb
[HB_NR
] = { {1, 1, 1}, {1, 0, 0}, {0, 0, 1}, {1, 0, 1} };
2509 t_trxstatus
*status
;
2513 int npargs
, natoms
, nframes
= 0, shatom
;
2519 real t
, ccut
, dist
= 0.0, ang
= 0.0;
2520 double max_nhb
, aver_nhb
, aver_dist
;
2521 int h
= 0, i
= 0, j
, k
= 0, ogrp
, nsel
;
2523 int xj
, yj
, zj
, aj
, xjj
, yjj
, zjj
;
2524 gmx_bool bSelected
, bHBmap
, bStop
, bTwo
, bBox
, bTric
;
2526 int grp
, nabin
, nrbin
, resdist
, ihb
;
2529 FILE *fp
, *fpnhb
= nullptr, *donor_properties
= nullptr;
2531 t_ncell
*icell
, *jcell
;
2533 unsigned char *datable
;
2534 gmx_output_env_t
*oenv
;
2535 int ii
, hh
, actual_nThreads
;
2538 gmx_bool bEdge_yjj
, bEdge_xjj
;
2540 t_hbdata
**p_hb
= nullptr; /* one per thread, then merge after the frame loop */
2541 int **p_adist
= nullptr, **p_rdist
= nullptr; /* a histogram for each thread. */
2543 const bool bOMP
= GMX_OPENMP
;
2546 ppa
= add_acf_pargs(&npargs
, pa
);
2548 if (!parse_common_args(&argc
, argv
, PCA_CAN_TIME
| PCA_TIME_UNIT
, NFILE
, fnm
, npargs
,
2549 ppa
, asize(desc
), desc
, asize(bugs
), bugs
, &oenv
))
2557 ccut
= std::cos(acut
*DEG2RAD
);
2563 gmx_fatal(FARGS
, "Can not analyze selected contacts.");
2567 gmx_fatal(FARGS
, "Can not analyze contact between H and A: turn off -noda");
2571 /* Initiate main data structure! */
2572 bHBmap
= (opt2bSet("-ac", NFILE
, fnm
) ||
2573 opt2bSet("-life", NFILE
, fnm
) ||
2574 opt2bSet("-hbn", NFILE
, fnm
) ||
2575 opt2bSet("-hbm", NFILE
, fnm
));
2577 if (opt2bSet("-nhbdist", NFILE
, fnm
))
2579 const char *leg
[MAXHH
+1] = { "0 HBs", "1 HB", "2 HBs", "3 HBs", "Total" };
2580 fpnhb
= xvgropen(opt2fn("-nhbdist", NFILE
, fnm
),
2581 "Number of donor-H with N HBs", output_env_get_xvgr_tlabel(oenv
), "N", oenv
);
2582 xvgr_legend(fpnhb
, asize(leg
), leg
, oenv
);
2585 hb
= mk_hbdata(bHBmap
, opt2bSet("-dan", NFILE
, fnm
), bMerge
|| bContact
);
2588 t_inputrec irInstance
;
2589 t_inputrec
*ir
= &irInstance
;
2590 read_tpx_top(ftp2fn(efTPR
, NFILE
, fnm
), ir
, box
, &natoms
, nullptr, nullptr, &top
);
2592 snew(grpnames
, grNR
);
2595 /* Make Donor-Acceptor table */
2596 snew(datable
, top
.atoms
.nr
);
2600 /* analyze selected hydrogen bonds */
2601 printf("Select group with selected atoms:\n");
2602 get_index(&(top
.atoms
), opt2fn("-sel", NFILE
, fnm
),
2603 1, &nsel
, index
, grpnames
);
2606 gmx_fatal(FARGS
, "Number of atoms in group '%s' not a multiple of 3\n"
2607 "and therefore cannot contain triplets of "
2608 "Donor-Hydrogen-Acceptor", grpnames
[0]);
2612 for (i
= 0; (i
< nsel
); i
+= 3)
2614 int dd
= index
[0][i
];
2615 int aa
= index
[0][i
+2];
2616 /* int */ hh
= index
[0][i
+1];
2617 add_dh (&hb
->d
, dd
, hh
, i
, datable
);
2618 add_acc(&hb
->a
, aa
, i
);
2619 /* Should this be here ? */
2620 snew(hb
->d
.dptr
, top
.atoms
.nr
);
2621 snew(hb
->a
.aptr
, top
.atoms
.nr
);
2622 add_hbond(hb
, dd
, aa
, hh
, gr0
, gr0
, 0, bMerge
, 0, bContact
);
2624 printf("Analyzing %d selected hydrogen bonds from '%s'\n",
2625 isize
[0], grpnames
[0]);
2629 /* analyze all hydrogen bonds: get group(s) */
2630 printf("Specify 2 groups to analyze:\n");
2631 get_index(&(top
.atoms
), ftp2fn_null(efNDX
, NFILE
, fnm
),
2632 2, isize
, index
, grpnames
);
2634 /* check if we have two identical or two non-overlapping groups */
2635 bTwo
= isize
[0] != isize
[1];
2636 for (i
= 0; (i
< isize
[0]) && !bTwo
; i
++)
2638 bTwo
= index
[0][i
] != index
[1][i
];
2642 printf("Checking for overlap in atoms between %s and %s\n",
2643 grpnames
[0], grpnames
[1]);
2645 gen_datable(index
[0], isize
[0], datable
, top
.atoms
.nr
);
2647 for (i
= 0; i
< isize
[1]; i
++)
2649 if (ISINGRP(datable
[index
[1][i
]]))
2651 gmx_fatal(FARGS
, "Partial overlap between groups '%s' and '%s'",
2652 grpnames
[0], grpnames
[1]);
2658 printf("Calculating %s "
2659 "between %s (%d atoms) and %s (%d atoms)\n",
2660 bContact
? "contacts" : "hydrogen bonds",
2661 grpnames
[0], isize
[0], grpnames
[1], isize
[1]);
2665 fprintf(stderr
, "Calculating %s in %s (%d atoms)\n",
2666 bContact
? "contacts" : "hydrogen bonds", grpnames
[0], isize
[0]);
2671 /* search donors and acceptors in groups */
2672 snew(datable
, top
.atoms
.nr
);
2673 for (i
= 0; (i
< grNR
); i
++)
2675 if ( ((i
== gr0
) && !bSelected
) ||
2676 ((i
== gr1
) && bTwo
))
2678 gen_datable(index
[i
], isize
[i
], datable
, top
.atoms
.nr
);
2681 search_acceptors(&top
, isize
[i
], index
[i
], &hb
->a
, i
,
2682 bNitAcc
, TRUE
, (bTwo
&& (i
== gr0
)) || !bTwo
, datable
);
2683 search_donors (&top
, isize
[i
], index
[i
], &hb
->d
, i
,
2684 TRUE
, (bTwo
&& (i
== gr1
)) || !bTwo
, datable
);
2688 search_acceptors(&top
, isize
[i
], index
[i
], &hb
->a
, i
, bNitAcc
, FALSE
, TRUE
, datable
);
2689 search_donors (&top
, isize
[i
], index
[i
], &hb
->d
, i
, FALSE
, TRUE
, datable
);
2693 clear_datable_grp(datable
, top
.atoms
.nr
);
2698 printf("Found %d donors and %d acceptors\n", hb
->d
.nrd
, hb
->a
.nra
);
2700 snew(donors[gr0D], dons[gr0D].nrd);*/
2702 donor_properties
= open_donor_properties_file(opt2fn_null("-don", NFILE
, fnm
), hb
, oenv
);
2706 printf("Making hbmap structure...");
2707 /* Generate hbond data structure */
2714 if (hb
->d
.nrd
+ hb
->a
.nra
== 0)
2716 printf("No Donors or Acceptors found\n");
2723 printf("No Donors found\n");
2728 printf("No Acceptors found\n");
2734 gmx_fatal(FARGS
, "Nothing to be done");
2743 /* get index group with atom for shell */
2746 printf("Select atom for shell (1 atom):\n");
2747 get_index(&(top
.atoms
), ftp2fn_null(efNDX
, NFILE
, fnm
),
2748 1, &shisz
, &shidx
, &shgrpnm
);
2751 printf("group contains %d atoms, should be 1 (one)\n", shisz
);
2756 printf("Will calculate hydrogen bonds within a shell "
2757 "of %g nm around atom %i\n", rshell
, shatom
+1);
2760 /* Analyze trajectory */
2761 natoms
= read_first_x(oenv
, &status
, ftp2fn(efTRX
, NFILE
, fnm
), &t
, &x
, box
);
2762 if (natoms
> top
.atoms
.nr
)
2764 gmx_fatal(FARGS
, "Topology (%d atoms) does not match trajectory (%d atoms)",
2765 top
.atoms
.nr
, natoms
);
2768 bBox
= (ir
->ePBC
!= epbcNONE
);
2769 grid
= init_grid(bBox
, box
, (rcut
> r2cut
) ? rcut
: r2cut
, ngrid
);
2770 nabin
= static_cast<int>(acut
/abin
);
2771 nrbin
= static_cast<int>(rcut
/rbin
);
2772 snew(adist
, nabin
+1);
2773 snew(rdist
, nrbin
+1);
2776 #define __ADIST adist
2777 #define __RDIST rdist
2779 #else /* GMX_OPENMP ================================================== \
2780 * Set up the OpenMP stuff, |
2781 * like the number of threads and such |
2782 * Also start the parallel loop. |
2784 #define __ADIST p_adist[threadNr]
2785 #define __RDIST p_rdist[threadNr]
2786 #define __HBDATA p_hb[threadNr]
2790 bParallel
= !bSelected
;
2794 actual_nThreads
= std::min((nThreads
<= 0) ? INT_MAX
: nThreads
, gmx_omp_get_max_threads());
2796 gmx_omp_set_num_threads(actual_nThreads
);
2797 printf("Frame loop parallelized with OpenMP using %i threads.\n", actual_nThreads
);
2802 actual_nThreads
= 1;
2805 snew(p_hb
, actual_nThreads
);
2806 snew(p_adist
, actual_nThreads
);
2807 snew(p_rdist
, actual_nThreads
);
2808 for (i
= 0; i
< actual_nThreads
; i
++)
2811 snew(p_adist
[i
], nabin
+1);
2812 snew(p_rdist
[i
], nrbin
+1);
2814 p_hb
[i
]->max_frames
= 0;
2815 p_hb
[i
]->nhb
= nullptr;
2816 p_hb
[i
]->ndist
= nullptr;
2817 p_hb
[i
]->n_bound
= nullptr;
2818 p_hb
[i
]->time
= nullptr;
2819 p_hb
[i
]->nhx
= nullptr;
2821 p_hb
[i
]->bHBmap
= hb
->bHBmap
;
2822 p_hb
[i
]->bDAnr
= hb
->bDAnr
;
2823 p_hb
[i
]->wordlen
= hb
->wordlen
;
2824 p_hb
[i
]->nframes
= hb
->nframes
;
2825 p_hb
[i
]->maxhydro
= hb
->maxhydro
;
2826 p_hb
[i
]->danr
= hb
->danr
;
2829 p_hb
[i
]->hbmap
= hb
->hbmap
;
2830 p_hb
[i
]->time
= hb
->time
; /* This may need re-syncing at every frame. */
2833 p_hb
[i
]->nrdist
= 0;
2837 /* Make a thread pool here,
2838 * instead of forking anew at every frame. */
2840 #pragma omp parallel \
2842 private(j, h, ii, hh, \
2843 xi, yi, zi, xj, yj, zj, threadNr, \
2844 dist, ang, icell, jcell, \
2845 grp, ogrp, ai, aj, xjj, yjj, zjj, \
2848 bEdge_xjj, bEdge_yjj) \
2850 { /* Start of parallel region */
2853 threadNr
= gmx_omp_get_thread_num();
2858 bTric
= bBox
&& TRICLINIC(box
);
2864 sync_hbdata(p_hb
[threadNr
], nframes
);
2866 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
2872 build_grid(hb
, x
, x
[shatom
], bBox
, box
, hbox
, (rcut
> r2cut
) ? rcut
: r2cut
,
2873 rshell
, ngrid
, grid
);
2874 reset_nhbonds(&(hb
->d
));
2876 if (debug
&& bDebug
)
2878 dump_grid(debug
, ngrid
, grid
);
2881 add_frames(hb
, nframes
);
2882 init_hbframe(hb
, nframes
, output_env_conv_time(oenv
, t
));
2886 count_da_grid(ngrid
, grid
, hb
->danr
[nframes
]);
2889 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
2894 p_hb
[threadNr
]->time
= hb
->time
; /* This pointer may have changed. */
2904 /* Do not parallelize this just yet. */
2906 for (ii
= 0; (ii
< nsel
); ii
++)
2908 int dd
= index
[0][i
];
2909 int aa
= index
[0][i
+2];
2910 /* int */ hh
= index
[0][i
+1];
2911 ihb
= is_hbond(hb
, ii
, ii
, dd
, aa
, rcut
, r2cut
, ccut
, x
, bBox
, box
,
2912 hbox
, &dist
, &ang
, bDA
, &h
, bContact
, bMerge
);
2916 /* add to index if not already there */
2918 add_hbond(hb
, dd
, aa
, hh
, ii
, ii
, nframes
, bMerge
, ihb
, bContact
);
2922 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
2924 } /* if (bSelected) */
2927 /* The outer grid loop will have to do for now. */
2928 #pragma omp for schedule(dynamic)
2929 for (xi
= 0; xi
< ngrid
[XX
]; xi
++)
2933 for (yi
= 0; (yi
< ngrid
[YY
]); yi
++)
2935 for (zi
= 0; (zi
< ngrid
[ZZ
]); zi
++)
2938 /* loop over donor groups gr0 (always) and gr1 (if necessary) */
2939 for (grp
= gr0
; (grp
<= (bTwo
? gr1
: gr0
)); grp
++)
2941 icell
= &(grid
[zi
][yi
][xi
].d
[grp
]);
2952 /* loop over all hydrogen atoms from group (grp)
2953 * in this gridcell (icell)
2955 for (ai
= 0; (ai
< icell
->nr
); ai
++)
2957 i
= icell
->atoms
[ai
];
2959 /* loop over all adjacent gridcells (xj,yj,zj) */
2960 for (zjj
= grid_loop_begin(ngrid
[ZZ
], zi
, bTric
, FALSE
);
2961 zjj
<= grid_loop_end(ngrid
[ZZ
], zi
, bTric
, FALSE
);
2964 zj
= grid_mod(zjj
, ngrid
[ZZ
]);
2965 bEdge_yjj
= (zj
== 0) || (zj
== ngrid
[ZZ
] - 1);
2966 for (yjj
= grid_loop_begin(ngrid
[YY
], yi
, bTric
, bEdge_yjj
);
2967 yjj
<= grid_loop_end(ngrid
[YY
], yi
, bTric
, bEdge_yjj
);
2970 yj
= grid_mod(yjj
, ngrid
[YY
]);
2972 (yj
== 0) || (yj
== ngrid
[YY
] - 1) ||
2973 (zj
== 0) || (zj
== ngrid
[ZZ
] - 1);
2974 for (xjj
= grid_loop_begin(ngrid
[XX
], xi
, bTric
, bEdge_xjj
);
2975 xjj
<= grid_loop_end(ngrid
[XX
], xi
, bTric
, bEdge_xjj
);
2978 xj
= grid_mod(xjj
, ngrid
[XX
]);
2979 jcell
= &(grid
[zj
][yj
][xj
].a
[ogrp
]);
2980 /* loop over acceptor atoms from other group (ogrp)
2981 * in this adjacent gridcell (jcell)
2983 for (aj
= 0; (aj
< jcell
->nr
); aj
++)
2985 j
= jcell
->atoms
[aj
];
2987 /* check if this once was a h-bond */
2988 ihb
= is_hbond(__HBDATA
, grp
, ogrp
, i
, j
, rcut
, r2cut
, ccut
, x
, bBox
, box
,
2989 hbox
, &dist
, &ang
, bDA
, &h
, bContact
, bMerge
);
2993 /* add to index if not already there */
2995 add_hbond(__HBDATA
, i
, j
, h
, grp
, ogrp
, nframes
, bMerge
, ihb
, bContact
);
2997 /* make angle and distance distributions */
2998 if (ihb
== hbHB
&& !bContact
)
3002 gmx_fatal(FARGS
, "distance is higher than what is allowed for an hbond: %f", dist
);
3005 __ADIST
[static_cast<int>( ang
/abin
)]++;
3006 __RDIST
[static_cast<int>(dist
/rbin
)]++;
3009 if (donor_index(&hb
->d
, grp
, i
) == NOTSET
)
3011 gmx_fatal(FARGS
, "Invalid donor %d", i
);
3013 if (acceptor_index(&hb
->a
, ogrp
, j
) == NOTSET
)
3015 gmx_fatal(FARGS
, "Invalid acceptor %d", j
);
3017 resdist
= std::abs(top
.atoms
.atom
[i
].resind
-top
.atoms
.atom
[j
].resind
);
3018 if (resdist
>= max_hx
)
3022 __HBDATA
->nhx
[nframes
][resdist
]++;
3033 } /* for xi,yi,zi */
3036 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
3038 } /* if (bSelected) {...} else */
3041 /* Better wait for all threads to finnish using x[] before updating it. */
3044 #pragma omp critical
3048 /* Sum up histograms and counts from p_hb[] into hb */
3051 hb
->nhb
[k
] += p_hb
[threadNr
]->nhb
[k
];
3052 hb
->ndist
[k
] += p_hb
[threadNr
]->ndist
[k
];
3053 for (j
= 0; j
< max_hx
; j
++)
3055 hb
->nhx
[k
][j
] += p_hb
[threadNr
]->nhx
[k
][j
];
3059 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
3062 /* Here are a handful of single constructs
3063 * to share the workload a bit. The most
3064 * important one is of course the last one,
3065 * where there's a potential bottleneck in form
3072 analyse_donor_properties(donor_properties
, hb
, k
, t
);
3074 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
3083 do_nhb_dist(fpnhb
, hb
, t
);
3086 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
3093 trrStatus
= (read_next_x(oenv
, status
, &t
, x
, box
));
3096 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
3105 #pragma omp critical
3107 hb
->nrhb
+= p_hb
[threadNr
]->nrhb
;
3108 hb
->nrdist
+= p_hb
[threadNr
]->nrdist
;
3111 /* Free parallel datastructures */
3112 sfree(p_hb
[threadNr
]->nhb
);
3113 sfree(p_hb
[threadNr
]->ndist
);
3114 sfree(p_hb
[threadNr
]->nhx
);
3117 for (i
= 0; i
< nabin
; i
++)
3121 for (j
= 0; j
< actual_nThreads
; j
++)
3124 adist
[i
] += p_adist
[j
][i
];
3127 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
3130 for (i
= 0; i
<= nrbin
; i
++)
3134 for (j
= 0; j
< actual_nThreads
; j
++)
3136 rdist
[i
] += p_rdist
[j
][i
];
3139 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
;
3142 sfree(p_adist
[threadNr
]);
3143 sfree(p_rdist
[threadNr
]);
3145 } /* End of parallel region */
3152 if (nframes
< 2 && (opt2bSet("-ac", NFILE
, fnm
) || opt2bSet("-life", NFILE
, fnm
)))
3154 gmx_fatal(FARGS
, "Cannot calculate autocorrelation of life times with less than two frames");
3157 free_grid(ngrid
, &grid
);
3161 if (donor_properties
)
3163 xvgrclose(donor_properties
);
3171 /* Compute maximum possible number of different hbonds */
3178 max_nhb
= 0.5*(hb
->d
.nrd
*hb
->a
.nra
);
3185 printf("No %s found!!\n", bContact
? "contacts" : "hydrogen bonds");
3189 printf("Found %d different %s in trajectory\n"
3190 "Found %d different atom-pairs within %s distance\n",
3191 hb
->nrhb
, bContact
? "contacts" : "hydrogen bonds",
3192 hb
->nrdist
, (r2cut
> 0) ? "second cut-off" : "hydrogen bonding");
3196 merge_hb(hb
, bTwo
, bContact
);
3199 if (opt2bSet("-hbn", NFILE
, fnm
))
3201 dump_hbmap(hb
, NFILE
, fnm
, bTwo
, bContact
, isize
, index
, grpnames
, &top
.atoms
);
3204 /* Moved the call to merge_hb() to a line BEFORE dump_hbmap
3205 * to make the -hbn and -hmb output match eachother.
3206 * - Erik Marklund, May 30, 2006 */
3209 /* Print out number of hbonds and distances */
3212 fp
= xvgropen(opt2fn("-num", NFILE
, fnm
), bContact
? "Contacts" :
3213 "Hydrogen Bonds", output_env_get_xvgr_tlabel(oenv
), "Number", oenv
);
3215 snew(leg
[0], STRLEN
);
3216 snew(leg
[1], STRLEN
);
3217 sprintf(leg
[0], "%s", bContact
? "Contacts" : "Hydrogen bonds");
3218 sprintf(leg
[1], "Pairs within %g nm", (r2cut
> 0) ? r2cut
: rcut
);
3219 xvgr_legend(fp
, 2, (const char**)leg
, oenv
);
3223 for (i
= 0; (i
< nframes
); i
++)
3225 fprintf(fp
, "%10g %10d %10d\n", hb
->time
[i
], hb
->nhb
[i
], hb
->ndist
[i
]);
3226 aver_nhb
+= hb
->nhb
[i
];
3227 aver_dist
+= hb
->ndist
[i
];
3230 aver_nhb
/= nframes
;
3231 aver_dist
/= nframes
;
3232 /* Print HB distance distribution */
3233 if (opt2bSet("-dist", NFILE
, fnm
))
3238 for (i
= 0; i
< nrbin
; i
++)
3243 fp
= xvgropen(opt2fn("-dist", NFILE
, fnm
),
3244 "Hydrogen Bond Distribution",
3246 "Donor - Acceptor Distance (nm)" :
3247 "Hydrogen - Acceptor Distance (nm)", "", oenv
);
3248 for (i
= 0; i
< nrbin
; i
++)
3250 fprintf(fp
, "%10g %10g\n", (i
+0.5)*rbin
, rdist
[i
]/(rbin
*sum
));
3255 /* Print HB angle distribution */
3256 if (opt2bSet("-ang", NFILE
, fnm
))
3261 for (i
= 0; i
< nabin
; i
++)
3266 fp
= xvgropen(opt2fn("-ang", NFILE
, fnm
),
3267 "Hydrogen Bond Distribution",
3268 "Hydrogen - Donor - Acceptor Angle (\\SO\\N)", "", oenv
);
3269 for (i
= 0; i
< nabin
; i
++)
3271 fprintf(fp
, "%10g %10g\n", (i
+0.5)*abin
, adist
[i
]/(abin
*sum
));
3276 /* Print HB in alpha-helix */
3277 if (opt2bSet("-hx", NFILE
, fnm
))
3279 fp
= xvgropen(opt2fn("-hx", NFILE
, fnm
),
3280 "Hydrogen Bonds", output_env_get_xvgr_tlabel(oenv
), "Count", oenv
);
3281 xvgr_legend(fp
, NRHXTYPES
, hxtypenames
, oenv
);
3282 for (i
= 0; i
< nframes
; i
++)
3284 fprintf(fp
, "%10g", hb
->time
[i
]);
3285 for (j
= 0; j
< max_hx
; j
++)
3287 fprintf(fp
, " %6d", hb
->nhx
[i
][j
]);
3294 printf("Average number of %s per timeframe %.3f out of %g possible\n",
3295 bContact
? "contacts" : "hbonds",
3296 bContact
? aver_dist
: aver_nhb
, max_nhb
);
3298 /* Do Autocorrelation etc. */
3302 Added support for -contact in ac and hbm calculations below.
3303 - Erik Marklund, May 29, 2006
3305 if (opt2bSet("-ac", NFILE
, fnm
) || opt2bSet("-life", NFILE
, fnm
))
3307 please_cite(stdout
, "Spoel2006b");
3309 if (opt2bSet("-ac", NFILE
, fnm
))
3311 do_hbac(opt2fn("-ac", NFILE
, fnm
), hb
, nDump
,
3312 bMerge
, bContact
, fit_start
, temp
, r2cut
> 0, oenv
,
3315 if (opt2bSet("-life", NFILE
, fnm
))
3317 do_hblife(opt2fn("-life", NFILE
, fnm
), hb
, bMerge
, bContact
, oenv
);
3319 if (opt2bSet("-hbm", NFILE
, fnm
))
3322 int id
, ia
, hh
, x
, y
;
3323 mat
.flags
= mat
.y0
= 0;
3325 if ((nframes
> 0) && (hb
->nrhb
> 0))
3330 snew(mat
.matrix
, mat
.nx
);
3331 for (x
= 0; (x
< mat
.nx
); x
++)
3333 snew(mat
.matrix
[x
], mat
.ny
);
3336 for (id
= 0; (id
< hb
->d
.nrd
); id
++)
3338 for (ia
= 0; (ia
< hb
->a
.nra
); ia
++)
3340 for (hh
= 0; (hh
< hb
->maxhydro
); hh
++)
3342 if (hb
->hbmap
[id
][ia
])
3344 if (ISHB(hb
->hbmap
[id
][ia
]->history
[hh
]))
3346 for (x
= 0; (x
<= hb
->hbmap
[id
][ia
]->nframes
); x
++)
3348 int nn0
= hb
->hbmap
[id
][ia
]->n0
;
3349 range_check(y
, 0, mat
.ny
);
3350 mat
.matrix
[x
+nn0
][y
] = is_hb(hb
->hbmap
[id
][ia
]->h
[hh
], x
);
3358 mat
.axis_x
= hb
->time
;
3359 snew(mat
.axis_y
, mat
.ny
);
3360 for (j
= 0; j
< mat
.ny
; j
++)
3364 sprintf(mat
.title
, bContact
? "Contact Existence Map" :
3365 "Hydrogen Bond Existence Map");
3366 sprintf(mat
.legend
, bContact
? "Contacts" : "Hydrogen Bonds");
3367 sprintf(mat
.label_x
, "%s", output_env_get_xvgr_tlabel(oenv
).c_str());
3368 sprintf(mat
.label_y
, bContact
? "Contact Index" : "Hydrogen Bond Index");
3369 mat
.bDiscrete
= TRUE
;
3371 snew(mat
.map
, mat
.nmap
);
3372 for (i
= 0; i
< mat
.nmap
; i
++)
3374 mat
.map
[i
].code
.c1
= hbmap
[i
];
3375 mat
.map
[i
].desc
= hbdesc
[i
];
3376 mat
.map
[i
].rgb
= hbrgb
[i
];
3378 fp
= opt2FILE("-hbm", NFILE
, fnm
, "w");
3379 write_xpm_m(fp
, mat
);
3381 for (x
= 0; x
< mat
.nx
; x
++)
3383 sfree(mat
.matrix
[x
]);
3391 fprintf(stderr
, "No hydrogen bonds/contacts found. No hydrogen bond map will be printed.\n");
3402 #define USE_THIS_GROUP(j) ( ((j) == gr0) || (bTwo && ((j) == gr1)) )
3404 fp
= xvgropen(opt2fn("-dan", NFILE
, fnm
),
3405 "Donors and Acceptors", output_env_get_xvgr_tlabel(oenv
), "Count", oenv
);
3406 nleg
= (bTwo
? 2 : 1)*2;
3407 snew(legnames
, nleg
);
3409 for (j
= 0; j
< grNR
; j
++)
3411 if (USE_THIS_GROUP(j
) )
3413 sprintf(buf
, "Donors %s", grpnames
[j
]);
3414 legnames
[i
++] = gmx_strdup(buf
);
3415 sprintf(buf
, "Acceptors %s", grpnames
[j
]);
3416 legnames
[i
++] = gmx_strdup(buf
);
3421 gmx_incons("number of legend entries");
3423 xvgr_legend(fp
, nleg
, (const char**)legnames
, oenv
);
3424 for (i
= 0; i
< nframes
; i
++)
3426 fprintf(fp
, "%10g", hb
->time
[i
]);
3427 for (j
= 0; (j
< grNR
); j
++)
3429 if (USE_THIS_GROUP(j
) )
3431 fprintf(fp
, " %6d", hb
->danr
[i
][j
]);