Fixes #882 - looping bug in trxio.c
[gromacs.git] / src / tools / gmx_hbond.c
blob91ba5ee565b5f0d4198d4fe3520ff3fbdb312884
1 /* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
2 *
3 *
4 * This source code is part of
5 *
6 * G R O M A C S
7 *
8 * GROningen MAchine for Chemical Simulations
9 *
10 * VERSION 3.3.3
11 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
12 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
13 * Copyright (c) 2001-2008, The GROMACS development team,
14 * check out http://www.gromacs.org for more information.
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version 2
19 * of the License, or (at your option) any later version.
21 * If you want to redistribute modifications, please consider that
22 * scientific software is very special. Version control is crucial -
23 * bugs must be traceable. We will be happy to consider code for
24 * inclusion in the official distribution, but derived work must not
25 * be called official GROMACS. Details are found in the README & COPYING
26 * files - if they are missing, get the official version at www.gromacs.org.
28 * To help us fund GROMACS development, we humbly ask that you cite
29 * the papers on the package - you can find them in the top README file.
31 * For more info, check our website at http://www.gromacs.org
33 * And Hey:
34 * Groningen Machine for Chemical Simulation
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
39 #include <math.h>
41 /*#define HAVE_NN_LOOPS*/
42 /* Set environment variable CFLAGS = "-fopenmp" when running
43 * configure and define DOUSEOPENMP to make use of parallelized
44 * calculation of autocorrelation function.
45 * It also adds a new option -nthreads which sets the number of threads.
46 * */
47 /*#define DOUSEOPENMP*/
49 #ifdef DOUSEOPENMP
50 #define HAVE_OPENMP
51 #include "omp.h"
52 #endif
54 #include "statutil.h"
55 #include "copyrite.h"
56 #include "sysstuff.h"
57 #include "txtdump.h"
58 #include "futil.h"
59 #include "tpxio.h"
60 #include "physics.h"
61 #include "macros.h"
62 #include "gmx_fatal.h"
63 #include "index.h"
64 #include "smalloc.h"
65 #include "vec.h"
66 #include "xvgr.h"
67 #include "gstat.h"
68 #include "matio.h"
69 #include "string2.h"
70 #include "pbc.h"
71 #include "correl.h"
72 #include "gmx_ana.h"
73 #include "geminate.h"
75 typedef short int t_E;
76 typedef int t_EEst;
77 #define max_hx 7
78 typedef int t_hx[max_hx];
79 #define NRHXTYPES max_hx
80 const char *hxtypenames[NRHXTYPES]=
81 {"n-n","n-n+1","n-n+2","n-n+3","n-n+4","n-n+5","n-n>6"};
82 #define MAXHH 4
84 #ifdef HAVE_OPENMP
85 #define MASTER_THREAD_ONLY(threadNr) ((threadNr)==0)
86 #else
87 #define MASTER_THREAD_ONLY(threadNr) ((threadNr)==(threadNr))
88 #endif
90 /* -----------------------------------------*/
92 enum { gr0, gr1, grI, grNR };
93 enum { hbNo, hbDist, hbHB, hbNR, hbR2};
94 enum { noDA, ACC, DON, DA, INGROUP};
95 enum {NN_NULL, NN_NONE, NN_BINARY, NN_1_over_r3, NN_dipole, NN_NR};
97 static const char *grpnames[grNR] = {"0","1","I" };
99 static gmx_bool bDebug = FALSE;
101 #define HB_NO 0
102 #define HB_YES 1<<0
103 #define HB_INS 1<<1
104 #define HB_YESINS HB_YES|HB_INS
105 #define HB_NR (1<<2)
106 #define MAXHYDRO 4
108 #define ISHB(h) (((h) & 2) == 2)
109 #define ISDIST(h) (((h) & 1) == 1)
110 #define ISDIST2(h) (((h) & 4) == 4)
111 #define ISACC(h) (((h) & 1) == 1)
112 #define ISDON(h) (((h) & 2) == 2)
113 #define ISINGRP(h) (((h) & 4) == 4)
115 typedef struct {
116 int nr;
117 int maxnr;
118 atom_id *atoms;
119 } t_ncell;
121 typedef struct {
122 t_ncell d[grNR];
123 t_ncell a[grNR];
124 } t_gridcell;
126 typedef int t_icell[grNR];
127 typedef atom_id h_id[MAXHYDRO];
129 typedef struct {
130 int history[MAXHYDRO];
131 /* Has this hbond existed ever? If so as hbDist or hbHB or both.
132 * Result is stored as a bitmap (1 = hbDist) || (2 = hbHB)
134 /* Bitmask array which tells whether a hbond is present
135 * at a given time. Either of these may be NULL
137 int n0; /* First frame a HB was found */
138 int nframes,maxframes; /* Amount of frames in this hbond */
139 unsigned int **h;
140 unsigned int **g;
141 /* See Xu and Berne, JPCB 105 (2001), p. 11929. We define the
142 * function g(t) = [1-h(t)] H(t) where H(t) is one when the donor-
143 * acceptor distance is less than the user-specified distance (typically
144 * 0.35 nm).
146 } t_hbond;
148 typedef struct {
149 int nra,max_nra;
150 atom_id *acc; /* Atom numbers of the acceptors */
151 int *grp; /* Group index */
152 int *aptr; /* Map atom number to acceptor index */
153 } t_acceptors;
155 typedef struct {
156 int nrd,max_nrd;
157 int *don; /* Atom numbers of the donors */
158 int *grp; /* Group index */
159 int *dptr; /* Map atom number to donor index */
160 int *nhydro; /* Number of hydrogens for each donor */
161 h_id *hydro; /* The atom numbers of the hydrogens */
162 h_id *nhbonds; /* The number of HBs per H at current */
163 } t_donors;
165 /* Tune this to match memory requirements. It should be a signed integer type, e.g. signed char.*/
166 #define PSTYPE int
168 typedef struct {
169 int len; /* The length of frame and p. */
170 int *frame; /* The frames at which transitio*/
171 PSTYPE *p;
172 } t_pShift;
174 typedef struct {
175 /* Periodicity history. Used for the reversible geminate recombination. */
176 t_pShift **pHist; /* The periodicity of every hbond in t_hbdata->hbmap:
177 * pHist[d][a]. We can safely assume that the same
178 * periodic shift holds for all hydrogens of a da-pair.
180 * Nowadays it only stores TRANSITIONS, and not the shift at every frame.
181 * That saves a LOT of memory, an hopefully kills a mysterious bug where
182 * pHist gets contaminated. */
184 PSTYPE nper; /* The length of p2i */
185 ivec *p2i; /* Maps integer to periodic shift for a pair.*/
186 matrix P; /* Projection matrix to find the box shifts. */
187 int gemtype; /* enumerated type */
188 } t_gemPeriod;
190 typedef struct {
191 int nframes;
192 int *Etot; /* Total energy for each frame */
193 t_E ****E; /* Energy estimate for [d][a][h][frame-n0] */
194 } t_hbEmap;
196 typedef struct {
197 gmx_bool bHBmap,bDAnr,bGem;
198 int wordlen;
199 /* The following arrays are nframes long */
200 int nframes,max_frames,maxhydro;
201 int *nhb,*ndist;
202 h_id *n_bound;
203 real *time;
204 t_icell *danr;
205 t_hx *nhx;
206 /* These structures are initialized from the topology at start up */
207 t_donors d;
208 t_acceptors a;
209 /* This holds a matrix with all possible hydrogen bonds */
210 int nrhb,nrdist;
211 t_hbond ***hbmap;
212 #ifdef HAVE_NN_LOOPS
213 t_hbEmap hbE;
214 #endif
215 /* For parallelization reasons this will have to be a pointer.
216 * Otherwise discrepancies may arise between the periodicity data
217 * seen by different threads. */
218 t_gemPeriod *per;
219 } t_hbdata;
221 static void clearPshift(t_pShift *pShift)
223 if (pShift->len > 0) {
224 sfree(pShift->p);
225 sfree(pShift->frame);
226 pShift->len = 0;
230 static void calcBoxProjection(matrix B, matrix P)
232 const int vp[] = {XX,YY,ZZ};
233 int i,j;
234 int m,n;
235 matrix M, N, U;
237 for (i=0; i<3; i++){ m = vp[i];
238 for (j=0; j<3; j++){ n = vp[j];
239 U[m][n] = i==j ? 1:0;
242 m_inv(B,M);
243 for (i=0; i<3; i++){ m = vp[i];
244 mvmul(M, U[m], P[m]);
246 transpose(P,N);
249 static void calcBoxDistance(matrix P, rvec d, ivec ibd){
250 /* returns integer distance in box coordinates.
251 * P is the projection matrix from cartesian coordinates
252 * obtained with calcBoxProjection(). */
253 int i;
254 rvec bd;
255 mvmul(P, d, bd);
256 /* extend it by 0.5 in all directions since (int) rounds toward 0.*/
257 for (i=0;i<3;i++)
258 bd[i]=bd[i] + (bd[i]<0 ? -0.5 : 0.5);
259 ibd[XX] = (int)bd[XX];
260 ibd[YY] = (int)bd[YY];
261 ibd[ZZ] = (int)bd[ZZ];
264 /* Changed argument 'bMerge' into 'oneHB' below,
265 * since -contact should cause maxhydro to be 1,
266 * not just -merge.
267 * - Erik Marklund May 29, 2006
270 static PSTYPE periodicIndex(ivec r, t_gemPeriod *per, gmx_bool daSwap) {
271 /* Try to merge hbonds on the fly. That means that if the
272 * acceptor and donor are mergable, then:
273 * 1) store the hb-info so that acceptor id > donor id,
274 * 2) add the periodic shift in pairs, so that [-x,-y,-z] is
275 * stored in per.p2i[] whenever acceptor id < donor id.
276 * Note that [0,0,0] should already be the first element of per.p2i
277 * by the time this function is called. */
279 /* daSwap is TRUE if the donor and acceptor were swapped.
280 * If so, then the negative vector should be used. */
281 PSTYPE i;
283 if (per->p2i == NULL || per->nper == 0)
284 gmx_fatal(FARGS, "'per' not initialized properly.");
285 for (i=0; i<per->nper; i++) {
286 if (r[XX] == per->p2i[i][XX] &&
287 r[YY] == per->p2i[i][YY] &&
288 r[ZZ] == per->p2i[i][ZZ])
289 return i;
291 /* Not found apparently. Add it to the list! */
292 /* printf("New shift found: %i,%i,%i\n",r[XX],r[YY],r[ZZ]); */
294 /* Unfortunately this needs to be critical it seems. */
295 #ifdef HAVE_OPENMP
296 #pragma omp critical
297 #endif
299 if (!per->p2i) {
300 fprintf(stderr, "p2i not initialized. This shouldn't happen!\n");
301 snew(per->p2i, 1);
303 else
304 srenew(per->p2i, per->nper+2);
305 copy_ivec(r, per->p2i[per->nper]);
306 (per->nper)++;
308 /* Add the mirror too. It's rather likely that it'll be needed. */
309 per->p2i[per->nper][XX] = -r[XX];
310 per->p2i[per->nper][YY] = -r[YY];
311 per->p2i[per->nper][ZZ] = -r[ZZ];
312 (per->nper)++;
313 } /* omp critical */
314 return per->nper - 1 - (daSwap ? 0:1);
317 static t_hbdata *mk_hbdata(gmx_bool bHBmap,gmx_bool bDAnr,gmx_bool oneHB, gmx_bool bGem, int gemmode)
319 t_hbdata *hb;
321 snew(hb,1);
322 hb->wordlen = 8*sizeof(unsigned int);
323 hb->bHBmap = bHBmap;
324 hb->bDAnr = bDAnr;
325 hb->bGem = bGem;
326 if (oneHB)
327 hb->maxhydro = 1;
328 else
329 hb->maxhydro = MAXHYDRO;
330 snew(hb->per, 1);
331 hb->per->gemtype = bGem ? gemmode : 0;
333 return hb;
336 static void mk_hbmap(t_hbdata *hb,gmx_bool bTwo)
338 int i,j;
340 snew(hb->hbmap,hb->d.nrd);
341 for(i=0; (i<hb->d.nrd); i++) {
342 snew(hb->hbmap[i],hb->a.nra);
343 if (hb->hbmap[i] == NULL)
344 gmx_fatal(FARGS,"Could not allocate enough memory for hbmap");
345 for (j=0; (j>hb->a.nra); j++)
346 hb->hbmap[i][j] = NULL;
350 /* Consider redoing pHist so that is only stores transitions between
351 * periodicities and not the periodicity for all frames. This eats heaps of memory. */
352 static void mk_per(t_hbdata *hb)
354 int i,j;
355 if (hb->bGem) {
356 snew(hb->per->pHist, hb->d.nrd);
357 for (i=0; i<hb->d.nrd; i++) {
358 snew(hb->per->pHist[i], hb->a.nra);
359 if (hb->per->pHist[i]==NULL)
360 gmx_fatal(FARGS,"Could not allocate enough memory for per->pHist");
361 for (j=0; j<hb->a.nra; j++) {
362 clearPshift(&(hb->per->pHist[i][j]));
365 /* add the [0,0,0] shift to element 0 of p2i. */
366 snew(hb->per->p2i, 1);
367 clear_ivec(hb->per->p2i[0]);
368 hb->per->nper = 1;
372 #ifdef HAVE_NN_LOOPS
373 static void mk_hbEmap (t_hbdata *hb, int n0)
375 int i, j, k;
376 hb->hbE.E = NULL;
377 hb->hbE.nframes = 0;
378 snew(hb->hbE.E, hb->d.nrd);
379 for (i=0; i<hb->d.nrd; i++)
381 snew(hb->hbE.E[i], hb->a.nra);
382 for (j=0; j<hb->a.nra; j++)
384 snew(hb->hbE.E[i][j], MAXHYDRO);
385 for (k=0; k<MAXHYDRO; k++)
386 hb->hbE.E[i][j][k] = NULL;
389 hb->hbE.Etot = NULL;
392 static void free_hbEmap (t_hbdata *hb)
394 int i, j, k;
395 for (i=0; i<hb->d.nrd; i++)
397 for (j=0; j<hb->a.nra; j++)
399 for (k=0; k<MAXHYDRO; k++)
400 sfree(hb->hbE.E[i][j][k]);
401 sfree(hb->hbE.E[i][j]);
403 sfree(hb->hbE.E[i]);
405 sfree(hb->hbE.E);
406 sfree(hb->hbE.Etot);
409 static void addFramesNN(t_hbdata *hb, int frame)
412 #define DELTAFRAMES_HBE 10
414 int d,a,h,nframes;
416 if (frame >= hb->hbE.nframes) {
417 nframes = hb->hbE.nframes + DELTAFRAMES_HBE;
418 srenew(hb->hbE.Etot, nframes);
420 for (d=0; d<hb->d.nrd; d++)
421 for (a=0; a<hb->a.nra; a++)
422 for (h=0; h<hb->d.nhydro[d]; h++)
423 srenew(hb->hbE.E[d][a][h], nframes);
425 hb->hbE.nframes += DELTAFRAMES_HBE;
429 static t_E calcHbEnergy(int d, int a, int h, rvec x[], t_EEst EEst,
430 matrix box, rvec hbox, t_donors *donors){
431 /* d - donor atom
432 * a - acceptor atom
433 * h - hydrogen
434 * alpha - angle between dipoles
435 * x[] - atomic positions
436 * EEst - the type of energy estimate (see enum in hbplugin.h)
437 * box - the box vectors \
438 * hbox - half box lengths _These two are only needed for the pbc correction
441 t_E E;
442 rvec dist;
443 rvec dipole[2], xmol[3], xmean[2];
444 int i;
445 real r, realE;
447 if (d == a)
448 /* Self-interaction */
449 return NONSENSE_E;
451 switch (EEst)
453 case NN_BINARY:
454 /* This is a simple binary existence function that sets E=1 whenever
455 * the distance between the oxygens is equal too or less than 0.35 nm.
457 rvec_sub(x[d], x[a], dist);
458 pbc_correct_gem(dist, box, hbox);
459 if (norm(dist) <= 0.35)
460 E = 1;
461 else
462 E = 0;
463 break;
465 case NN_1_over_r3:
466 /* Negative potential energy of a dipole.
467 * E = -cos(alpha) * 1/r^3 */
469 copy_rvec(x[d], xmol[0]); /* donor */
470 copy_rvec(x[donors->hydro[donors->dptr[d]][0]], xmol[1]); /* hydrogen */
471 copy_rvec(x[donors->hydro[donors->dptr[d]][1]], xmol[2]); /* hydrogen */
473 svmul(15.9994*(1/1.008), xmol[0], xmean[0]);
474 rvec_inc(xmean[0], xmol[1]);
475 rvec_inc(xmean[0], xmol[2]);
476 for(i=0; i<3; i++)
477 xmean[0][i] /= (15.9994 + 1.008 + 1.008)/1.008;
479 /* Assumes that all acceptors are also donors. */
480 copy_rvec(x[a], xmol[0]); /* acceptor */
481 copy_rvec(x[donors->hydro[donors->dptr[a]][0]], xmol[1]); /* hydrogen */
482 copy_rvec(x[donors->hydro[donors->dptr[a]][1]], xmol[2]); /* hydrogen */
485 svmul(15.9994*(1/1.008), xmol[0], xmean[1]);
486 rvec_inc(xmean[1], xmol[1]);
487 rvec_inc(xmean[1], xmol[2]);
488 for(i=0; i<3; i++)
489 xmean[1][i] /= (15.9994 + 1.008 + 1.008)/1.008;
491 rvec_sub(xmean[0], xmean[1], dist);
492 pbc_correct_gem(dist, box, hbox);
493 r = norm(dist);
495 realE = pow(r, -3.0);
496 E = (t_E)(SCALEFACTOR_E * realE);
497 break;
499 case NN_dipole:
500 /* Negative potential energy of a (unpolarizable) dipole.
501 * E = -cos(alpha) * 1/r^3 */
502 clear_rvec(dipole[1]);
503 clear_rvec(dipole[0]);
505 copy_rvec(x[d], xmol[0]); /* donor */
506 copy_rvec(x[donors->hydro[donors->dptr[d]][0]], xmol[1]); /* hydrogen */
507 copy_rvec(x[donors->hydro[donors->dptr[d]][1]], xmol[2]); /* hydrogen */
509 rvec_inc(dipole[0], xmol[1]);
510 rvec_inc(dipole[0], xmol[2]);
511 for (i=0; i<3; i++)
512 dipole[0][i] *= 0.5;
513 rvec_dec(dipole[0], xmol[0]);
515 svmul(15.9994*(1/1.008), xmol[0], xmean[0]);
516 rvec_inc(xmean[0], xmol[1]);
517 rvec_inc(xmean[0], xmol[2]);
518 for(i=0; i<3; i++)
519 xmean[0][i] /= (15.9994 + 1.008 + 1.008)/1.008;
521 /* Assumes that all acceptors are also donors. */
522 copy_rvec(x[a], xmol[0]); /* acceptor */
523 copy_rvec(x[donors->hydro[donors->dptr[a]][0]], xmol[1]); /* hydrogen */
524 copy_rvec(x[donors->hydro[donors->dptr[a]][2]], xmol[2]); /* hydrogen */
527 rvec_inc(dipole[1], xmol[1]);
528 rvec_inc(dipole[1], xmol[2]);
529 for (i=0; i<3; i++)
530 dipole[1][i] *= 0.5;
531 rvec_dec(dipole[1], xmol[0]);
533 svmul(15.9994*(1/1.008), xmol[0], xmean[1]);
534 rvec_inc(xmean[1], xmol[1]);
535 rvec_inc(xmean[1], xmol[2]);
536 for(i=0; i<3; i++)
537 xmean[1][i] /= (15.9994 + 1.008 + 1.008)/1.008;
539 rvec_sub(xmean[0], xmean[1], dist);
540 pbc_correct_gem(dist, box, hbox);
541 r = norm(dist);
543 double cosalpha = cos_angle(dipole[0],dipole[1]);
544 realE = cosalpha * pow(r, -3.0);
545 E = (t_E)(SCALEFACTOR_E * realE);
546 break;
548 default:
549 printf("Can't do that type of energy estimate: %i\n.", EEst);
550 E = NONSENSE_E;
553 return E;
556 static void storeHbEnergy(t_hbdata *hb, int d, int a, int h, t_E E, int frame){
557 /* hb - hbond data structure
558 d - donor
559 a - acceptor
560 h - hydrogen
561 E - estimate of the energy
562 frame - the current frame.
565 /* Store the estimated energy */
566 if (E == NONSENSE_E)
567 E = 0;
569 hb->hbE.E[d][a][h][frame] = E;
570 #ifdef HAVE_OPENMP
571 #pragma omp critical
572 #endif
574 hb->hbE.Etot[frame] += E;
577 #endif /* HAVE_NN_LOOPS */
580 /* Finds -v[] in the periodicity index */
581 static int findMirror(PSTYPE p, ivec v[], PSTYPE nper)
583 PSTYPE i;
584 ivec u;
585 for (i=0; i<nper; i++){
586 if (v[i][XX] == -(v[p][XX]) &&
587 v[i][YY] == -(v[p][YY]) &&
588 v[i][ZZ] == -(v[p][ZZ]))
589 return (int)i;
591 printf("Couldn't find mirror of [%i, %i, %i], index \n",
592 v[p][XX],
593 v[p][YY],
594 v[p][ZZ]);
595 return -1;
599 static void add_frames(t_hbdata *hb,int nframes)
601 int i,j,k,l;
603 if (nframes >= hb->max_frames) {
604 hb->max_frames += 4096;
605 srenew(hb->time,hb->max_frames);
606 srenew(hb->nhb,hb->max_frames);
607 srenew(hb->ndist,hb->max_frames);
608 srenew(hb->n_bound,hb->max_frames);
609 srenew(hb->nhx,hb->max_frames);
610 if (hb->bDAnr)
611 srenew(hb->danr,hb->max_frames);
613 hb->nframes=nframes;
616 #define OFFSET(frame) (frame / 32)
617 #define MASK(frame) (1 << (frame % 32))
619 static void _set_hb(unsigned int hbexist[],unsigned int frame,gmx_bool bValue)
621 if (bValue)
622 hbexist[OFFSET(frame)] |= MASK(frame);
623 else
624 hbexist[OFFSET(frame)] &= ~MASK(frame);
627 static gmx_bool is_hb(unsigned int hbexist[],int frame)
629 return ((hbexist[OFFSET(frame)] & MASK(frame)) != 0) ? 1 : 0;
632 static void set_hb(t_hbdata *hb,int id,int ih, int ia,int frame,int ihb)
634 unsigned int *ghptr=NULL;
636 if (ihb == hbHB)
637 ghptr = hb->hbmap[id][ia]->h[ih];
638 else if (ihb == hbDist)
639 ghptr = hb->hbmap[id][ia]->g[ih];
640 else
641 gmx_fatal(FARGS,"Incomprehensible iValue %d in set_hb",ihb);
643 _set_hb(ghptr,frame-hb->hbmap[id][ia]->n0,TRUE);
646 static void addPshift(t_pShift *pHist, PSTYPE p, int frame)
648 if (pHist->len == 0) {
649 snew(pHist->frame, 1);
650 snew(pHist->p, 1);
651 pHist->len = 1;
652 pHist->frame[0] = frame;
653 pHist->p[0] = p;
654 return;
655 } else
656 if (pHist->p[pHist->len-1] != p) {
657 pHist->len++;
658 srenew(pHist->frame, pHist->len);
659 srenew(pHist->p, pHist->len);
660 pHist->frame[pHist->len-1] = frame;
661 pHist->p[pHist->len-1] = p;
662 } /* Otherwise, there is no transition. */
663 return;
666 static PSTYPE getPshift(t_pShift pHist, int frame)
668 int f, i;
670 if (pHist.len == 0
671 || (pHist.len > 0 && pHist.frame[0]>frame))
672 return -1;
674 for (i=0; i<pHist.len; i++)
676 f = pHist.frame[i];
677 if (f==frame)
678 return pHist.p[i];
679 if (f>frame)
680 return pHist.p[i-1];
683 /* It seems that frame is after the last periodic transition. Return the last periodicity. */
684 return pHist.p[pHist.len-1];
687 static void add_ff(t_hbdata *hbd,int id,int h,int ia,int frame,int ihb, PSTYPE p)
689 int i,j,n;
690 t_hbond *hb = hbd->hbmap[id][ia];
691 int maxhydro = min(hbd->maxhydro,hbd->d.nhydro[id]);
692 int wlen = hbd->wordlen;
693 int delta = 32*wlen;
694 gmx_bool bGem = hbd->bGem;
696 if (!hb->h[0]) {
697 hb->n0 = frame;
698 hb->maxframes = delta;
699 for(i=0; (i<maxhydro); i++) {
700 snew(hb->h[i],hb->maxframes/wlen);
701 snew(hb->g[i],hb->maxframes/wlen);
703 } else {
704 hb->nframes = frame-hb->n0;
705 /* We need a while loop here because hbonds may be returning
706 * after a long time.
708 while (hb->nframes >= hb->maxframes) {
709 n = hb->maxframes + delta;
710 for(i=0; (i<maxhydro); i++) {
711 srenew(hb->h[i],n/wlen);
712 srenew(hb->g[i],n/wlen);
713 for(j=hb->maxframes/wlen; (j<n/wlen); j++) {
714 hb->h[i][j] = 0;
715 hb->g[i][j] = 0;
719 hb->maxframes = n;
722 if (frame >= 0) {
723 set_hb(hbd,id,h,ia,frame,ihb);
724 if (bGem) {
725 if (p>=hbd->per->nper)
726 gmx_fatal(FARGS, "invalid shift: p=%u, nper=%u", p, hbd->per->nper);
727 else
728 addPshift(&(hbd->per->pHist[id][ia]), p, frame);
734 static void inc_nhbonds(t_donors *ddd,int d, int h)
736 int j;
737 int dptr = ddd->dptr[d];
739 for(j=0; (j<ddd->nhydro[dptr]); j++)
740 if (ddd->hydro[dptr][j] == h) {
741 ddd->nhbonds[dptr][j]++;
742 break;
744 if (j == ddd->nhydro[dptr])
745 gmx_fatal(FARGS,"No such hydrogen %d on donor %d\n",h+1,d+1);
748 static int _acceptor_index(t_acceptors *a,int grp,atom_id i,
749 const char *file,int line)
751 int ai = a->aptr[i];
753 if (a->grp[ai] != grp) {
754 if (debug && bDebug)
755 fprintf(debug,"Acc. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
756 ai,a->grp[ai],grp,file,line);
757 return NOTSET;
759 else
760 return ai;
762 #define acceptor_index(a,grp,i) _acceptor_index(a,grp,i,__FILE__,__LINE__)
764 static int _donor_index(t_donors *d,int grp,atom_id i,const char *file,int line)
766 int di = d->dptr[i];
768 if (di == NOTSET)
769 return NOTSET;
771 if (d->grp[di] != grp) {
772 if (debug && bDebug)
773 fprintf(debug,"Don. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
774 di,d->grp[di],grp,file,line);
775 return NOTSET;
777 else
778 return di;
780 #define donor_index(d,grp,i) _donor_index(d,grp,i,__FILE__,__LINE__)
782 static gmx_bool isInterchangable(t_hbdata *hb, int d, int a, int grpa, int grpd)
784 /* g_hbond doesn't allow overlapping groups */
785 if (grpa!=grpd)
786 return FALSE;
787 return
788 donor_index(&hb->d,grpd,a) != NOTSET
789 && acceptor_index(&hb->a,grpa,d) != NOTSET;
793 static void add_hbond(t_hbdata *hb,int d,int a,int h,int grpd,int grpa,
794 int frame,gmx_bool bMerge,int ihb,gmx_bool bContact, PSTYPE p)
796 int k,id,ia,hh;
797 gmx_bool daSwap = FALSE;
799 if ((id = hb->d.dptr[d]) == NOTSET)
800 gmx_fatal(FARGS,"No donor atom %d",d+1);
801 else if (grpd != hb->d.grp[id])
802 gmx_fatal(FARGS,"Inconsistent donor groups, %d iso %d, atom %d",
803 grpd,hb->d.grp[id],d+1);
804 if ((ia = hb->a.aptr[a]) == NOTSET)
805 gmx_fatal(FARGS,"No acceptor atom %d",a+1);
806 else if (grpa != hb->a.grp[ia])
807 gmx_fatal(FARGS,"Inconsistent acceptor groups, %d iso %d, atom %d",
808 grpa,hb->a.grp[ia],a+1);
810 if (bMerge)
813 if (isInterchangable(hb, d, a, grpd, grpa) && d>a)
814 /* Then swap identity so that the id of d is lower then that of a.
816 * This should really be redundant by now, as is_hbond() now ought to return
817 * hbNo in the cases where this conditional is TRUE. */
819 daSwap = TRUE;
820 k = d;
821 d = a;
822 a = k;
824 /* Now repeat donor/acc check. */
825 if ((id = hb->d.dptr[d]) == NOTSET)
826 gmx_fatal(FARGS,"No donor atom %d",d+1);
827 else if (grpd != hb->d.grp[id])
828 gmx_fatal(FARGS,"Inconsistent donor groups, %d iso %d, atom %d",
829 grpd,hb->d.grp[id],d+1);
830 if ((ia = hb->a.aptr[a]) == NOTSET)
831 gmx_fatal(FARGS,"No acceptor atom %d",a+1);
832 else if (grpa != hb->a.grp[ia])
833 gmx_fatal(FARGS,"Inconsistent acceptor groups, %d iso %d, atom %d",
834 grpa,hb->a.grp[ia],a+1);
838 if (hb->hbmap) {
839 /* Loop over hydrogens to find which hydrogen is in this particular HB */
840 if ((ihb == hbHB) && !bMerge && !bContact) {
841 for(k=0; (k<hb->d.nhydro[id]); k++)
842 if (hb->d.hydro[id][k] == h)
843 break;
844 if (k == hb->d.nhydro[id])
845 gmx_fatal(FARGS,"Donor %d does not have hydrogen %d (a = %d)",
846 d+1,h+1,a+1);
848 else
849 k = 0;
851 if (hb->bHBmap) {
852 if (hb->hbmap[id][ia] == NULL) {
853 snew(hb->hbmap[id][ia],1);
854 snew(hb->hbmap[id][ia]->h,hb->maxhydro);
855 snew(hb->hbmap[id][ia]->g,hb->maxhydro);
857 add_ff(hb,id,k,ia,frame,ihb,p);
860 /* Strange construction with frame >=0 is a relic from old code
861 * for selected hbond analysis. It may be necessary again if that
862 * is made to work again.
864 if (frame >= 0) {
865 hh = hb->hbmap[id][ia]->history[k];
866 if (ihb == hbHB) {
867 hb->nhb[frame]++;
868 if (!(ISHB(hh))) {
869 hb->hbmap[id][ia]->history[k] = hh | 2;
870 hb->nrhb++;
873 else
875 if (ihb == hbDist) {
876 hb->ndist[frame]++;
877 if (!(ISDIST(hh))) {
878 hb->hbmap[id][ia]->history[k] = hh | 1;
879 hb->nrdist++;
884 } else {
885 if (frame >= 0) {
886 if (ihb == hbHB) {
887 hb->nhb[frame]++;
888 } else {
889 if (ihb == hbDist) {
890 hb->ndist[frame]++;
895 if (bMerge && daSwap)
896 h = hb->d.hydro[id][0];
897 /* Increment number if HBonds per H */
898 if (ihb == hbHB && !bContact)
899 inc_nhbonds(&(hb->d),d,h);
902 /* Now a redundant function. It might find use at some point though. */
903 static gmx_bool in_list(atom_id selection,int isize,atom_id *index)
905 int i;
906 gmx_bool bFound;
908 bFound=FALSE;
909 for(i=0; (i<isize) && !bFound; i++)
910 if(selection == index[i])
911 bFound=TRUE;
913 return bFound;
916 static char *mkatomname(t_atoms *atoms,int i)
918 static char buf[32];
919 int rnr;
921 rnr = atoms->atom[i].resind;
922 sprintf(buf,"%4s%d%-4s",
923 *atoms->resinfo[rnr].name,atoms->resinfo[rnr].nr,*atoms->atomname[i]);
925 return buf;
928 static void gen_datable(atom_id *index, int isize, unsigned char *datable, int natoms){
929 /* Generates table of all atoms and sets the ingroup bit for atoms in index[] */
930 int i;
932 for (i=0; i<isize; i++){
933 if (index[i] >= natoms)
934 gmx_fatal(FARGS,"Atom has index %d larger than number of atoms %d.",index[i],natoms);
935 datable[index[i]] |= INGROUP;
939 static void clear_datable_grp(unsigned char *datable, int size){
940 /* Clears group information from the table */
941 int i;
942 const char mask = !(char)INGROUP;
943 if (size > 0)
944 for (i=0;i<size;i++)
945 datable[i] &= mask;
948 static void add_acc(t_acceptors *a,int ia,int grp)
950 if (a->nra >= a->max_nra) {
951 a->max_nra += 16;
952 srenew(a->acc,a->max_nra);
953 srenew(a->grp,a->max_nra);
955 a->grp[a->nra] = grp;
956 a->acc[a->nra++] = ia;
959 static void search_acceptors(t_topology *top,int isize,
960 atom_id *index,t_acceptors *a,int grp,
961 gmx_bool bNitAcc,
962 gmx_bool bContact,gmx_bool bDoIt, unsigned char *datable)
964 int i,n;
966 if (bDoIt) {
967 for (i=0; (i<isize); i++) {
968 n = index[i];
969 if ((bContact ||
970 (((*top->atoms.atomname[n])[0] == 'O') ||
971 (bNitAcc && ((*top->atoms.atomname[n])[0] == 'N')))) &&
972 ISINGRP(datable[n])) {
973 datable[n] |= ACC; /* set the atom's acceptor flag in datable. */
974 add_acc(a,n,grp);
978 snew(a->aptr,top->atoms.nr);
979 for(i=0; (i<top->atoms.nr); i++)
980 a->aptr[i] = NOTSET;
981 for(i=0; (i<a->nra); i++)
982 a->aptr[a->acc[i]] = i;
985 static void add_h2d(int id,int ih,t_donors *ddd)
987 int i;
989 for(i=0; (i<ddd->nhydro[id]); i++)
990 if (ddd->hydro[id][i] == ih) {
991 printf("Hm. This isn't the first time I found this donor (%d,%d)\n",
992 ddd->don[id],ih);
993 break;
995 if (i == ddd->nhydro[id]) {
996 if (ddd->nhydro[id] >= MAXHYDRO)
997 gmx_fatal(FARGS,"Donor %d has more than %d hydrogens!",
998 ddd->don[id],MAXHYDRO);
999 ddd->hydro[id][i] = ih;
1000 ddd->nhydro[id]++;
1004 static void add_dh(t_donors *ddd,int id,int ih,int grp, unsigned char *datable)
1006 int i;
1008 if (ISDON(datable[id]) || !datable) {
1009 if (ddd->dptr[id] == NOTSET) { /* New donor */
1010 i = ddd->nrd;
1011 ddd->dptr[id] = i;
1012 } else
1013 i = ddd->dptr[id];
1015 if (i == ddd->nrd) {
1016 if (ddd->nrd >= ddd->max_nrd) {
1017 ddd->max_nrd += 128;
1018 srenew(ddd->don,ddd->max_nrd);
1019 srenew(ddd->nhydro,ddd->max_nrd);
1020 srenew(ddd->hydro,ddd->max_nrd);
1021 srenew(ddd->nhbonds,ddd->max_nrd);
1022 srenew(ddd->grp,ddd->max_nrd);
1024 ddd->don[ddd->nrd] = id;
1025 ddd->nhydro[ddd->nrd] = 0;
1026 ddd->grp[ddd->nrd] = grp;
1027 ddd->nrd++;
1028 } else
1029 ddd->don[i] = id;
1030 add_h2d(i,ih,ddd);
1031 } else
1032 if (datable)
1033 printf("Warning: Atom %d is not in the d/a-table!\n", id);
1036 static void search_donors(t_topology *top, int isize, atom_id *index,
1037 t_donors *ddd,int grp,gmx_bool bContact,gmx_bool bDoIt,
1038 unsigned char *datable)
1040 int i,j,nra,n;
1041 t_functype func_type;
1042 t_ilist *interaction;
1043 atom_id nr1,nr2;
1044 gmx_bool stop;
1046 if (!ddd->dptr) {
1047 snew(ddd->dptr,top->atoms.nr);
1048 for(i=0; (i<top->atoms.nr); i++)
1049 ddd->dptr[i] = NOTSET;
1052 if (bContact) {
1053 if (bDoIt)
1054 for(i=0; (i<isize); i++) {
1055 datable[index[i]] |= DON;
1056 add_dh(ddd,index[i],-1,grp,datable);
1059 else {
1060 for(func_type=0; (func_type < F_NRE); func_type++) {
1061 interaction=&(top->idef.il[func_type]);
1062 if (func_type == F_POSRES)
1063 { /* The ilist looks strange for posre. Bug in grompp?
1064 * We don't need posre interactions for hbonds anyway.*/
1065 continue;
1067 for(i=0; i < interaction->nr;
1068 i+=interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms+1) {
1069 /* next function */
1070 if (func_type != top->idef.functype[interaction->iatoms[i]]) {
1071 fprintf(stderr,"Error in func_type %s",
1072 interaction_function[func_type].longname);
1073 continue;
1076 /* check out this functype */
1077 if (func_type == F_SETTLE) {
1078 nr1=interaction->iatoms[i+1];
1080 if (ISINGRP(datable[nr1])) {
1081 if (ISINGRP(datable[nr1+1])) {
1082 datable[nr1] |= DON;
1083 add_dh(ddd,nr1,nr1+1,grp,datable);
1085 if (ISINGRP(datable[nr1+2])) {
1086 datable[nr1] |= DON;
1087 add_dh(ddd,nr1,nr1+2,grp,datable);
1091 else if (IS_CHEMBOND(func_type)) {
1092 for (j=0; j<2; j++) {
1093 nr1=interaction->iatoms[i+1+j];
1094 nr2=interaction->iatoms[i+2-j];
1095 if ((*top->atoms.atomname[nr1][0] == 'H') &&
1096 ((*top->atoms.atomname[nr2][0] == 'O') ||
1097 (*top->atoms.atomname[nr2][0] == 'N')) &&
1098 ISINGRP(datable[nr1]) && ISINGRP(datable[nr2])) {
1099 datable[nr2] |= DON;
1100 add_dh(ddd,nr2,nr1,grp,datable);
1106 #ifdef SAFEVSITES
1107 for(func_type=0; func_type < F_NRE; func_type++) {
1108 interaction=&top->idef.il[func_type];
1109 for(i=0; i < interaction->nr;
1110 i+=interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms+1) {
1111 /* next function */
1112 if (func_type != top->idef.functype[interaction->iatoms[i]])
1113 gmx_incons("function type in search_donors");
1115 if ( interaction_function[func_type].flags & IF_VSITE ) {
1116 nr1=interaction->iatoms[i+1];
1117 if ( *top->atoms.atomname[nr1][0] == 'H') {
1118 nr2=nr1-1;
1119 stop=FALSE;
1120 while (!stop && ( *top->atoms.atomname[nr2][0] == 'H'))
1121 if (nr2)
1122 nr2--;
1123 else
1124 stop=TRUE;
1125 if ( !stop && ( ( *top->atoms.atomname[nr2][0] == 'O') ||
1126 ( *top->atoms.atomname[nr2][0] == 'N') ) &&
1127 ISINGRP(datable[nr1]) && ISINGRP(datable[nr2])) {
1128 datable[nr2] |= DON;
1129 add_dh(ddd,nr2,nr1,grp,datable);
1135 #endif
1139 static t_gridcell ***init_grid(gmx_bool bBox,rvec box[],real rcut,ivec ngrid)
1141 t_gridcell ***grid;
1142 int i,y,z;
1144 if (bBox)
1145 for(i=0; i<DIM; i++)
1146 ngrid[i]=(box[i][i]/(1.2*rcut));
1148 if ( !bBox || (ngrid[XX]<3) || (ngrid[YY]<3) || (ngrid[ZZ]<3) )
1149 for(i=0; i<DIM; i++)
1150 ngrid[i]=1;
1151 else
1152 printf("\nWill do grid-seach on %dx%dx%d grid, rcut=%g\n",
1153 ngrid[XX],ngrid[YY],ngrid[ZZ],rcut);
1154 snew(grid,ngrid[ZZ]);
1155 for (z=0; z<ngrid[ZZ]; z++) {
1156 snew((grid)[z],ngrid[YY]);
1157 for (y=0; y<ngrid[YY]; y++)
1158 snew((grid)[z][y],ngrid[XX]);
1160 return grid;
1163 static void control_pHist(t_hbdata *hb, int nframes)
1165 int i,j,k;
1166 PSTYPE p;
1167 for (i=0;i<hb->d.nrd;i++)
1168 for (j=0;j<hb->a.nra;j++)
1169 if (hb->per->pHist[i][j].len != 0)
1170 for (k=hb->hbmap[i][j][0].n0; k<nframes; k++) {
1171 p = getPshift(hb->per->pHist[i][j], k);
1172 if (p>hb->per->nper)
1173 fprintf(stderr, "Weird stuff in pHist[%i][%i].p at frame %i: p=%i\n",
1174 i,j,k,p);
1178 static void reset_nhbonds(t_donors *ddd)
1180 int i,j;
1182 for(i=0; (i<ddd->nrd); i++)
1183 for(j=0; (j<MAXHH); j++)
1184 ddd->nhbonds[i][j] = 0;
1187 void pbc_correct_gem(rvec dx,matrix box,rvec hbox);
1189 static void build_grid(t_hbdata *hb,rvec x[], rvec xshell,
1190 gmx_bool bBox, matrix box, rvec hbox,
1191 real rcut, real rshell,
1192 ivec ngrid, t_gridcell ***grid)
1194 int i,m,gr,xi,yi,zi,nr;
1195 atom_id *ad;
1196 ivec grididx;
1197 rvec invdelta,dshell,xtemp={0,0,0};
1198 t_ncell *newgrid;
1199 gmx_bool bDoRshell,bInShell,bAcc;
1200 real rshell2=0;
1201 int gx,gy,gz;
1202 int dum = -1;
1204 bDoRshell = (rshell > 0);
1205 rshell2 = sqr(rshell);
1206 bInShell = TRUE;
1208 #define DBB(x) if (debug && bDebug) fprintf(debug,"build_grid, line %d, %s = %d\n",__LINE__,#x,x)
1209 DBB(dum);
1210 for(m=0; m<DIM; m++) {
1211 hbox[m]=box[m][m]*0.5;
1212 if (bBox) {
1213 invdelta[m]=ngrid[m]/box[m][m];
1214 if (1/invdelta[m] < rcut)
1215 gmx_fatal(FARGS,"Your computational box has shrunk too much.\n"
1216 "%s can not handle this situation, sorry.\n",
1217 ShortProgram());
1218 } else
1219 invdelta[m]=0;
1221 grididx[XX]=0;
1222 grididx[YY]=0;
1223 grididx[ZZ]=0;
1224 DBB(dum);
1225 /* resetting atom counts */
1226 for(gr=0; (gr<grNR); gr++) {
1227 for (zi=0; zi<ngrid[ZZ]; zi++)
1228 for (yi=0; yi<ngrid[YY]; yi++)
1229 for (xi=0; xi<ngrid[XX]; xi++) {
1230 grid[zi][yi][xi].d[gr].nr=0;
1231 grid[zi][yi][xi].a[gr].nr=0;
1233 DBB(dum);
1235 /* put atoms in grid cells */
1236 for(bAcc=FALSE; (bAcc<=TRUE); bAcc++) {
1237 if (bAcc) {
1238 nr = hb->a.nra;
1239 ad = hb->a.acc;
1241 else {
1242 nr = hb->d.nrd;
1243 ad = hb->d.don;
1245 DBB(bAcc);
1246 for(i=0; (i<nr); i++) {
1247 /* check if we are inside the shell */
1248 /* if bDoRshell=FALSE then bInShell=TRUE always */
1249 DBB(i);
1250 if ( bDoRshell ) {
1251 bInShell=TRUE;
1252 rvec_sub(x[ad[i]],xshell,dshell);
1253 if (bBox) {
1254 if (FALSE && !hb->bGem) {
1255 for(m=DIM-1; m>=0 && bInShell; m--) {
1256 if ( dshell[m] < -hbox[m] )
1257 rvec_inc(dshell,box[m]);
1258 else if ( dshell[m] >= hbox[m] )
1259 dshell[m] -= 2*hbox[m];
1260 /* if we're outside the cube, we're outside the sphere also! */
1261 if ( (dshell[m]>rshell) || (-dshell[m]>rshell) )
1262 bInShell=FALSE;
1264 } else {
1265 gmx_bool bDone = FALSE;
1266 while (!bDone)
1268 bDone = TRUE;
1269 for(m=DIM-1; m>=0 && bInShell; m--) {
1270 if ( dshell[m] < -hbox[m] ) {
1271 bDone = FALSE;
1272 rvec_inc(dshell,box[m]);
1274 if ( dshell[m] >= hbox[m] ) {
1275 bDone = FALSE;
1276 dshell[m] -= 2*hbox[m];
1280 for(m=DIM-1; m>=0 && bInShell; m--) {
1281 /* if we're outside the cube, we're outside the sphere also! */
1282 if ( (dshell[m]>rshell) || (-dshell[m]>rshell) )
1283 bInShell=FALSE;
1287 /* if we're inside the cube, check if we're inside the sphere */
1288 if (bInShell)
1289 bInShell = norm2(dshell) < rshell2;
1291 DBB(i);
1292 if ( bInShell ) {
1293 if (bBox) {
1294 if (hb->bGem)
1295 copy_rvec(x[ad[i]], xtemp);
1296 pbc_correct_gem(x[ad[i]], box, hbox);
1298 for(m=DIM-1; m>=0; m--) {
1299 if (TRUE || !hb->bGem){
1300 /* put atom in the box */
1301 while( x[ad[i]][m] < 0 )
1302 rvec_inc(x[ad[i]],box[m]);
1303 while( x[ad[i]][m] >= box[m][m] )
1304 rvec_dec(x[ad[i]],box[m]);
1306 /* determine grid index of atom */
1307 grididx[m]=x[ad[i]][m]*invdelta[m];
1308 grididx[m] = (grididx[m]+ngrid[m]) % ngrid[m];
1310 if (hb->bGem)
1311 copy_rvec(xtemp, x[ad[i]]); /* copy back */
1312 gx = grididx[XX];
1313 gy = grididx[YY];
1314 gz = grididx[ZZ];
1315 range_check(gx,0,ngrid[XX]);
1316 range_check(gy,0,ngrid[YY]);
1317 range_check(gz,0,ngrid[ZZ]);
1318 DBB(gx);
1319 DBB(gy);
1320 DBB(gz);
1321 /* add atom to grid cell */
1322 if (bAcc)
1323 newgrid=&(grid[gz][gy][gx].a[gr]);
1324 else
1325 newgrid=&(grid[gz][gy][gx].d[gr]);
1326 if (newgrid->nr >= newgrid->maxnr) {
1327 newgrid->maxnr+=10;
1328 DBB(newgrid->maxnr);
1329 srenew(newgrid->atoms, newgrid->maxnr);
1331 DBB(newgrid->nr);
1332 newgrid->atoms[newgrid->nr]=ad[i];
1333 newgrid->nr++;
1340 static void count_da_grid(ivec ngrid, t_gridcell ***grid, t_icell danr)
1342 int gr,xi,yi,zi;
1344 for(gr=0; (gr<grNR); gr++) {
1345 danr[gr]=0;
1346 for (zi=0; zi<ngrid[ZZ]; zi++)
1347 for (yi=0; yi<ngrid[YY]; yi++)
1348 for (xi=0; xi<ngrid[XX]; xi++) {
1349 danr[gr] += grid[zi][yi][xi].d[gr].nr;
1354 /* The grid loop.
1355 * Without a box, the grid is 1x1x1, so all loops are 1 long.
1356 * With a rectangular box (bTric==FALSE) all loops are 3 long.
1357 * With a triclinic box all loops are 3 long, except when a cell is
1358 * located next to one of the box edges which is not parallel to the
1359 * x/y-plane, in that case all cells in a line or layer are searched.
1360 * This could be implemented slightly more efficient, but the code
1361 * would get much more complicated.
1363 #define B(n,x,bTric,bEdge) ((n==1) ? x : bTric&&(bEdge) ? 0 : (x-1))
1364 #define E(n,x,bTric,bEdge) ((n==1) ? x : bTric&&(bEdge) ? n-1 : (x+1))
1365 #define GRIDMOD(j,n) (j+n)%(n)
1366 #define LOOPGRIDINNER(x,y,z,xx,yy,zz,xo,yo,zo,n,bTric) \
1367 for(zz=B(n[ZZ],zo,bTric,FALSE); zz<=E(n[ZZ],zo,bTric,FALSE); zz++) { \
1368 z=GRIDMOD(zz,n[ZZ]); \
1369 for(yy=B(n[YY],yo,bTric,z==0||z==n[ZZ]-1); \
1370 yy<=E(n[YY],yo,bTric,z==0||z==n[ZZ]-1); yy++) { \
1371 y=GRIDMOD(yy,n[YY]); \
1372 for(xx=B(n[XX],xo,bTric,y==0||y==n[YY]-1||z==0||z==n[ZZ]-1); \
1373 xx<=E(n[XX],xo,bTric,y==0||y==n[YY]-1||z==0||z==n[ZZ]-1); xx++) { \
1374 x=GRIDMOD(xx,n[XX]);
1375 #define ENDLOOPGRIDINNER \
1381 static void dump_grid(FILE *fp, ivec ngrid, t_gridcell ***grid)
1383 int gr,x,y,z,sum[grNR];
1385 fprintf(fp,"grid %dx%dx%d\n",ngrid[XX],ngrid[YY],ngrid[ZZ]);
1386 for (gr=0; gr<grNR; gr++) {
1387 sum[gr]=0;
1388 fprintf(fp,"GROUP %d (%s)\n",gr,grpnames[gr]);
1389 for (z=0; z<ngrid[ZZ]; z+=2) {
1390 fprintf(fp,"Z=%d,%d\n",z,z+1);
1391 for (y=0; y<ngrid[YY]; y++) {
1392 for (x=0; x<ngrid[XX]; x++) {
1393 fprintf(fp,"%3d",grid[x][y][z].d[gr].nr);
1394 sum[gr]+=grid[z][y][x].d[gr].nr;
1395 fprintf(fp,"%3d",grid[x][y][z].a[gr].nr);
1396 sum[gr]+=grid[z][y][x].a[gr].nr;
1399 fprintf(fp," | ");
1400 if ( (z+1) < ngrid[ZZ] )
1401 for (x=0; x<ngrid[XX]; x++) {
1402 fprintf(fp,"%3d",grid[z+1][y][x].d[gr].nr);
1403 sum[gr]+=grid[z+1][y][x].d[gr].nr;
1404 fprintf(fp,"%3d",grid[z+1][y][x].a[gr].nr);
1405 sum[gr]+=grid[z+1][y][x].a[gr].nr;
1407 fprintf(fp,"\n");
1411 fprintf(fp,"TOTALS:");
1412 for (gr=0; gr<grNR; gr++)
1413 fprintf(fp," %d=%d",gr,sum[gr]);
1414 fprintf(fp,"\n");
1417 /* New GMX record! 5 * in a row. Congratulations!
1418 * Sorry, only four left.
1420 static void free_grid(ivec ngrid, t_gridcell ****grid)
1422 int y,z;
1423 t_gridcell ***g = *grid;
1425 for (z=0; z<ngrid[ZZ]; z++) {
1426 for (y=0; y<ngrid[YY]; y++) {
1427 sfree(g[z][y]);
1429 sfree(g[z]);
1431 sfree(g);
1432 g=NULL;
1435 static void pbc_correct(rvec dx,matrix box,rvec hbox)
1437 int m;
1438 for(m=DIM-1; m>=0; m--) {
1439 if ( dx[m] < -hbox[m] )
1440 rvec_inc(dx,box[m]);
1441 else if ( dx[m] >= hbox[m] )
1442 rvec_dec(dx,box[m]);
1446 void pbc_correct_gem(rvec dx,matrix box,rvec hbox)
1448 int m;
1449 gmx_bool bDone = FALSE;
1450 while (!bDone) {
1451 bDone = TRUE;
1452 for(m=DIM-1; m>=0; m--) {
1453 if ( dx[m] < -hbox[m] ) {
1454 bDone = FALSE;
1455 rvec_inc(dx,box[m]);
1457 if ( dx[m] >= hbox[m] ) {
1458 bDone = FALSE;
1459 rvec_dec(dx,box[m]);
1465 /* Added argument r2cut, changed contact and implemented
1466 * use of second cut-off.
1467 * - Erik Marklund, June 29, 2006
1469 static int is_hbond(t_hbdata *hb,int grpd,int grpa,int d,int a,
1470 real rcut, real r2cut, real ccut,
1471 rvec x[], gmx_bool bBox, matrix box,rvec hbox,
1472 real *d_ha, real *ang,gmx_bool bDA,int *hhh,
1473 gmx_bool bContact, gmx_bool bMerge, PSTYPE *p)
1475 int h,hh,id,ja,ihb;
1476 rvec r_da,r_ha,r_dh, r={0, 0, 0};
1477 ivec ri;
1478 real rc2,r2c2,rda2,rha2,ca;
1479 gmx_bool HAinrange = FALSE; /* If !bDA. Needed for returning hbDist in a correct way. */
1480 gmx_bool daSwap = FALSE;
1482 if (d == a)
1483 return hbNo;
1485 if (((id = donor_index(&hb->d,grpd,d)) == NOTSET) ||
1486 ((ja = acceptor_index(&hb->a,grpa,a)) == NOTSET))
1487 return hbNo;
1489 rc2 = rcut*rcut;
1490 r2c2 = r2cut*r2cut;
1492 rvec_sub(x[d],x[a],r_da);
1493 /* Insert projection code here */
1495 if (bMerge && d>a && isInterchangable(hb, d, a, grpd, grpa))
1497 /* Then this hbond/contact will be found again, or it has already been found. */
1498 /*return hbNo;*/
1500 if (bBox){
1501 if (d>a && bMerge && isInterchangable(hb, d, a, grpd, grpa)) { /* acceptor is also a donor and vice versa? */
1502 /* return hbNo; */
1503 daSwap = TRUE; /* If so, then their history should be filed with donor and acceptor swapped. */
1505 if (hb->bGem) {
1506 copy_rvec(r_da, r); /* Save this for later */
1507 pbc_correct_gem(r_da,box,hbox);
1508 } else {
1509 pbc_correct_gem(r_da,box,hbox);
1512 rda2 = iprod(r_da,r_da);
1514 if (bContact) {
1515 if (daSwap && grpa == grpd)
1516 return hbNo;
1517 if (rda2 <= rc2){
1518 if (hb->bGem){
1519 calcBoxDistance(hb->per->P, r, ri);
1520 *p = periodicIndex(ri, hb->per, daSwap); /* find (or add) periodicity index. */
1522 return hbHB;
1524 else if (rda2 < r2c2)
1525 return hbDist;
1526 else
1527 return hbNo;
1529 *hhh = NOTSET;
1531 if (bDA && (rda2 > rc2))
1532 return hbNo;
1534 for(h=0; (h < hb->d.nhydro[id]); h++) {
1535 hh = hb->d.hydro[id][h];
1536 rha2 = rc2+1;
1537 if (!bDA) {
1538 rvec_sub(x[hh],x[a],r_ha);
1539 if (bBox) {
1540 pbc_correct_gem(r_ha,box,hbox);
1542 rha2 = iprod(r_ha,r_ha);
1545 if (hb->bGem) {
1546 calcBoxDistance(hb->per->P, r, ri);
1547 *p = periodicIndex(ri, hb->per, daSwap); /* find periodicity index. */
1550 if (bDA || (!bDA && (rha2 <= rc2))) {
1551 rvec_sub(x[d],x[hh],r_dh);
1552 if (bBox) {
1553 if (hb->bGem)
1554 pbc_correct_gem(r_dh,box,hbox);
1555 else
1556 pbc_correct_gem(r_dh,box,hbox);
1559 if (!bDA)
1560 HAinrange = TRUE;
1561 ca = cos_angle(r_dh,r_da);
1562 /* if angle is smaller, cos is larger */
1563 if (ca >= ccut) {
1564 *hhh = hh;
1565 *d_ha = sqrt(bDA?rda2:rha2);
1566 *ang = acos(ca);
1567 return hbHB;
1571 if (bDA || (!bDA && HAinrange))
1572 return hbDist;
1573 else
1574 return hbNo;
1577 /* Fixed previously undiscovered bug in the merge
1578 code, where the last frame of each hbond disappears.
1579 - Erik Marklund, June 1, 2006 */
1580 /* Added the following arguments:
1581 * ptmp[] - temporary periodicity hisory
1582 * a1 - identity of first acceptor/donor
1583 * a2 - identity of second acceptor/donor
1584 * - Erik Marklund, FEB 20 2010 */
1586 /* Merging is now done on the fly, so do_merge is most likely obsolete now.
1587 * Will do some more testing before removing the function entirely.
1588 * - Erik Marklund, MAY 10 2010 */
1589 static void do_merge(t_hbdata *hb,int ntmp,
1590 unsigned int htmp[],unsigned int gtmp[],PSTYPE ptmp[],
1591 t_hbond *hb0,t_hbond *hb1, int a1, int a2)
1593 /* Here we need to make sure we're treating periodicity in
1594 * the right way for the geminate recombination kinetics. */
1596 int m,mm,n00,n01,nn0,nnframes;
1597 PSTYPE pm;
1598 t_pShift *pShift;
1600 /* Decide where to start from when merging */
1601 n00 = hb0->n0;
1602 n01 = hb1->n0;
1603 nn0 = min(n00,n01);
1604 nnframes = max(n00 + hb0->nframes,n01 + hb1->nframes) - nn0;
1605 /* Initiate tmp arrays */
1606 for(m=0; (m<ntmp); m++) {
1607 htmp[m] = 0;
1608 gtmp[m] = 0;
1609 ptmp[m] = 0;
1611 /* Fill tmp arrays with values due to first HB */
1612 /* Once again '<' had to be replaced with '<='
1613 to catch the last frame in which the hbond
1614 appears.
1615 - Erik Marklund, June 1, 2006 */
1616 for(m=0; (m<=hb0->nframes); m++) {
1617 mm = m+n00-nn0;
1618 htmp[mm] = is_hb(hb0->h[0],m);
1619 if (hb->bGem) {
1620 pm = getPshift(hb->per->pHist[a1][a2], m+hb0->n0);
1621 if (pm > hb->per->nper)
1622 gmx_fatal(FARGS, "Illegal shift!");
1623 else
1624 ptmp[mm] = pm; /*hb->per->pHist[a1][a2][m];*/
1627 /* If we're doing geminate recompbination we usually don't need the distances.
1628 * Let's save some memory and time. */
1629 if (TRUE || !hb->bGem || hb->per->gemtype == gemAD){
1630 for(m=0; (m<=hb0->nframes); m++) {
1631 mm = m+n00-nn0;
1632 gtmp[mm] = is_hb(hb0->g[0],m);
1635 /* Next HB */
1636 for(m=0; (m<=hb1->nframes); m++) {
1637 mm = m+n01-nn0;
1638 htmp[mm] = htmp[mm] || is_hb(hb1->h[0],m);
1639 gtmp[mm] = gtmp[mm] || is_hb(hb1->g[0],m);
1640 if (hb->bGem /* && ptmp[mm] != 0 */) {
1642 /* If this hbond has been seen before with donor and acceptor swapped,
1643 * then we need to find the mirrored (*-1) periodicity vector to truely
1644 * merge the hbond history. */
1645 pm = findMirror(getPshift(hb->per->pHist[a2][a1],m+hb1->n0), hb->per->p2i, hb->per->nper);
1646 /* Store index of mirror */
1647 if (pm > hb->per->nper)
1648 gmx_fatal(FARGS, "Illegal shift!");
1649 ptmp[mm] = pm;
1652 /* Reallocate target array */
1653 if (nnframes > hb0->maxframes) {
1654 srenew(hb0->h[0],4+nnframes/hb->wordlen);
1655 srenew(hb0->g[0],4+nnframes/hb->wordlen);
1657 if (NULL != hb->per->pHist)
1659 clearPshift(&(hb->per->pHist[a1][a2]));
1662 /* Copy temp array to target array */
1663 for(m=0; (m<=nnframes); m++) {
1664 _set_hb(hb0->h[0],m,htmp[m]);
1665 _set_hb(hb0->g[0],m,gtmp[m]);
1666 if (hb->bGem)
1667 addPshift(&(hb->per->pHist[a1][a2]), ptmp[m], m+nn0);
1670 /* Set scalar variables */
1671 hb0->n0 = nn0;
1672 hb0->maxframes = nnframes;
1675 /* Added argument bContact for nicer output.
1676 * Erik Marklund, June 29, 2006
1678 static void merge_hb(t_hbdata *hb,gmx_bool bTwo, gmx_bool bContact){
1679 int i,inrnew,indnew,j,ii,jj,m,id,ia,grp,ogrp,ntmp;
1680 unsigned int *htmp,*gtmp;
1681 PSTYPE *ptmp;
1682 t_hbond *hb0,*hb1;
1684 inrnew = hb->nrhb;
1685 indnew = hb->nrdist;
1687 /* Check whether donors are also acceptors */
1688 printf("Merging hbonds with Acceptor and Donor swapped\n");
1690 ntmp = 2*hb->max_frames;
1691 snew(gtmp,ntmp);
1692 snew(htmp,ntmp);
1693 snew(ptmp,ntmp);
1694 for(i=0; (i<hb->d.nrd); i++) {
1695 fprintf(stderr,"\r%d/%d",i+1,hb->d.nrd);
1696 id = hb->d.don[i];
1697 ii = hb->a.aptr[id];
1698 for(j=0; (j<hb->a.nra); j++) {
1699 ia = hb->a.acc[j];
1700 jj = hb->d.dptr[ia];
1701 if ((id != ia) && (ii != NOTSET) && (jj != NOTSET) &&
1702 (!bTwo || (bTwo && (hb->d.grp[i] != hb->a.grp[j])))) {
1703 hb0 = hb->hbmap[i][j];
1704 hb1 = hb->hbmap[jj][ii];
1705 if (hb0 && hb1 && ISHB(hb0->history[0]) && ISHB(hb1->history[0])) {
1706 do_merge(hb,ntmp,htmp,gtmp,ptmp,hb0,hb1,i,j);
1707 if (ISHB(hb1->history[0]))
1708 inrnew--;
1709 else if (ISDIST(hb1->history[0]))
1710 indnew--;
1711 else
1712 if (bContact)
1713 gmx_incons("No contact history");
1714 else
1715 gmx_incons("Neither hydrogen bond nor distance");
1716 sfree(hb1->h[0]);
1717 sfree(hb1->g[0]);
1718 if (hb->bGem) {
1719 clearPshift(&(hb->per->pHist[jj][ii]));
1721 hb1->h[0] = NULL;
1722 hb1->g[0] = NULL;
1723 hb1->history[0] = hbNo;
1728 fprintf(stderr,"\n");
1729 printf("- Reduced number of hbonds from %d to %d\n",hb->nrhb,inrnew);
1730 printf("- Reduced number of distances from %d to %d\n",hb->nrdist,indnew);
1731 hb->nrhb = inrnew;
1732 hb->nrdist = indnew;
1733 sfree(gtmp);
1734 sfree(htmp);
1735 sfree(ptmp);
1738 static void do_nhb_dist(FILE *fp,t_hbdata *hb,real t)
1740 int i,j,k,n_bound[MAXHH],nbtot;
1741 h_id nhb;
1744 /* Set array to 0 */
1745 for(k=0; (k<MAXHH); k++)
1746 n_bound[k] = 0;
1747 /* Loop over possible donors */
1748 for(i=0; (i<hb->d.nrd); i++) {
1749 for(j=0; (j<hb->d.nhydro[i]); j++)
1750 n_bound[hb->d.nhbonds[i][j]]++;
1752 fprintf(fp,"%12.5e",t);
1753 nbtot = 0;
1754 for(k=0; (k<MAXHH); k++) {
1755 fprintf(fp," %8d",n_bound[k]);
1756 nbtot += n_bound[k]*k;
1758 fprintf(fp," %8d\n",nbtot);
1761 /* Added argument bContact in do_hblife(...). Also
1762 * added support for -contact in function body.
1763 * - Erik Marklund, May 31, 2006 */
1764 /* Changed the contact code slightly.
1765 * - Erik Marklund, June 29, 2006
1767 static void do_hblife(const char *fn,t_hbdata *hb,gmx_bool bMerge,gmx_bool bContact,
1768 const output_env_t oenv)
1770 FILE *fp;
1771 const char *leg[] = { "p(t)", "t p(t)" };
1772 int *histo;
1773 int i,j,j0,k,m,nh,ihb,ohb,nhydro,ndump=0;
1774 int nframes = hb->nframes;
1775 unsigned int **h;
1776 real t,x1,dt;
1777 double sum,integral;
1778 t_hbond *hbh;
1780 snew(h,hb->maxhydro);
1781 snew(histo,nframes+1);
1782 /* Total number of hbonds analyzed here */
1783 for(i=0; (i<hb->d.nrd); i++) {
1784 for(k=0; (k<hb->a.nra); k++) {
1785 hbh = hb->hbmap[i][k];
1786 if (hbh) {
1787 if (bMerge) {
1788 if (hbh->h[0]) {
1789 h[0] = hbh->h[0];
1790 nhydro = 1;
1792 else
1793 nhydro = 0;
1795 else {
1796 nhydro = 0;
1797 for(m=0; (m<hb->maxhydro); m++)
1798 if (hbh->h[m]) {
1799 h[nhydro++] = bContact ? hbh->g[m] : hbh->h[m];
1802 for(nh=0; (nh<nhydro); nh++) {
1803 ohb = 0;
1804 j0 = 0;
1806 /* Changed '<' into '<=' below, just like I
1807 did in the hbm-output-loop in the main code.
1808 - Erik Marklund, May 31, 2006
1810 for(j=0; (j<=hbh->nframes); j++) {
1811 ihb = is_hb(h[nh],j);
1812 if (debug && (ndump < 10))
1813 fprintf(debug,"%5d %5d\n",j,ihb);
1814 if (ihb != ohb) {
1815 if (ihb) {
1816 j0 = j;
1818 else {
1819 histo[j-j0]++;
1821 ohb = ihb;
1824 ndump++;
1829 fprintf(stderr,"\n");
1830 if (bContact)
1831 fp = xvgropen(fn,"Uninterrupted contact lifetime",output_env_get_xvgr_tlabel(oenv),"()",oenv);
1832 else
1833 fp = xvgropen(fn,"Uninterrupted hydrogen bond lifetime",output_env_get_xvgr_tlabel(oenv),"()",
1834 oenv);
1836 xvgr_legend(fp,asize(leg),leg,oenv);
1837 j0 = nframes-1;
1838 while ((j0 > 0) && (histo[j0] == 0))
1839 j0--;
1840 sum = 0;
1841 for(i=0; (i<=j0); i++)
1842 sum+=histo[i];
1843 dt = hb->time[1]-hb->time[0];
1844 sum = dt*sum;
1845 integral = 0;
1846 for(i=1; (i<=j0); i++) {
1847 t = hb->time[i] - hb->time[0] - 0.5*dt;
1848 x1 = t*histo[i]/sum;
1849 fprintf(fp,"%8.3f %10.3e %10.3e\n",t,histo[i]/sum,x1);
1850 integral += x1;
1852 integral *= dt;
1853 ffclose(fp);
1854 printf("%s lifetime = %.2f ps\n", bContact?"Contact":"HB", integral);
1855 printf("Note that the lifetime obtained in this manner is close to useless\n");
1856 printf("Use the -ac option instead and check the Forward lifetime\n");
1857 please_cite(stdout,"Spoel2006b");
1858 sfree(h);
1859 sfree(histo);
1862 /* Changed argument bMerge into oneHB to handle contacts properly.
1863 * - Erik Marklund, June 29, 2006
1865 static void dump_ac(t_hbdata *hb,gmx_bool oneHB,int nDump)
1867 FILE *fp;
1868 int i,j,k,m,nd,ihb,idist;
1869 int nframes = hb->nframes;
1870 gmx_bool bPrint;
1871 t_hbond *hbh;
1873 if (nDump <= 0)
1874 return;
1875 fp = ffopen("debug-ac.xvg","w");
1876 for(j=0; (j<nframes); j++) {
1877 fprintf(fp,"%10.3f",hb->time[j]);
1878 for(i=nd=0; (i<hb->d.nrd) && (nd < nDump); i++) {
1879 for(k=0; (k<hb->a.nra) && (nd < nDump); k++) {
1880 bPrint = FALSE;
1881 ihb = idist = 0;
1882 hbh = hb->hbmap[i][k];
1883 if (oneHB) {
1884 if (hbh->h[0]) {
1885 ihb = is_hb(hbh->h[0],j);
1886 idist = is_hb(hbh->g[0],j);
1887 bPrint = TRUE;
1890 else {
1891 for(m=0; (m<hb->maxhydro) && !ihb ; m++) {
1892 ihb = ihb || ((hbh->h[m]) && is_hb(hbh->h[m],j));
1893 idist = idist || ((hbh->g[m]) && is_hb(hbh->g[m],j));
1895 /* This is not correct! */
1896 /* What isn't correct? -Erik M */
1897 bPrint = TRUE;
1899 if (bPrint) {
1900 fprintf(fp," %1d-%1d",ihb,idist);
1901 nd++;
1905 fprintf(fp,"\n");
1907 ffclose(fp);
1910 static real calc_dg(real tau,real temp)
1912 real kbt;
1914 kbt = BOLTZ*temp;
1915 if (tau <= 0)
1916 return -666;
1917 else
1918 return kbt*log(kbt*tau/PLANCK);
1921 typedef struct {
1922 int n0,n1,nparams,ndelta;
1923 real kkk[2];
1924 real *t,*ct,*nt,*kt,*sigma_ct,*sigma_nt,*sigma_kt;
1925 } t_luzar;
1927 #ifdef HAVE_LIBGSL
1928 #include <gsl/gsl_multimin.h>
1929 #include <gsl/gsl_sf.h>
1930 #include <gsl/gsl_version.h>
1932 static double my_f(const gsl_vector *v,void *params)
1934 t_luzar *tl = (t_luzar *)params;
1935 int i;
1936 double tol=1e-16,chi2=0;
1937 double di;
1938 real k,kp;
1940 for(i=0; (i<tl->nparams); i++) {
1941 tl->kkk[i] = gsl_vector_get(v, i);
1943 k = tl->kkk[0];
1944 kp = tl->kkk[1];
1946 for(i=tl->n0; (i<tl->n1); i+=tl->ndelta) {
1947 di=sqr(k*tl->sigma_ct[i]) + sqr(kp*tl->sigma_nt[i]) + sqr(tl->sigma_kt[i]);
1948 /*di = 1;*/
1949 if (di > tol)
1950 chi2 += sqr(k*tl->ct[i]-kp*tl->nt[i]-tl->kt[i])/di;
1952 else
1953 fprintf(stderr,"WARNING: sigma_ct = %g, sigma_nt = %g, sigma_kt = %g\n"
1954 "di = %g k = %g kp = %g\n",tl->sigma_ct[i],
1955 tl->sigma_nt[i],tl->sigma_kt[i],di,k,kp);
1957 #ifdef DEBUG
1958 chi2 = 0.3*sqr(k-0.6)+0.7*sqr(kp-1.3);
1959 #endif
1960 return chi2;
1963 static real optimize_luzar_parameters(FILE *fp,t_luzar *tl,int maxiter,
1964 real tol)
1966 real size,d2;
1967 int iter = 0;
1968 int status = 0;
1969 int i;
1971 const gsl_multimin_fminimizer_type *T;
1972 gsl_multimin_fminimizer *s;
1974 gsl_vector *x,*dx;
1975 gsl_multimin_function my_func;
1977 my_func.f = &my_f;
1978 my_func.n = tl->nparams;
1979 my_func.params = (void *) tl;
1981 /* Starting point */
1982 x = gsl_vector_alloc (my_func.n);
1983 for(i=0; (i<my_func.n); i++)
1984 gsl_vector_set (x, i, tl->kkk[i]);
1986 /* Step size, different for each of the parameters */
1987 dx = gsl_vector_alloc (my_func.n);
1988 for(i=0; (i<my_func.n); i++)
1989 gsl_vector_set (dx, i, 0.01*tl->kkk[i]);
1991 T = gsl_multimin_fminimizer_nmsimplex;
1992 s = gsl_multimin_fminimizer_alloc (T, my_func.n);
1994 gsl_multimin_fminimizer_set (s, &my_func, x, dx);
1995 gsl_vector_free (x);
1996 gsl_vector_free (dx);
1998 if (fp)
1999 fprintf(fp,"%5s %12s %12s %12s %12s\n","Iter","k","kp","NM Size","Chi2");
2001 do {
2002 iter++;
2003 status = gsl_multimin_fminimizer_iterate (s);
2005 if (status != 0)
2006 gmx_fatal(FARGS,"Something went wrong in the iteration in minimizer %s",
2007 gsl_multimin_fminimizer_name(s));
2009 d2 = gsl_multimin_fminimizer_minimum(s);
2010 size = gsl_multimin_fminimizer_size(s);
2011 status = gsl_multimin_test_size(size,tol);
2013 if (status == GSL_SUCCESS)
2014 if (fp)
2015 fprintf(fp,"Minimum found using %s at:\n",
2016 gsl_multimin_fminimizer_name(s));
2018 if (fp) {
2019 fprintf(fp,"%5d", iter);
2020 for(i=0; (i<my_func.n); i++)
2021 fprintf(fp," %12.4e",gsl_vector_get (s->x,i));
2022 fprintf (fp," %12.4e %12.4e\n",size,d2);
2025 while ((status == GSL_CONTINUE) && (iter < maxiter));
2027 gsl_multimin_fminimizer_free (s);
2029 return d2;
2032 static real quality_of_fit(real chi2,int N)
2034 return gsl_sf_gamma_inc_Q((N-2)/2.0,chi2/2.0);
2037 #else
2038 static real optimize_luzar_parameters(FILE *fp,t_luzar *tl,int maxiter,
2039 real tol)
2041 fprintf(stderr,"This program needs the GNU scientific library to work.\n");
2043 return -1;
2045 static real quality_of_fit(real chi2,int N)
2047 fprintf(stderr,"This program needs the GNU scientific library to work.\n");
2049 return -1;
2052 #endif
2054 static real compute_weighted_rates(int n,real t[],real ct[],real nt[],
2055 real kt[],real sigma_ct[],real sigma_nt[],
2056 real sigma_kt[],real *k,real *kp,
2057 real *sigma_k,real *sigma_kp,
2058 real fit_start)
2060 #define NK 10
2061 int i,j;
2062 t_luzar tl;
2063 real kkk=0,kkp=0,kk2=0,kp2=0,chi2;
2065 *sigma_k = 0;
2066 *sigma_kp = 0;
2068 for(i=0; (i<n); i++) {
2069 if (t[i] >= fit_start)
2070 break;
2072 tl.n0 = i;
2073 tl.n1 = n;
2074 tl.nparams = 2;
2075 tl.ndelta = 1;
2076 tl.t = t;
2077 tl.ct = ct;
2078 tl.nt = nt;
2079 tl.kt = kt;
2080 tl.sigma_ct = sigma_ct;
2081 tl.sigma_nt = sigma_nt;
2082 tl.sigma_kt = sigma_kt;
2083 tl.kkk[0] = *k;
2084 tl.kkk[1] = *kp;
2086 chi2 = optimize_luzar_parameters(debug,&tl,1000,1e-3);
2087 *k = tl.kkk[0];
2088 *kp = tl.kkk[1] = *kp;
2089 tl.ndelta = NK;
2090 for(j=0; (j<NK); j++) {
2091 (void) optimize_luzar_parameters(debug,&tl,1000,1e-3);
2092 kkk += tl.kkk[0];
2093 kkp += tl.kkk[1];
2094 kk2 += sqr(tl.kkk[0]);
2095 kp2 += sqr(tl.kkk[1]);
2096 tl.n0++;
2098 *sigma_k = sqrt(kk2/NK - sqr(kkk/NK));
2099 *sigma_kp = sqrt(kp2/NK - sqr(kkp/NK));
2101 return chi2;
2104 static void smooth_tail(int n,real t[],real c[],real sigma_c[],real start,
2105 const output_env_t oenv)
2107 FILE *fp;
2108 real e_1,fitparm[4];
2109 int i;
2111 e_1 = exp(-1);
2112 for(i=0; (i<n); i++)
2113 if (c[i] < e_1)
2114 break;
2115 if (i < n)
2116 fitparm[0] = t[i];
2117 else
2118 fitparm[0] = 10;
2119 fitparm[1] = 0.95;
2120 do_lmfit(n,c,sigma_c,0,t,start,t[n-1],oenv,bDebugMode(),effnEXP2,fitparm,0);
2123 void analyse_corr(int n,real t[],real ct[],real nt[],real kt[],
2124 real sigma_ct[],real sigma_nt[],real sigma_kt[],
2125 real fit_start,real temp,real smooth_tail_start,
2126 const output_env_t oenv)
2128 int i0,i;
2129 real k=1,kp=1,kow=1;
2130 real Q=0,chi22,chi2,dg,dgp,tau_hb,dtau,tau_rlx,e_1,dt,sigma_k,sigma_kp,ddg;
2131 double tmp,sn2=0,sc2=0,sk2=0,scn=0,sck=0,snk=0;
2132 gmx_bool bError = (sigma_ct != NULL) && (sigma_nt != NULL) && (sigma_kt != NULL);
2134 if (smooth_tail_start >= 0) {
2135 smooth_tail(n,t,ct,sigma_ct,smooth_tail_start,oenv);
2136 smooth_tail(n,t,nt,sigma_nt,smooth_tail_start,oenv);
2137 smooth_tail(n,t,kt,sigma_kt,smooth_tail_start,oenv);
2139 for(i0=0; (i0<n-2) && ((t[i0]-t[0]) < fit_start); i0++)
2141 if (i0 < n-2) {
2142 for(i=i0; (i<n); i++) {
2143 sc2 += sqr(ct[i]);
2144 sn2 += sqr(nt[i]);
2145 sk2 += sqr(kt[i]);
2146 sck += ct[i]*kt[i];
2147 snk += nt[i]*kt[i];
2148 scn += ct[i]*nt[i];
2150 printf("Hydrogen bond thermodynamics at T = %g K\n",temp);
2151 tmp = (sn2*sc2-sqr(scn));
2152 if ((tmp > 0) && (sn2 > 0)) {
2153 k = (sn2*sck-scn*snk)/tmp;
2154 kp = (k*scn-snk)/sn2;
2155 if (bError) {
2156 chi2 = 0;
2157 for(i=i0; (i<n); i++) {
2158 chi2 += sqr(k*ct[i]-kp*nt[i]-kt[i]);
2160 chi22 = compute_weighted_rates(n,t,ct,nt,kt,sigma_ct,sigma_nt,
2161 sigma_kt,&k,&kp,
2162 &sigma_k,&sigma_kp,fit_start);
2163 Q = quality_of_fit(chi2,2);
2164 ddg = BOLTZ*temp*sigma_k/k;
2165 printf("Fitting paramaters chi^2 = %10g, Quality of fit = %10g\n",
2166 chi2,Q);
2167 printf("The Rate and Delta G are followed by an error estimate\n");
2168 printf("----------------------------------------------------------\n"
2169 "Type Rate (1/ps) Sigma Time (ps) DG (kJ/mol) Sigma\n");
2170 printf("Forward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
2171 k,sigma_k,1/k,calc_dg(1/k,temp),ddg);
2172 ddg = BOLTZ*temp*sigma_kp/kp;
2173 printf("Backward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
2174 kp,sigma_kp,1/kp,calc_dg(1/kp,temp),ddg);
2176 else {
2177 chi2 = 0;
2178 for(i=i0; (i<n); i++) {
2179 chi2 += sqr(k*ct[i]-kp*nt[i]-kt[i]);
2181 printf("Fitting parameters chi^2 = %10g\nQ = %10g\n",
2182 chi2,Q);
2183 printf("--------------------------------------------------\n"
2184 "Type Rate (1/ps) Time (ps) DG (kJ/mol) Chi^2\n");
2185 printf("Forward %10.3f %8.3f %10.3f %10g\n",
2186 k,1/k,calc_dg(1/k,temp),chi2);
2187 printf("Backward %10.3f %8.3f %10.3f\n",
2188 kp,1/kp,calc_dg(1/kp,temp));
2191 if (sc2 > 0) {
2192 kow = 2*sck/sc2;
2193 printf("One-way %10.3f %s%8.3f %10.3f\n",
2194 kow,bError ? " " : "",1/kow,calc_dg(1/kow,temp));
2196 else
2197 printf(" - Numerical problems computing HB thermodynamics:\n"
2198 "sc2 = %g sn2 = %g sk2 = %g sck = %g snk = %g scn = %g\n",
2199 sc2,sn2,sk2,sck,snk,scn);
2200 /* Determine integral of the correlation function */
2201 tau_hb = evaluate_integral(n,t,ct,NULL,(t[n-1]-t[0])/2,&dtau);
2202 printf("Integral %10.3f %s%8.3f %10.3f\n",1/tau_hb,
2203 bError ? " " : "",tau_hb,calc_dg(tau_hb,temp));
2204 e_1 = exp(-1);
2205 for(i=0; (i<n-2); i++) {
2206 if ((ct[i] > e_1) && (ct[i+1] <= e_1)) {
2207 break;
2210 if (i < n-2) {
2211 /* Determine tau_relax from linear interpolation */
2212 tau_rlx = t[i]-t[0] + (e_1-ct[i])*(t[i+1]-t[i])/(ct[i+1]-ct[i]);
2213 printf("Relaxation %10.3f %8.3f %s%10.3f\n",1/tau_rlx,
2214 tau_rlx,bError ? " " : "",
2215 calc_dg(tau_rlx,temp));
2218 else
2219 printf("Correlation functions too short to compute thermodynamics\n");
2222 void compute_derivative(int nn,real x[],real y[],real dydx[])
2224 int j;
2226 /* Compute k(t) = dc(t)/dt */
2227 for(j=1; (j<nn-1); j++)
2228 dydx[j] = (y[j+1]-y[j-1])/(x[j+1]-x[j-1]);
2229 /* Extrapolate endpoints */
2230 dydx[0] = 2*dydx[1] - dydx[2];
2231 dydx[nn-1] = 2*dydx[nn-2] - dydx[nn-3];
2234 static void parallel_print(int *data, int nThreads)
2236 /* This prints the donors on which each tread is currently working. */
2237 int i;
2239 fprintf(stderr, "\r");
2240 for (i=0; i<nThreads; i++)
2241 fprintf(stderr, "%-7i",data[i]);
2244 static void normalizeACF(real *ct, real *gt, int len)
2246 real ct_fac, gt_fac;
2247 int i;
2249 /* Xu and Berne use the same normalization constant */
2251 ct_fac = 1.0/ct[0];
2252 gt_fac = (gt!=NULL && gt[0]!=0) ? 1.0/gt[0] : 0;
2253 printf("Normalization for c(t) = %g for gh(t) = %g\n",ct_fac,gt_fac);
2254 for (i=0; i<len; i++)
2256 ct[i] *= ct_fac;
2257 if (gt != NULL)
2258 gt[i] *= gt_fac;
2262 /* Added argument bContact in do_hbac(...). Also
2263 * added support for -contact in the actual code.
2264 * - Erik Marklund, May 31, 2006 */
2265 /* Changed contact code and added argument R2
2266 * - Erik Marklund, June 29, 2006
2268 static void do_hbac(const char *fn,t_hbdata *hb,
2269 int nDump,gmx_bool bMerge,gmx_bool bContact, real fit_start,
2270 real temp,gmx_bool R2,real smooth_tail_start, const output_env_t oenv,
2271 t_gemParams *params, const char *gemType, int nThreads,
2272 const int NN, const gmx_bool bBallistic, const gmx_bool bGemFit)
2274 FILE *fp;
2275 int i,j,k,m,n,o,nd,ihb,idist,n2,nn,iter,nSets;
2276 const char *legNN[] = { "Ac(t)",
2277 "Ac'(t)"};
2278 static char **legGem;
2280 const char *legLuzar[] = { "Ac\\sfin sys\\v{}\\z{}(t)",
2281 "Ac(t)",
2282 "Cc\\scontact,hb\\v{}\\z{}(t)",
2283 "-dAc\\sfs\\v{}\\z{}/dt" };
2284 gmx_bool bNorm=FALSE;
2285 double nhb = 0;
2286 int nhbi=0;
2287 real *rhbex=NULL,*ht,*gt,*ght,*dght,*kt;
2288 real *ct,*p_ct,tail,tail2,dtail,ct_fac,ght_fac,*cct;
2289 const real tol = 1e-3;
2290 int nframes = hb->nframes,nf;
2291 unsigned int **h,**g;
2292 int nh,nhbonds,nhydro,ngh;
2293 t_hbond *hbh;
2294 PSTYPE p, *pfound = NULL, np;
2295 t_pShift *pHist;
2296 int *ptimes=NULL, *poff=NULL, anhb, n0, mMax=INT_MIN;
2297 real **rHbExGem = NULL;
2298 gmx_bool c;
2299 int acType;
2300 t_E *E;
2301 double *ctdouble, *timedouble, *fittedct;
2302 double fittolerance=0.1;
2304 enum {AC_NONE, AC_NN, AC_GEM, AC_LUZAR};
2307 #ifdef HAVE_OPENMP
2308 int *dondata=NULL, thisThread;
2309 #endif
2312 printf("Doing autocorrelation ");
2314 /* Decide what kind of ACF calculations to do. */
2315 if (NN > NN_NONE && NN < NN_NR) {
2316 #ifdef HAVE_NN_LOOPS
2317 acType = AC_NN;
2318 printf("using the energy estimate.\n");
2319 #else
2320 acType = AC_NONE;
2321 printf("Can't do the NN-loop. Yet.\n");
2322 #endif
2323 } else if (hb->bGem) {
2324 acType = AC_GEM;
2325 printf("according to the reversible geminate recombination model by Omer Markowitch.\n");
2327 nSets = 1 + (bBallistic ? 1:0) + (bGemFit ? 1:0);
2328 snew(legGem, nSets);
2329 for (i=0;i<nSets;i++)
2330 snew(legGem[i], 128);
2331 sprintf(legGem[0], "Ac\\s%s\\v{}\\z{}(t)", gemType);
2332 if (bBallistic)
2333 sprintf(legGem[1], "Ac'(t)");
2334 if (bGemFit)
2335 sprintf(legGem[(bBallistic ? 3:2)], "Ac\\s%s,fit\\v{}\\z{}(t)", gemType);
2337 } else {
2338 acType = AC_LUZAR;
2339 printf("according to the theory of Luzar and Chandler.\n");
2341 fflush(stdout);
2343 /* build hbexist matrix in reals for autocorr */
2344 /* Allocate memory for computing ACF (rhbex) and aggregating the ACF (ct) */
2345 n2=1;
2346 while (n2 < nframes)
2347 n2*=2;
2349 nn = nframes/2;
2351 if (acType != AC_NN ||
2352 #ifndef HAVE_OPENMP
2353 TRUE
2354 #else
2355 FALSE
2356 #endif
2358 snew(h,hb->maxhydro);
2359 snew(g,hb->maxhydro);
2362 /* Dump hbonds for debugging */
2363 dump_ac(hb,bMerge||bContact,nDump);
2365 /* Total number of hbonds analyzed here */
2366 nhbonds = 0;
2367 ngh = 0;
2368 anhb = 0;
2370 /* ------------------------------------------------
2371 * I got tired of waiting for the acf calculations
2372 * and parallelized it with openMP
2373 * set environment variable CFLAGS = "-fopenmp" when running
2374 * configure and define DOUSEOPENMP to make use of it.
2377 #ifdef HAVE_OPENMP /* ================================================= \
2378 * Set up the OpenMP stuff, |
2379 * like the number of threads and such |
2381 if (acType != AC_LUZAR)
2383 /* #if (_OPENMP >= 200805) /\* =====================\ *\/ */
2384 /* nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_thread_limit()); */
2385 /* #else */
2386 nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_num_procs());
2387 /* #endif /\* _OPENMP >= 200805 ====================/ *\/ */
2389 omp_set_num_threads(nThreads);
2390 snew(dondata, nThreads);
2391 for (i=0; i<nThreads; i++)
2392 dondata[i] = -1;
2393 printf("ACF calculations parallelized with OpenMP using %i threads.\n"
2394 "Expect close to linear scaling over this donor-loop.\n", nThreads);
2395 fflush(stdout);
2396 fprintf(stderr, "Donors: [thread no]\n");
2398 char tmpstr[7];
2399 for (i=0;i<nThreads;i++) {
2400 snprintf(tmpstr, 7, "[%i]", i);
2401 fprintf(stderr, "%-7s", tmpstr);
2404 fprintf(stderr, "\n"); /* | */
2405 } /* | */
2406 #endif /* HAVE_OPENMP ===================================================/ */
2409 /* Build the ACF according to acType */
2410 switch (acType)
2413 case AC_NN:
2414 #ifdef HAVE_NN_LOOPS
2415 /* Here we're using the estimated energy for the hydrogen bonds. */
2416 snew(ct,nn);
2417 #ifdef HAVE_OPENMP /* ==================================\ */
2418 #pragma omp parallel \
2419 private(i, j, k, nh, E, rhbex, thisThread), \
2420 default(shared)
2422 #pragma omp barrier
2423 thisThread = omp_get_thread_num();
2424 rhbex = NULL;
2425 #endif /* ==============================================/ */
2427 snew(rhbex, n2);
2428 memset(rhbex, 0, n2*sizeof(real)); /* Trust no-one, not even malloc()! */
2430 #ifdef HAVE_OPENMP /* ################################################## \
2434 #pragma omp barrier
2435 #pragma omp for schedule (dynamic)
2436 #endif
2437 for (i=0; i<hb->d.nrd; i++) /* loop over donors */
2439 #ifdef HAVE_OPENMP /* ====== Write some output ======\ */
2440 #pragma omp critical
2442 dondata[thisThread] = i;
2443 parallel_print(dondata, nThreads);
2445 #else
2446 fprintf(stderr, "\r %i", i);
2447 #endif /* ===========================================/ */
2449 for (j=0; j<hb->a.nra; j++) /* loop over acceptors */
2451 for (nh=0; nh<hb->d.nhydro[i]; nh++) /* loop over donors' hydrogens */
2453 E = hb->hbE.E[i][j][nh];
2454 if (E != NULL)
2456 for (k=0; k<nframes; k++)
2458 if (E[k] != NONSENSE_E)
2459 rhbex[k] = (real)E[k];
2462 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&(rhbex),hb->time[1]-hb->time[0],
2463 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2464 #ifdef HAVE_OPENMP
2465 #pragma omp critical
2466 #endif
2468 for(k=0; (k<nn); k++)
2469 ct[k] += rhbex[k];
2472 } /* k loop */
2473 } /* j loop */
2474 } /* i loop */
2475 sfree(rhbex);
2476 #pragma omp barrier
2477 #ifdef HAVE_OPENMP
2478 /* # */
2479 } /* End of parallel block # */
2480 /* ##############################################################/ */
2481 sfree(dondata);
2482 #endif
2483 normalizeACF(ct, NULL, nn);
2484 snew(ctdouble, nn);
2485 snew(timedouble, nn);
2486 for (j=0; j<nn; j++)
2488 timedouble[j]=(double)(hb->time[j]);
2489 ctdouble[j]=(double)(ct[j]);
2492 /* Remove ballistic term */
2493 /* Ballistic component removal and fitting to the reversible geminate recombination model
2494 * will be taken out for the time being. First of all, one can remove the ballistic
2495 * component with g_analyze afterwards. Secondly, and more importantly, there are still
2496 * problems with the robustness of the fitting to the model. More work is needed.
2497 * A third reason is that we're currently using gsl for this and wish to reduce dependence
2498 * on external libraries. There are Levenberg-Marquardt and nsimplex solvers that come with
2499 * a BSD-licence that can do the job.
2501 * - Erik Marklund, June 18 2010.
2503 /* if (params->ballistic/params->tDelta >= params->nExpFit*2+1) */
2504 /* takeAwayBallistic(ctdouble, timedouble, nn, params->ballistic, params->nExpFit, params->bDt); */
2505 /* else */
2506 /* printf("\nNumber of data points is less than the number of parameters to fit\n." */
2507 /* "The system is underdetermined, hence no ballistic term can be found.\n\n"); */
2509 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation",output_env_get_xvgr_tlabel(oenv),"C(t)");
2510 xvgr_legend(fp,asize(legNN),legNN);
2512 for(j=0; (j<nn); j++)
2513 fprintf(fp,"%10g %10g %10g\n",
2514 hb->time[j]-hb->time[0],
2515 ct[j],
2516 ctdouble[j]);
2517 fclose(fp);
2518 sfree(ct);
2519 sfree(ctdouble);
2520 sfree(timedouble);
2521 #endif /* HAVE_NN_LOOPS */
2522 break; /* case AC_NN */
2524 case AC_GEM:
2525 snew(ct,2*n2);
2526 memset(ct,0,2*n2*sizeof(real));
2527 #ifndef HAVE_OPENMP
2528 fprintf(stderr, "Donor:\n");
2529 #define __ACDATA ct
2530 #else
2531 #define __ACDATA p_ct
2532 #endif
2534 #ifdef HAVE_OPENMP /* =========================================\
2535 * */
2536 #pragma omp parallel default(none) \
2537 private(i, k, nh, hbh, pHist, h, g, n0, nf, np, j, m, \
2538 pfound, poff, rHbExGem, p, ihb, mMax, \
2539 thisThread, p_ct) \
2540 shared(hb, dondata, ct, nn, nThreads, n2, stderr, bNorm, \
2541 nframes, bMerge, bContact)
2542 { /* ########## THE START OF THE ENORMOUS PARALLELIZED BLOCK! ########## */
2543 h = NULL;
2544 g = NULL;
2545 thisThread = omp_get_thread_num();
2546 snew(h,hb->maxhydro);
2547 snew(g,hb->maxhydro);
2548 mMax = INT_MIN;
2549 rHbExGem = NULL;
2550 poff = NULL;
2551 pfound = NULL;
2552 p_ct = NULL;
2553 snew(p_ct,2*n2);
2554 memset(p_ct,0,2*n2*sizeof(real));
2556 /* I'm using a chunk size of 1, since I expect \
2557 * the overhead to be really small compared \
2558 * to the actual calculations \ */
2559 #pragma omp for schedule(dynamic,1) nowait /* \ */
2560 #endif /* HAVE_OPENMP =========================================/ */
2562 for (i=0; i<hb->d.nrd; i++) {
2563 #ifdef HAVE_OPENMP
2564 #pragma omp critical
2566 dondata[thisThread] = i;
2567 parallel_print(dondata, nThreads);
2569 #else
2570 fprintf(stderr, "\r %i", i);
2571 #endif
2573 for (k=0; k<hb->a.nra; k++) {
2574 for (nh=0; nh < ((bMerge || bContact) ? 1 : hb->d.nhydro[i]); nh++) {
2575 hbh = hb->hbmap[i][k];
2576 if (hbh) {
2577 /* Note that if hb->per->gemtype==gemDD, then distances will be stored in
2578 * hb->hbmap[d][a].h array anyway, because the contact flag will be set.
2579 * hence, it's only with the gemAD mode that hb->hbmap[d][a].g will be used. */
2580 pHist = &(hb->per->pHist[i][k]);
2581 if (ISHB(hbh->history[nh]) && pHist->len != 0) {
2583 /* No need for a critical section */
2584 /* #ifdef HAVE_OPENMP */
2585 /* #pragma omp critical */
2586 /* #endif */
2588 h[nh] = hbh->h[nh];
2589 g[nh] = hb->per->gemtype==gemAD ? hbh->g[nh] : NULL;
2591 n0 = hbh->n0;
2592 nf = hbh->nframes;
2593 /* count the number of periodic shifts encountered and store
2594 * them in separate arrays. */
2595 np = 0;
2596 for (j=0; j<pHist->len; j++)
2598 p = pHist->p[j];
2599 for (m=0; m<=np; m++) {
2600 if (m == np) { /* p not recognized in list. Add it and set up new array. */
2601 np++;
2602 if (np>hb->per->nper)
2603 gmx_fatal(FARGS, "Too many pshifts. Something's utterly wrong here.");
2604 if (m>=mMax) { /* Extend the arrays.
2605 * Doing it like this, using mMax to keep track of the sizes,
2606 * eleviates the need for freeing and re-allocating the arrays
2607 * when taking on the next donor-acceptor pair */
2608 mMax = m;
2609 srenew(pfound,np); /* The list of found periodic shifts. */
2610 srenew(rHbExGem,np); /* The hb existence functions (-aver_hb). */
2611 snew(rHbExGem[m],2*n2);
2612 srenew(poff,np);
2615 /* This shouldn't have to be critical, right? */
2616 /* #ifdef HAVE_OPENMP */
2617 /* #pragma omp critical */
2618 /* #endif */
2620 if (rHbExGem != NULL && rHbExGem[m] != NULL) {
2621 /* This must be done, as this array was most likey
2622 * used to store stuff in some previous iteration. */
2623 memset(rHbExGem[m], 0, (sizeof(real)) * (2*n2));
2625 else
2626 fprintf(stderr, "rHbExGem not initialized! m = %i\n", m);
2628 pfound[m] = p;
2629 poff[m] = -1;
2631 break;
2632 } /* m==np */
2633 if (p == pfound[m])
2634 break;
2635 } /* m: Loop over found shifts */
2636 } /* j: Loop over shifts */
2638 /* Now unpack and disentangle the existence funtions. */
2639 for (j=0; j<nf; j++) {
2640 /* i: donor,
2641 * k: acceptor
2642 * nh: hydrogen
2643 * j: time
2644 * p: periodic shift
2645 * pfound: list of periodic shifts found for this pair.
2646 * poff: list of frame offsets; that is, the first
2647 * frame a hbond has a particular periodic shift. */
2648 p = getPshift(*pHist, j+n0);
2649 if (p != -1)
2651 for (m=0; m<np; m++)
2653 if (pfound[m] == p)
2654 break;
2655 if (m==(np-1))
2656 gmx_fatal(FARGS,"Shift not found, but must be there.");
2659 ihb = is_hb(h[nh],j) || ((hb->per->gemtype!=gemAD || j==0) ? FALSE : is_hb(g[nh],j));
2660 if (ihb)
2662 if (poff[m] == -1)
2663 poff[m] = j; /* Here's where the first hbond with shift p is,
2664 * relative to the start of h[0].*/
2665 if (j<poff[m])
2666 gmx_fatal(FARGS, "j<poff[m]");
2667 rHbExGem[m][j-poff[m]] += 1;
2672 /* Now, build ac. */
2673 for (m=0; m<np; m++) {
2674 if (rHbExGem[m][0]>0 && n0+poff[m]<nn/* && m==0 */) {
2675 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&(rHbExGem[m]),hb->time[1]-hb->time[0],
2676 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2677 for(j=0; (j<nn); j++)
2678 __ACDATA[j] += rHbExGem[m][j];
2680 } /* Building of ac. */
2681 } /* if (ISHB(...*/
2682 } /* if (hbh) */
2683 } /* hydrogen loop */
2684 } /* acceptor loop */
2685 } /* donor loop */
2687 for (m=0; m<=mMax; m++) {
2688 sfree(rHbExGem[m]);
2690 sfree(pfound);
2691 sfree(poff);
2692 sfree(rHbExGem);
2694 sfree(h);
2695 sfree(g);
2696 #ifdef HAVE_OPENMP /* =======================================\ */
2697 #pragma omp critical
2699 for (i=0; i<nn; i++)
2700 ct[i] += p_ct[i];
2702 sfree(p_ct);
2704 } /* ########## THE END OF THE ENORMOUS PARALLELIZED BLOCK ########## */
2705 sfree(dondata);
2706 #endif /* HAVE_OPENMP =======================================/ */
2708 normalizeACF(ct, NULL, nn);
2710 fprintf(stderr, "\n\nACF successfully calculated.\n");
2712 /* Use this part to fit to geminate recombination - JCP 129, 84505 (2008) */
2714 snew(ctdouble, nn);
2715 snew(timedouble, nn);
2716 snew(fittedct, nn);
2718 for (j=0;j<nn;j++){
2719 timedouble[j]=(double)(hb->time[j]);
2720 ctdouble[j]=(double)(ct[j]);
2723 /* Remove ballistic term */
2724 /* Ballistic component removal and fitting to the reversible geminate recombination model
2725 * will be taken out for the time being. First of all, one can remove the ballistic
2726 * component with g_analyze afterwards. Secondly, and more importantly, there are still
2727 * problems with the robustness of the fitting to the model. More work is needed.
2728 * A third reason is that we're currently using gsl for this and wish to reduce dependence
2729 * on external libraries. There are Levenberg-Marquardt and nsimplex solvers that come with
2730 * a BSD-licence that can do the job.
2732 * - Erik Marklund, June 18 2010.
2734 /* if (bBallistic) { */
2735 /* if (params->ballistic/params->tDelta >= params->nExpFit*2+1) */
2736 /* takeAwayBallistic(ctdouble, timedouble, nn, params->ballistic, params->nExpFit, params->bDt); */
2737 /* else */
2738 /* printf("\nNumber of data points is less than the number of parameters to fit\n." */
2739 /* "The system is underdetermined, hence no ballistic term can be found.\n\n"); */
2740 /* } */
2741 /* if (bGemFit) */
2742 /* fitGemRecomb(ctdouble, timedouble, &fittedct, nn, params); */
2745 if (bContact)
2746 fp = xvgropen(fn, "Contact Autocorrelation",output_env_get_xvgr_tlabel(oenv),"C(t)",oenv);
2747 else
2748 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation",output_env_get_xvgr_tlabel(oenv),"C(t)",oenv);
2749 xvgr_legend(fp,asize(legGem),(const char**)legGem,oenv);
2751 for(j=0; (j<nn); j++)
2753 fprintf(fp, "%10g %10g", hb->time[j]-hb->time[0],ct[j]);
2754 if (bBallistic)
2755 fprintf(fp," %10g", ctdouble[j]);
2756 if (bGemFit)
2757 fprintf(fp," %10g", fittedct[j]);
2758 fprintf(fp,"\n");
2760 fclose(fp);
2762 sfree(ctdouble);
2763 sfree(timedouble);
2764 sfree(fittedct);
2765 sfree(ct);
2767 break; /* case AC_GEM */
2769 case AC_LUZAR:
2770 snew(rhbex,2*n2);
2771 snew(ct,2*n2);
2772 snew(gt,2*n2);
2773 snew(ht,2*n2);
2774 snew(ght,2*n2);
2775 snew(dght,2*n2);
2777 snew(kt,nn);
2778 snew(cct,nn);
2780 for(i=0; (i<hb->d.nrd); i++) {
2781 for(k=0; (k<hb->a.nra); k++) {
2782 nhydro = 0;
2783 hbh = hb->hbmap[i][k];
2785 if (hbh) {
2786 if (bMerge || bContact) {
2787 if (ISHB(hbh->history[0])) {
2788 h[0] = hbh->h[0];
2789 g[0] = hbh->g[0];
2790 nhydro = 1;
2793 else {
2794 for(m=0; (m<hb->maxhydro); m++)
2795 if (bContact ? ISDIST(hbh->history[m]) : ISHB(hbh->history[m])) {
2796 g[nhydro] = hbh->g[m];
2797 h[nhydro] = hbh->h[m];
2798 nhydro++;
2802 nf = hbh->nframes;
2803 for(nh=0; (nh<nhydro); nh++) {
2804 int nrint = bContact ? hb->nrdist : hb->nrhb;
2805 if ((((nhbonds+1) % 10) == 0) || (nhbonds+1 == nrint))
2806 fprintf(stderr,"\rACF %d/%d",nhbonds+1,nrint);
2807 nhbonds++;
2808 for(j=0; (j<nframes); j++) {
2809 /* Changed '<' into '<=' below, just like I did in
2810 the hbm-output-loop in the gmx_hbond() block.
2811 - Erik Marklund, May 31, 2006 */
2812 if (j <= nf) {
2813 ihb = is_hb(h[nh],j);
2814 idist = is_hb(g[nh],j);
2816 else {
2817 ihb = idist = 0;
2819 rhbex[j] = ihb;
2820 /* For contacts: if a second cut-off is provided, use it,
2821 * otherwise use g(t) = 1-h(t) */
2822 if (!R2 && bContact)
2823 gt[j] = 1-ihb;
2824 else
2825 gt[j] = idist*(1-ihb);
2826 ht[j] = rhbex[j];
2827 nhb += ihb;
2831 /* The autocorrelation function is normalized after summation only */
2832 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&rhbex,hb->time[1]-hb->time[0],
2833 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2835 /* Cross correlation analysis for thermodynamics */
2836 for(j=nframes; (j<n2); j++) {
2837 ht[j] = 0;
2838 gt[j] = 0;
2841 cross_corr(n2,ht,gt,dght);
2843 for(j=0; (j<nn); j++) {
2844 ct[j] += rhbex[j];
2845 ght[j] += dght[j];
2851 fprintf(stderr,"\n");
2852 sfree(h);
2853 sfree(g);
2854 normalizeACF(ct, gt, nn);
2856 /* Determine tail value for statistics */
2857 tail = 0;
2858 tail2 = 0;
2859 for(j=nn/2; (j<nn); j++) {
2860 tail += ct[j];
2861 tail2 += ct[j]*ct[j];
2863 tail /= (nn - nn/2);
2864 tail2 /= (nn - nn/2);
2865 dtail = sqrt(tail2-tail*tail);
2867 /* Check whether the ACF is long enough */
2868 if (dtail > tol) {
2869 printf("\nWARNING: Correlation function is probably not long enough\n"
2870 "because the standard deviation in the tail of C(t) > %g\n"
2871 "Tail value (average C(t) over second half of acf): %g +/- %g\n",
2872 tol,tail,dtail);
2874 for(j=0; (j<nn); j++) {
2875 cct[j] = ct[j];
2876 ct[j] = (cct[j]-tail)/(1-tail);
2878 /* Compute negative derivative k(t) = -dc(t)/dt */
2879 compute_derivative(nn,hb->time,ct,kt);
2880 for(j=0; (j<nn); j++)
2881 kt[j] = -kt[j];
2884 if (bContact)
2885 fp = xvgropen(fn, "Contact Autocorrelation",output_env_get_xvgr_tlabel(oenv),"C(t)",oenv);
2886 else
2887 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation",output_env_get_xvgr_tlabel(oenv),"C(t)",oenv);
2888 xvgr_legend(fp,asize(legLuzar),legLuzar, oenv);
2891 for(j=0; (j<nn); j++)
2892 fprintf(fp,"%10g %10g %10g %10g %10g\n",
2893 hb->time[j]-hb->time[0],ct[j],cct[j],ght[j],kt[j]);
2894 ffclose(fp);
2896 analyse_corr(nn,hb->time,ct,ght,kt,NULL,NULL,NULL,
2897 fit_start,temp,smooth_tail_start,oenv);
2899 do_view(oenv,fn,NULL);
2900 sfree(rhbex);
2901 sfree(ct);
2902 sfree(gt);
2903 sfree(ht);
2904 sfree(ght);
2905 sfree(dght);
2906 sfree(cct);
2907 sfree(kt);
2908 /* sfree(h); */
2909 /* sfree(g); */
2911 break; /* case AC_LUZAR */
2913 default:
2914 gmx_fatal(FARGS, "Unrecognized type of ACF-calulation. acType = %i.", acType);
2915 } /* switch (acType) */
2918 static void init_hbframe(t_hbdata *hb,int nframes,real t)
2920 int i,j,m;
2922 hb->time[nframes] = t;
2923 hb->nhb[nframes] = 0;
2924 hb->ndist[nframes] = 0;
2925 for (i=0; (i<max_hx); i++)
2926 hb->nhx[nframes][i]=0;
2927 /* Loop invalidated */
2928 if (hb->bHBmap && 0)
2929 for (i=0; (i<hb->d.nrd); i++)
2930 for (j=0; (j<hb->a.nra); j++)
2931 for (m=0; (m<hb->maxhydro); m++)
2932 if (hb->hbmap[i][j] && hb->hbmap[i][j]->h[m])
2933 set_hb(hb,i,m,j,nframes,HB_NO);
2934 /*set_hb(hb->hbmap[i][j]->h[m],nframes-hb->hbmap[i][j]->n0,HB_NO);*/
2937 static void analyse_donor_props(const char *fn,t_hbdata *hb,int nframes,real t,
2938 const output_env_t oenv)
2940 static FILE *fp = NULL;
2941 const char *leg[] = { "Nbound", "Nfree" };
2942 int i,j,k,nbound,nb,nhtot;
2944 if (!fn)
2945 return;
2946 if (!fp) {
2947 fp = xvgropen(fn,"Donor properties",output_env_get_xvgr_tlabel(oenv),"Number",oenv);
2948 xvgr_legend(fp,asize(leg),leg,oenv);
2950 nbound = 0;
2951 nhtot = 0;
2952 for(i=0; (i<hb->d.nrd); i++) {
2953 for(k=0; (k<hb->d.nhydro[i]); k++) {
2954 nb = 0;
2955 nhtot ++;
2956 for(j=0; (j<hb->a.nra) && (nb == 0); j++) {
2957 if (hb->hbmap[i][j] && hb->hbmap[i][j]->h[k] &&
2958 is_hb(hb->hbmap[i][j]->h[k],nframes))
2959 nb = 1;
2961 nbound += nb;
2964 fprintf(fp,"%10.3e %6d %6d\n",t,nbound,nhtot-nbound);
2967 static void dump_hbmap(t_hbdata *hb,
2968 int nfile,t_filenm fnm[],gmx_bool bTwo,
2969 gmx_bool bContact, int isize[],int *index[],char *grpnames[],
2970 t_atoms *atoms)
2972 FILE *fp,*fplog;
2973 int ddd,hhh,aaa,i,j,k,m,grp;
2974 char ds[32],hs[32],as[32];
2975 gmx_bool first;
2977 fp = opt2FILE("-hbn",nfile,fnm,"w");
2978 if (opt2bSet("-g",nfile,fnm)) {
2979 fplog = ffopen(opt2fn("-g",nfile,fnm),"w");
2980 if (bContact)
2981 fprintf(fplog,"# %10s %12s %12s\n","Donor","Hydrogen","Acceptor");
2982 else
2983 fprintf(fplog,"# %10s %12s %12s\n","Donor","Hydrogen","Acceptor");
2985 else
2986 fplog = NULL;
2987 for (grp=gr0; grp<=(bTwo?gr1:gr0); grp++) {
2988 fprintf(fp,"[ %s ]",grpnames[grp]);
2989 for (i=0; i<isize[grp]; i++) {
2990 fprintf(fp,(i%15)?" ":"\n");
2991 fprintf(fp," %4u",index[grp][i]+1);
2993 fprintf(fp,"\n");
2995 Added -contact support below.
2996 - Erik Marklund, May 29, 2006
2998 if (!bContact) {
2999 fprintf(fp,"[ donors_hydrogens_%s ]\n",grpnames[grp]);
3000 for (i=0; (i<hb->d.nrd); i++) {
3001 if (hb->d.grp[i] == grp) {
3002 for(j=0; (j<hb->d.nhydro[i]); j++)
3003 fprintf(fp," %4u %4u",hb->d.don[i]+1,
3004 hb->d.hydro[i][j]+1);
3005 fprintf(fp,"\n");
3008 first = TRUE;
3009 fprintf(fp,"[ acceptors_%s ]",grpnames[grp]);
3010 for (i=0; (i<hb->a.nra); i++) {
3011 if (hb->a.grp[i] == grp) {
3012 fprintf(fp,(i%15 && !first)?" ":"\n");
3013 fprintf(fp," %4u",hb->a.acc[i]+1);
3014 first = FALSE;
3017 fprintf(fp,"\n");
3020 if (bTwo)
3021 fprintf(fp,bContact ? "[ contacts_%s-%s ]\n" :
3022 "[ hbonds_%s-%s ]\n",grpnames[0],grpnames[1]);
3023 else
3024 fprintf(fp,bContact ? "[ contacts_%s ]" : "[ hbonds_%s ]\n",grpnames[0]);
3026 for(i=0; (i<hb->d.nrd); i++) {
3027 ddd = hb->d.don[i];
3028 for(k=0; (k<hb->a.nra); k++) {
3029 aaa = hb->a.acc[k];
3030 for(m=0; (m<hb->d.nhydro[i]); m++) {
3031 if (hb->hbmap[i][k] && ISHB(hb->hbmap[i][k]->history[m])) {
3032 sprintf(ds,"%s",mkatomname(atoms,ddd));
3033 sprintf(as,"%s",mkatomname(atoms,aaa));
3034 if (bContact) {
3035 fprintf(fp," %6u %6u\n",ddd+1,aaa+1);
3036 if (fplog)
3037 fprintf(fplog,"%12s %12s\n",ds,as);
3038 } else {
3039 hhh = hb->d.hydro[i][m];
3040 sprintf(hs,"%s",mkatomname(atoms,hhh));
3041 fprintf(fp," %6u %6u %6u\n",ddd+1,hhh+1,aaa+1);
3042 if (fplog)
3043 fprintf(fplog,"%12s %12s %12s\n",ds,hs,as);
3049 ffclose(fp);
3050 if (fplog)
3051 ffclose(fplog);
3054 #ifdef HAVE_OPENMP
3055 /* sync_hbdata() updates the parallel t_hbdata p_hb using hb as template.
3056 * It mimics add_frames() and init_frame() to some extent. */
3057 static void sync_hbdata(t_hbdata *hb, t_hbdata *p_hb,
3058 int nframes, real t)
3060 int i;
3061 if (nframes >= p_hb->max_frames)
3063 p_hb->max_frames += 4096;
3064 srenew(p_hb->nhb, p_hb->max_frames);
3065 srenew(p_hb->ndist, p_hb->max_frames);
3066 srenew(p_hb->n_bound, p_hb->max_frames);
3067 srenew(p_hb->nhx, p_hb->max_frames);
3068 if (p_hb->bDAnr)
3069 srenew(p_hb->danr, p_hb->max_frames);
3070 memset(&(p_hb->nhb[nframes]), 0, sizeof(int) * (p_hb->max_frames-nframes));
3071 memset(&(p_hb->ndist[nframes]), 0, sizeof(int) * (p_hb->max_frames-nframes));
3072 p_hb->nhb[nframes] = 0;
3073 p_hb->ndist[nframes] = 0;
3076 p_hb->nframes = nframes;
3077 /* for (i=0;) */
3078 /* { */
3079 /* p_hb->nhx[nframes][i] */
3080 /* } */
3081 memset(&(p_hb->nhx[nframes]), 0 ,sizeof(int)*max_hx); /* zero the helix count for this frame */
3083 /* hb->per will remain constant througout the frame loop,
3084 * even though the data its members point to will change,
3085 * hence no need for re-syncing. */
3087 #endif
3089 int gmx_hbond(int argc,char *argv[])
3091 const char *desc[] = {
3092 "[TT]g_hbond[tt] computes and analyzes hydrogen bonds. Hydrogen bonds are",
3093 "determined based on cutoffs for the angle Acceptor - Donor - Hydrogen",
3094 "(zero is extended) and the distance Hydrogen - Acceptor.",
3095 "OH and NH groups are regarded as donors, O is an acceptor always,",
3096 "N is an acceptor by default, but this can be switched using",
3097 "[TT]-nitacc[tt]. Dummy hydrogen atoms are assumed to be connected",
3098 "to the first preceding non-hydrogen atom.[PAR]",
3100 "You need to specify two groups for analysis, which must be either",
3101 "identical or non-overlapping. All hydrogen bonds between the two",
3102 "groups are analyzed.[PAR]",
3104 "If you set [TT]-shell[tt], you will be asked for an additional index group",
3105 "which should contain exactly one atom. In this case, only hydrogen",
3106 "bonds between atoms within the shell distance from the one atom are",
3107 "considered.[PAR]",
3109 /* "It is also possible to analyse specific hydrogen bonds with",
3110 "[TT]-sel[tt]. This index file must contain a group of atom triplets",
3111 "Donor Hydrogen Acceptor, in the following way:[PAR]",
3113 "[TT]",
3114 "[ selected ][BR]",
3115 " 20 21 24[BR]",
3116 " 25 26 29[BR]",
3117 " 1 3 6[BR]",
3118 "[tt][BR]",
3119 "Note that the triplets need not be on separate lines.",
3120 "Each atom triplet specifies a hydrogen bond to be analyzed,",
3121 "note also that no check is made for the types of atoms.[PAR]",
3123 "[BB]Output:[bb][BR]",
3124 "[TT]-num[tt]: number of hydrogen bonds as a function of time.[BR]",
3125 "[TT]-ac[tt]: average over all autocorrelations of the existence",
3126 "functions (either 0 or 1) of all hydrogen bonds.[BR]",
3127 "[TT]-dist[tt]: distance distribution of all hydrogen bonds.[BR]",
3128 "[TT]-ang[tt]: angle distribution of all hydrogen bonds.[BR]",
3129 "[TT]-hx[tt]: the number of n-n+i hydrogen bonds as a function of time",
3130 "where n and n+i stand for residue numbers and i ranges from 0 to 6.",
3131 "This includes the n-n+3, n-n+4 and n-n+5 hydrogen bonds associated",
3132 "with helices in proteins.[BR]",
3133 "[TT]-hbn[tt]: all selected groups, donors, hydrogens and acceptors",
3134 "for selected groups, all hydrogen bonded atoms from all groups and",
3135 "all solvent atoms involved in insertion.[BR]",
3136 "[TT]-hbm[tt]: existence matrix for all hydrogen bonds over all",
3137 "frames, this also contains information on solvent insertion",
3138 "into hydrogen bonds. Ordering is identical to that in [TT]-hbn[tt]",
3139 "index file.[BR]",
3140 "[TT]-dan[tt]: write out the number of donors and acceptors analyzed for",
3141 "each timeframe. This is especially useful when using [TT]-shell[tt].[BR]",
3142 "[TT]-nhbdist[tt]: compute the number of HBonds per hydrogen in order to",
3143 "compare results to Raman Spectroscopy.",
3144 "[PAR]",
3145 "Note: options [TT]-ac[tt], [TT]-life[tt], [TT]-hbn[tt] and [TT]-hbm[tt]",
3146 "require an amount of memory proportional to the total numbers of donors",
3147 "times the total number of acceptors in the selected group(s)."
3150 static real acut=30, abin=1, rcut=0.35, r2cut=0, rbin=0.005, rshell=-1;
3151 static real maxnhb=0,fit_start=1,fit_end=60,temp=298.15,smooth_tail_start=-1, D=-1;
3152 static gmx_bool bNitAcc=TRUE,bDA=TRUE,bMerge=TRUE;
3153 static int nDump=0, nFitPoints=100;
3154 static int nThreads = 0, nBalExp=4;
3156 static gmx_bool bContact=FALSE, bBallistic=FALSE, bBallisticDt=FALSE, bGemFit=FALSE;
3157 static real logAfterTime = 10, gemBallistic = 0.2; /* ps */
3158 static const char *NNtype[] = {NULL, "none", "binary", "oneOverR3", "dipole", NULL};
3160 /* options */
3161 t_pargs pa [] = {
3162 { "-a", FALSE, etREAL, {&acut},
3163 "Cutoff angle (degrees, Acceptor - Donor - Hydrogen)" },
3164 { "-r", FALSE, etREAL, {&rcut},
3165 "Cutoff radius (nm, X - Acceptor, see next option)" },
3166 { "-da", FALSE, etBOOL, {&bDA},
3167 "Use distance Donor-Acceptor (if TRUE) or Hydrogen-Acceptor (FALSE)" },
3168 { "-r2", FALSE, etREAL, {&r2cut},
3169 "Second cutoff radius. Mainly useful with [TT]-contact[tt] and [TT]-ac[tt]"},
3170 { "-abin", FALSE, etREAL, {&abin},
3171 "Binwidth angle distribution (degrees)" },
3172 { "-rbin", FALSE, etREAL, {&rbin},
3173 "Binwidth distance distribution (nm)" },
3174 { "-nitacc",FALSE, etBOOL, {&bNitAcc},
3175 "Regard nitrogen atoms as acceptors" },
3176 { "-contact",FALSE,etBOOL, {&bContact},
3177 "Do not look for hydrogen bonds, but merely for contacts within the cut-off distance" },
3178 { "-shell", FALSE, etREAL, {&rshell},
3179 "when > 0, only calculate hydrogen bonds within # nm shell around "
3180 "one particle" },
3181 { "-fitstart", FALSE, etREAL, {&fit_start},
3182 "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]" },
3183 { "-fitstart", FALSE, etREAL, {&fit_start},
3184 "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])" },
3185 { "-temp", FALSE, etREAL, {&temp},
3186 "Temperature (K) for computing the Gibbs energy corresponding to HB breaking and reforming" },
3187 { "-smooth",FALSE, etREAL, {&smooth_tail_start},
3188 "If >= 0, the tail of the ACF will be smoothed by fitting it to an exponential function: y = A exp(-x/[GRK]tau[grk])" },
3189 { "-dump", FALSE, etINT, {&nDump},
3190 "Dump the first N hydrogen bond ACFs in a single [TT].xvg[tt] file for debugging" },
3191 { "-max_hb",FALSE, etREAL, {&maxnhb},
3192 "Theoretical maximum number of hydrogen bonds used for normalizing HB autocorrelation function. Can be useful in case the program estimates it wrongly" },
3193 { "-merge", FALSE, etBOOL, {&bMerge},
3194 "H-bonds between the same donor and acceptor, but with different hydrogen are treated as a single H-bond. Mainly important for the ACF." },
3195 { "-geminate", FALSE, etENUM, {gemType},
3196 "Use reversible geminate recombination for the kinetics/thermodynamics calclations. See Markovitch et al., J. Chem. Phys 129, 084505 (2008) for details."},
3197 { "-diff", FALSE, etREAL, {&D},
3198 "Dffusion coefficient to use in the reversible geminate recombination kinetic model. If negative, then it will be fitted to the ACF along with ka and kd."},
3199 #ifdef HAVE_OPENMP
3200 { "-nthreads", FALSE, etINT, {&nThreads},
3201 "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 processors (before OpenMP v.3 ) or environment variable OMP_THREAD_LIMIT (OpenMP v.3)"},
3202 #endif
3204 const char *bugs[] = {
3205 "The option [TT]-sel[tt] that used to work on selected hbonds is out of order, and therefore not available for the time being."
3207 t_filenm fnm[] = {
3208 { efTRX, "-f", NULL, ffREAD },
3209 { efTPX, NULL, NULL, ffREAD },
3210 { efNDX, NULL, NULL, ffOPTRD },
3211 /* { efNDX, "-sel", "select", ffOPTRD },*/
3212 { efXVG, "-num", "hbnum", ffWRITE },
3213 { efLOG, "-g", "hbond", ffOPTWR },
3214 { efXVG, "-ac", "hbac", ffOPTWR },
3215 { efXVG, "-dist","hbdist", ffOPTWR },
3216 { efXVG, "-ang", "hbang", ffOPTWR },
3217 { efXVG, "-hx", "hbhelix",ffOPTWR },
3218 { efNDX, "-hbn", "hbond", ffOPTWR },
3219 { efXPM, "-hbm", "hbmap", ffOPTWR },
3220 { efXVG, "-don", "donor", ffOPTWR },
3221 { efXVG, "-dan", "danum", ffOPTWR },
3222 { efXVG, "-life","hblife", ffOPTWR },
3223 { efXVG, "-nhbdist", "nhbdist",ffOPTWR }
3226 #define NFILE asize(fnm)
3228 char hbmap [HB_NR]={ ' ', 'o', '-', '*' };
3229 const char *hbdesc[HB_NR]={ "None", "Present", "Inserted", "Present & Inserted" };
3230 t_rgb hbrgb [HB_NR]={ {1,1,1},{1,0,0}, {0,0,1}, {1,0,1} };
3232 t_trxstatus *status;
3233 int trrStatus=1;
3234 t_topology top;
3235 t_inputrec ir;
3236 t_pargs *ppa;
3237 int npargs,natoms,nframes=0,shatom;
3238 int *isize;
3239 char **grpnames;
3240 atom_id **index;
3241 rvec *x,hbox;
3242 matrix box;
3243 real t,ccut,dist=0.0,ang=0.0;
3244 double max_nhb,aver_nhb,aver_dist;
3245 int h=0,i,j,k=0,l,start,end,id,ja,ogrp,nsel;
3246 int xi,yi,zi,ai;
3247 int xj,yj,zj,aj,xjj,yjj,zjj;
3248 int xk,yk,zk,ak,xkk,ykk,zkk;
3249 gmx_bool bSelected,bHBmap,bStop,bTwo,was,bBox,bTric;
3250 int *adist,*rdist;
3251 int grp,nabin,nrbin,bin,resdist,ihb;
3252 char **leg;
3253 t_hbdata *hb;
3254 FILE *fp,*fpins=NULL,*fpnhb=NULL;
3255 t_gridcell ***grid;
3256 t_ncell *icell,*jcell,*kcell;
3257 ivec ngrid;
3258 unsigned char *datable;
3259 output_env_t oenv;
3260 int gemmode, NN;
3261 PSTYPE peri=0;
3262 t_E E;
3263 int ii, jj, hh, actual_nThreads;
3264 int threadNr=0;
3265 gmx_bool bGem, bNN, bParallel;
3266 t_gemParams *params=NULL;
3268 CopyRight(stdout,argv[0]);
3270 npargs = asize(pa);
3271 ppa = add_acf_pargs(&npargs,pa);
3273 parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,NFILE,fnm,npargs,
3274 ppa,asize(desc),desc,asize(bugs),bugs,&oenv);
3276 /* NN-loop? If so, what estimator to use ?*/
3277 NN = 1;
3278 /* Outcommented for now DvdS 2010-07-13
3279 while (NN < NN_NR && gmx_strcasecmp(NNtype[0], NNtype[NN])!=0)
3280 NN++;
3281 if (NN == NN_NR)
3282 gmx_fatal(FARGS, "Invalid NN-loop type.");
3284 bNN = FALSE;
3285 for (i=2; bNN==FALSE && i<NN_NR; i++)
3286 bNN = bNN || NN == i;
3288 if (NN > NN_NONE && bMerge)
3289 bMerge = FALSE;
3291 /* geminate recombination? If so, which flavor? */
3292 gemmode = 1;
3293 while (gemmode < gemNR && gmx_strcasecmp(gemType[0], gemType[gemmode])!=0)
3294 gemmode++;
3295 if (gemmode == gemNR)
3296 gmx_fatal(FARGS, "Invalid recombination type.");
3298 bGem = FALSE;
3299 for (i=2; bGem==FALSE && i<gemNR; i++)
3300 bGem = bGem || gemmode == i;
3302 if (bGem) {
3303 printf("Geminate recombination: %s\n" ,gemType[gemmode]);
3304 #ifndef HAVE_LIBGSL
3305 printf("Note that some aspects of reversible geminate recombination won't work without gsl.\n");
3306 #endif
3307 if (bContact) {
3308 if (gemmode != gemDD) {
3309 printf("Turning off -contact option...\n");
3310 bContact = FALSE;
3312 } else {
3313 if (gemmode == gemDD) {
3314 printf("Turning on -contact option...\n");
3315 bContact = TRUE;
3318 if (bMerge) {
3319 if (gemmode == gemAA) {
3320 printf("Turning off -merge option...\n");
3321 bMerge = FALSE;
3323 } else {
3324 if (gemmode != gemAA) {
3325 printf("Turning on -merge option...\n");
3326 bMerge = TRUE;
3331 /* process input */
3332 bSelected = FALSE;
3333 ccut = cos(acut*DEG2RAD);
3335 if (bContact) {
3336 if (bSelected)
3337 gmx_fatal(FARGS,"Can not analyze selected contacts.");
3338 if (!bDA) {
3339 gmx_fatal(FARGS,"Can not analyze contact between H and A: turn off -noda");
3343 #ifndef HAVE_LIBGSL
3344 /* Don't pollute stdout with information about external libraries.
3346 * printf("NO GSL! Can't find and take away ballistic term in ACF without GSL\n.");
3348 #endif
3350 /* Initiate main data structure! */
3351 bHBmap = (opt2bSet("-ac",NFILE,fnm) ||
3352 opt2bSet("-life",NFILE,fnm) ||
3353 opt2bSet("-hbn",NFILE,fnm) ||
3354 opt2bSet("-hbm",NFILE,fnm) ||
3355 bGem);
3357 #ifdef HAVE_OPENMP
3358 /* Same thing here. There is no reason whatsoever to write the specific version of
3359 * OpenMP used for compilation to stdout for normal usage.
3361 * printf("Compiled with OpenMP (%i)\n", _OPENMP);
3363 #endif
3365 /* if (bContact && bGem) */
3366 /* gmx_fatal(FARGS, "Can't do reversible geminate recombination with -contact yet."); */
3368 if (opt2bSet("-nhbdist",NFILE,fnm)) {
3369 const char *leg[MAXHH+1] = { "0 HBs", "1 HB", "2 HBs", "3 HBs", "Total" };
3370 fpnhb = xvgropen(opt2fn("-nhbdist",NFILE,fnm),
3371 "Number of donor-H with N HBs",output_env_get_xvgr_tlabel(oenv),"N",oenv);
3372 xvgr_legend(fpnhb,asize(leg),leg,oenv);
3375 hb = mk_hbdata(bHBmap,opt2bSet("-dan",NFILE,fnm),bMerge || bContact, bGem, gemmode);
3377 /* get topology */
3378 read_tpx_top(ftp2fn(efTPX,NFILE,fnm),&ir,box,&natoms,NULL,NULL,NULL,&top);
3380 snew(grpnames,grNR);
3381 snew(index,grNR);
3382 snew(isize,grNR);
3383 /* Make Donor-Acceptor table */
3384 snew(datable, top.atoms.nr);
3385 gen_datable(index[0],isize[0],datable,top.atoms.nr);
3387 if (bSelected) {
3388 /* analyze selected hydrogen bonds */
3389 printf("Select group with selected atoms:\n");
3390 get_index(&(top.atoms),opt2fn("-sel",NFILE,fnm),
3391 1,&nsel,index,grpnames);
3392 if (nsel % 3)
3393 gmx_fatal(FARGS,"Number of atoms in group '%s' not a multiple of 3\n"
3394 "and therefore cannot contain triplets of "
3395 "Donor-Hydrogen-Acceptor",grpnames[0]);
3396 bTwo=FALSE;
3398 for(i=0; (i<nsel); i+=3) {
3399 int dd = index[0][i];
3400 int aa = index[0][i+2];
3401 /* int */ hh = index[0][i+1];
3402 add_dh (&hb->d,dd,hh,i,datable);
3403 add_acc(&hb->a,aa,i);
3404 /* Should this be here ? */
3405 snew(hb->d.dptr,top.atoms.nr);
3406 snew(hb->a.aptr,top.atoms.nr);
3407 add_hbond(hb,dd,aa,hh,gr0,gr0,0,bMerge,0,bContact,peri);
3409 printf("Analyzing %d selected hydrogen bonds from '%s'\n",
3410 isize[0],grpnames[0]);
3412 else {
3413 /* analyze all hydrogen bonds: get group(s) */
3414 printf("Specify 2 groups to analyze:\n");
3415 get_index(&(top.atoms),ftp2fn_null(efNDX,NFILE,fnm),
3416 2,isize,index,grpnames);
3418 /* check if we have two identical or two non-overlapping groups */
3419 bTwo = isize[0] != isize[1];
3420 for (i=0; (i<isize[0]) && !bTwo; i++)
3421 bTwo = index[0][i] != index[1][i];
3422 if (bTwo) {
3423 printf("Checking for overlap in atoms between %s and %s\n",
3424 grpnames[0],grpnames[1]);
3425 for (i=0; i<isize[1];i++)
3426 if (ISINGRP(datable[index[1][i]]))
3427 gmx_fatal(FARGS,"Partial overlap between groups '%s' and '%s'",
3428 grpnames[0],grpnames[1]);
3430 printf("Checking for overlap in atoms between %s and %s\n",
3431 grpnames[0],grpnames[1]);
3432 for (i=0; i<isize[0]; i++)
3433 for (j=0; j<isize[1]; j++)
3434 if (index[0][i] == index[1][j])
3435 gmx_fatal(FARGS,"Partial overlap between groups '%s' and '%s'",
3436 grpnames[0],grpnames[1]);
3439 if (bTwo)
3440 printf("Calculating %s "
3441 "between %s (%d atoms) and %s (%d atoms)\n",
3442 bContact ? "contacts" : "hydrogen bonds",
3443 grpnames[0],isize[0],grpnames[1],isize[1]);
3444 else
3445 fprintf(stderr,"Calculating %s in %s (%d atoms)\n",
3446 bContact?"contacts":"hydrogen bonds",grpnames[0],isize[0]);
3448 sfree(datable);
3450 /* search donors and acceptors in groups */
3451 snew(datable, top.atoms.nr);
3452 for (i=0; (i<grNR); i++)
3453 if ( ((i==gr0) && !bSelected ) ||
3454 ((i==gr1) && bTwo )) {
3455 gen_datable(index[i],isize[i],datable,top.atoms.nr);
3456 if (bContact) {
3457 search_acceptors(&top,isize[i],index[i],&hb->a,i,
3458 bNitAcc,TRUE,(bTwo && (i==gr0)) || !bTwo, datable);
3459 search_donors (&top,isize[i],index[i],&hb->d,i,
3460 TRUE,(bTwo && (i==gr1)) || !bTwo, datable);
3462 else {
3463 search_acceptors(&top,isize[i],index[i],&hb->a,i,bNitAcc,FALSE,TRUE, datable);
3464 search_donors (&top,isize[i],index[i],&hb->d,i,FALSE,TRUE, datable);
3466 if (bTwo)
3467 clear_datable_grp(datable,top.atoms.nr);
3469 sfree(datable);
3470 printf("Found %d donors and %d acceptors\n",hb->d.nrd,hb->a.nra);
3471 /*if (bSelected)
3472 snew(donors[gr0D], dons[gr0D].nrd);*/
3474 if (bHBmap) {
3475 printf("Making hbmap structure...");
3476 /* Generate hbond data structure */
3477 mk_hbmap(hb,bTwo);
3478 printf("done.\n");
3481 #ifdef HAVE_NN_LOOPS
3482 if (bNN)
3483 mk_hbEmap(hb, 0);
3484 #endif
3486 if (bGem) {
3487 printf("Making per structure...");
3488 /* Generate hbond data structure */
3489 mk_per(hb);
3490 printf("done.\n");
3493 /* check input */
3494 bStop=FALSE;
3495 if (hb->d.nrd + hb->a.nra == 0) {
3496 printf("No Donors or Acceptors found\n");
3497 bStop=TRUE;
3499 if (!bStop) {
3500 if (hb->d.nrd == 0) {
3501 printf("No Donors found\n");
3502 bStop=TRUE;
3504 if (hb->a.nra == 0) {
3505 printf("No Acceptors found\n");
3506 bStop=TRUE;
3509 if (bStop)
3510 gmx_fatal(FARGS,"Nothing to be done");
3512 shatom=0;
3513 if (rshell > 0) {
3514 int shisz;
3515 atom_id *shidx;
3516 char *shgrpnm;
3517 /* get index group with atom for shell */
3518 do {
3519 printf("Select atom for shell (1 atom):\n");
3520 get_index(&(top.atoms),ftp2fn_null(efNDX,NFILE,fnm),
3521 1,&shisz,&shidx,&shgrpnm);
3522 if (shisz != 1)
3523 printf("group contains %d atoms, should be 1 (one)\n",shisz);
3524 } while(shisz != 1);
3525 shatom = shidx[0];
3526 printf("Will calculate hydrogen bonds within a shell "
3527 "of %g nm around atom %i\n",rshell,shatom+1);
3530 /* Analyze trajectory */
3531 natoms=read_first_x(oenv,&status,ftp2fn(efTRX,NFILE,fnm),&t,&x,box);
3532 if ( natoms > top.atoms.nr )
3533 gmx_fatal(FARGS,"Topology (%d atoms) does not match trajectory (%d atoms)",
3534 top.atoms.nr,natoms);
3536 bBox = ir.ePBC!=epbcNONE;
3537 grid = init_grid(bBox, box, (rcut>r2cut)?rcut:r2cut, ngrid);
3538 nabin = acut/abin;
3539 nrbin = rcut/rbin;
3540 snew(adist,nabin+1);
3541 snew(rdist,nrbin+1);
3543 if (bGem && !bBox)
3544 gmx_fatal(FARGS, "Can't do geminate recombination without periodic box.");
3546 bParallel = FALSE;
3548 #ifndef HAVE_OPENMP
3549 #define __ADIST adist
3550 #define __RDIST rdist
3551 #define __HBDATA hb
3552 #else /* HAVE_OPENMP ================================================== \
3553 * Set up the OpenMP stuff, |
3554 * like the number of threads and such |
3555 * Also start the parallel loop. |
3557 #define __ADIST p_adist[threadNr]
3558 #define __RDIST p_rdist[threadNr]
3559 #define __HBDATA p_hb[threadNr]
3561 bParallel = !bSelected;
3563 if (bParallel)
3565 /* #if (_OPENMP > 200805) */
3566 /* actual_nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_thread_limit()); */
3567 /* #else */
3568 actual_nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_num_procs());
3569 /* #endif */
3570 omp_set_num_threads(actual_nThreads);
3571 printf("Frame loop parallelized with OpenMP using %i threads.\n", actual_nThreads);
3572 fflush(stdout);
3574 else
3576 actual_nThreads = 1;
3579 t_hbdata **p_hb; /* one per thread, then merge after the frame loop */
3580 int **p_adist, **p_rdist; /* a histogram for each thread. */
3581 snew(p_hb, actual_nThreads);
3582 snew(p_adist, actual_nThreads);
3583 snew(p_rdist, actual_nThreads);
3584 for (i=0; i<actual_nThreads; i++)
3586 snew(p_hb[i], 1);
3587 snew(p_adist[i], nabin+1);
3588 snew(p_rdist[i], nrbin+1);
3590 p_hb[i]->max_frames = 0;
3591 p_hb[i]->nhb = NULL;
3592 p_hb[i]->ndist = NULL;
3593 p_hb[i]->n_bound = NULL;
3594 p_hb[i]->time = NULL;
3595 p_hb[i]->nhx = NULL;
3597 p_hb[i]->bHBmap = hb->bHBmap;
3598 p_hb[i]->bDAnr = hb->bDAnr;
3599 p_hb[i]->bGem = hb->bGem;
3600 p_hb[i]->wordlen = hb->wordlen;
3601 p_hb[i]->nframes = hb->nframes;
3602 p_hb[i]->maxhydro = hb->maxhydro;
3603 p_hb[i]->danr = hb->danr;
3604 p_hb[i]->d = hb->d;
3605 p_hb[i]->a = hb->a;
3606 p_hb[i]->hbmap = hb->hbmap;
3607 p_hb[i]->time = hb->time; /* This may need re-syncing at every frame. */
3608 p_hb[i]->per = hb->per;
3610 #ifdef HAVE_NN_LOOPS
3611 p_hb[i]->hbE = hb->hbE;
3612 #endif
3614 p_hb[i]->nrhb = 0;
3615 p_hb[i]->nrdist = 0;
3618 /* Make a thread pool here,
3619 * instead of forking anew at every frame. */
3621 #pragma omp parallel \
3622 private(i, j, h, ii, jj, hh, E, \
3623 xi, yi, zi, xj, yj, zj, threadNr, \
3624 dist, ang, peri, icell, jcell, \
3625 grp, ogrp, ai, aj, xjj, yjj, zjj, \
3626 xk, yk, zk, ihb, id, resdist, \
3627 xkk, ykk, zkk, kcell, ak, k, bTric) \
3628 default(none) \
3629 shared(hb, p_hb, p_adist, p_rdist, actual_nThreads, \
3630 x, bBox, box, hbox, rcut, r2cut, rshell, \
3631 shatom, ngrid, grid, nframes, t, \
3632 bParallel, bNN, index, bMerge, bContact, \
3633 bTwo, bDA,ccut, abin, rbin, top, \
3634 bSelected, bDebug, stderr, nsel, \
3635 bGem, oenv, fnm, fpnhb, trrStatus, natoms, \
3636 status, nabin, nrbin, adist, rdist, debug)
3637 { /* Start of parallel region */
3638 threadNr = omp_get_thread_num();
3639 #endif /* HAVE_OPENMP ================================================= */
3642 bTric = bBox && TRICLINIC(box);
3644 #ifdef HAVE_OPENMP
3645 sync_hbdata(hb, p_hb[threadNr], nframes, t);
3646 #pragma omp single
3647 #endif
3649 build_grid(hb,x,x[shatom], bBox,box,hbox, (rcut>r2cut)?rcut:r2cut,
3650 rshell, ngrid,grid);
3651 reset_nhbonds(&(hb->d));
3653 if (debug && bDebug)
3654 dump_grid(debug, ngrid, grid);
3656 add_frames(hb,nframes);
3657 init_hbframe(hb,nframes,output_env_conv_time(oenv,t));
3659 if (hb->bDAnr)
3660 count_da_grid(ngrid, grid, hb->danr[nframes]);
3661 } /* omp single */
3662 #ifdef HAVE_OPENMP
3663 p_hb[threadNr]->time = hb->time; /* This pointer may have changed. */
3664 #endif
3665 if (bNN)
3667 #ifdef HAVE_NN_LOOPS /* Unlock this feature when testing */
3668 /* Loop over all atom pairs and estimate interaction energy */
3669 #ifdef HAVE_OPENMP /* ------- */
3670 #pragma omp single
3671 #endif /* HAVE_OPENMP ------- */
3673 addFramesNN(hb, nframes);
3675 #ifdef HAVE_OPENMP /* ---------------- */
3676 #pragma omp barrier
3677 #pragma omp for schedule(dynamic)
3678 #endif /* HAVE_OPENMP ---------------- */
3679 for (i=0; i<hb->d.nrd; i++)
3681 for(j=0;j<hb->a.nra; j++)
3683 for (h=0;
3684 h < (bContact ? 1 : hb->d.nhydro[i]);
3685 h++)
3687 if (i==hb->d.nrd || j==hb->a.nra)
3688 gmx_fatal(FARGS, "out of bounds");
3690 /* Get the real atom ids */
3691 ii = hb->d.don[i];
3692 jj = hb->a.acc[j];
3693 hh = hb->d.hydro[i][h];
3695 /* Estimate the energy from the geometry */
3696 E = calcHbEnergy(ii, jj, hh, x, NN, box, hbox, &(hb->d));
3697 /* Store the energy */
3698 storeHbEnergy(hb, i, j, h, E, nframes);
3702 #endif /* HAVE_NN_LOOPS */
3703 } /* if (bNN)*/
3704 else
3706 if (bSelected)
3708 #ifdef HAVE_OPENMP
3709 #pragma omp single
3710 #endif
3712 /* Do not parallelize this just yet. */
3713 /* int ii; */
3714 for(ii=0; (ii<nsel); ii++) {
3715 int dd = index[0][i];
3716 int aa = index[0][i+2];
3717 /* int */ hh = index[0][i+1];
3718 ihb = is_hbond(hb,ii,ii,dd,aa,rcut,r2cut,ccut,x,bBox,box,
3719 hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
3721 if (ihb) {
3722 /* add to index if not already there */
3723 /* Add a hbond */
3724 add_hbond(hb,dd,aa,hh,ii,ii,nframes,bMerge,ihb,bContact,peri);
3727 } /* omp single */
3728 } /* if (bSelected) */
3729 else
3731 #ifdef HAVE_OPENMP
3732 #pragma omp single
3734 #endif
3735 if (bGem)
3736 calcBoxProjection(box, hb->per->P);
3738 /* loop over all gridcells (xi,yi,zi) */
3739 /* Removed confusing macro, DvdS 27/12/98 */
3740 #ifdef HAVE_OPENMP
3742 /* The outer grid loop will have to do for now. */
3743 #pragma omp for schedule(dynamic)
3744 #endif
3745 for(xi=0; xi<ngrid[XX]; xi++)
3746 for(yi=0; (yi<ngrid[YY]); yi++)
3747 for(zi=0; (zi<ngrid[ZZ]); zi++) {
3749 /* loop over donor groups gr0 (always) and gr1 (if necessary) */
3750 for (grp=gr0; (grp <= (bTwo?gr1:gr0)); grp++) {
3751 icell=&(grid[zi][yi][xi].d[grp]);
3753 if (bTwo)
3754 ogrp = 1-grp;
3755 else
3756 ogrp = grp;
3758 /* loop over all hydrogen atoms from group (grp)
3759 * in this gridcell (icell)
3761 for (ai=0; (ai<icell->nr); ai++) {
3762 i = icell->atoms[ai];
3764 /* loop over all adjacent gridcells (xj,yj,zj) */
3765 /* This is a macro!!! */
3766 LOOPGRIDINNER(xj,yj,zj,xjj,yjj,zjj,xi,yi,zi,ngrid,bTric) {
3767 jcell=&(grid[zj][yj][xj].a[ogrp]);
3768 /* loop over acceptor atoms from other group (ogrp)
3769 * in this adjacent gridcell (jcell)
3771 for (aj=0; (aj<jcell->nr); aj++) {
3772 j = jcell->atoms[aj];
3774 /* check if this once was a h-bond */
3775 peri = -1;
3776 ihb = is_hbond(__HBDATA,grp,ogrp,i,j,rcut,r2cut,ccut,x,bBox,box,
3777 hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
3779 if (ihb) {
3780 /* add to index if not already there */
3781 /* Add a hbond */
3782 add_hbond(__HBDATA,i,j,h,grp,ogrp,nframes,bMerge,ihb,bContact,peri);
3784 /* make angle and distance distributions */
3785 if (ihb == hbHB && !bContact) {
3786 if (dist>rcut)
3787 gmx_fatal(FARGS,"distance is higher than what is allowed for an hbond: %f",dist);
3788 ang*=RAD2DEG;
3789 __ADIST[(int)( ang/abin)]++;
3790 __RDIST[(int)(dist/rbin)]++;
3791 if (!bTwo) {
3792 int id,ia;
3793 if ((id = donor_index(&hb->d,grp,i)) == NOTSET)
3794 gmx_fatal(FARGS,"Invalid donor %d",i);
3795 if ((ia = acceptor_index(&hb->a,ogrp,j)) == NOTSET)
3796 gmx_fatal(FARGS,"Invalid acceptor %d",j);
3797 resdist=abs(top.atoms.atom[i].resind-
3798 top.atoms.atom[j].resind);
3799 if (resdist >= max_hx)
3800 resdist = max_hx-1;
3801 __HBDATA->nhx[nframes][resdist]++;
3806 } /* for aj */
3808 ENDLOOPGRIDINNER;
3809 } /* for ai */
3810 } /* for grp */
3811 } /* for xi,yi,zi */
3812 } /* if (bSelected) {...} else */
3814 #ifdef HAVE_OPENMP /* ---------------------------- */
3815 /* Better wait for all threads to finnish using x[] before updating it. */
3816 k = nframes; /* */
3817 #pragma omp barrier /* */
3818 #pragma omp critical /* */
3819 { /* */
3820 /* Sum up histograms and counts from p_hb[] into hb */
3821 { /* */
3822 hb->nhb[k] += p_hb[threadNr]->nhb[k];
3823 hb->ndist[k] += p_hb[threadNr]->ndist[k];
3824 for (j=0; j<max_hx; j++) /**/
3825 hb->nhx[k][j] += p_hb[threadNr]->nhx[k][j];
3826 } /* */
3827 } /* */
3828 /* */
3829 /* Here are a handful of single constructs
3830 * to share the workload a bit. The most
3831 * important one is of course the last one,
3832 * where there's a potential bottleneck in form
3833 * of slow I/O. */
3834 #pragma omp single /* ++++++++++++++++, */
3835 #endif /* HAVE_OPENMP ----------------+------------*/
3836 { /* + */
3837 if (hb != NULL) /* */
3838 { /* + */
3839 analyse_donor_props(opt2fn_null("-don",NFILE,fnm),hb,k,t,oenv);
3840 } /* + */
3841 } /* + */
3842 #ifdef HAVE_OPENMP /* + */
3843 #pragma omp single /* +++ +++ */
3844 #endif /* + */
3845 { /* + */
3846 if (fpnhb) /* + */
3847 do_nhb_dist(fpnhb,hb,t);
3848 } /* + */
3849 } /* if (bNN) {...} else + */
3850 #ifdef HAVE_OPENMP /* + */
3851 #pragma omp single /* +++ +++ */
3852 #endif /* + */
3853 { /* + */
3854 trrStatus = (read_next_x(oenv,status,&t,natoms,x,box));
3855 nframes++; /* + */
3856 } /* + */
3857 #ifdef HAVE_OPENMP /* +++++++++++++++++ */
3858 #pragma omp barrier
3859 #endif
3860 } while (trrStatus);
3862 #ifdef HAVE_OPENMP
3863 #pragma omp critical
3865 hb->nrhb += p_hb[threadNr]->nrhb;
3866 hb->nrdist += p_hb[threadNr]->nrdist;
3868 /* Free parallel datastructures */
3869 sfree(p_hb[threadNr]->nhb);
3870 sfree(p_hb[threadNr]->ndist);
3871 sfree(p_hb[threadNr]->nhx);
3873 #pragma omp for
3874 for (i=0; i<nabin; i++)
3875 for (j=0; j<actual_nThreads; j++)
3877 adist[i] += p_adist[j][i];
3878 #pragma omp for
3879 for (i=0; i<=nrbin; i++)
3880 for (j=0; j<actual_nThreads; j++)
3881 rdist[i] += p_rdist[j][i];
3883 sfree(p_adist[threadNr]);
3884 sfree(p_rdist[threadNr]);
3885 } /* End of parallel region */
3886 sfree(p_adist);
3887 sfree(p_rdist);
3888 #endif
3890 if(nframes <2 && (opt2bSet("-ac",NFILE,fnm) || opt2bSet("-life",NFILE,fnm)))
3892 gmx_fatal(FARGS,"Cannot calculate autocorrelation of life times with less than two frames");
3895 free_grid(ngrid,&grid);
3897 close_trj(status);
3898 if (fpnhb)
3899 ffclose(fpnhb);
3901 /* Compute maximum possible number of different hbonds */
3902 if (maxnhb > 0)
3903 max_nhb = maxnhb;
3904 else {
3905 max_nhb = 0.5*(hb->d.nrd*hb->a.nra);
3907 /* Added support for -contact below.
3908 * - Erik Marklund, May 29-31, 2006 */
3909 /* Changed contact code.
3910 * - Erik Marklund, June 29, 2006 */
3911 if (bHBmap && !bNN) {
3912 if (hb->nrhb==0) {
3913 printf("No %s found!!\n", bContact ? "contacts" : "hydrogen bonds");
3914 } else {
3915 printf("Found %d different %s in trajectory\n"
3916 "Found %d different atom-pairs within %s distance\n",
3917 hb->nrhb, bContact?"contacts":"hydrogen bonds",
3918 hb->nrdist,(r2cut>0)?"second cut-off":"hydrogen bonding");
3920 /*Control the pHist.*/
3922 if (bMerge)
3923 merge_hb(hb,bTwo,bContact);
3925 if (opt2bSet("-hbn",NFILE,fnm))
3926 dump_hbmap(hb,NFILE,fnm,bTwo,bContact,isize,index,grpnames,&top.atoms);
3928 /* Moved the call to merge_hb() to a line BEFORE dump_hbmap
3929 * to make the -hbn and -hmb output match eachother.
3930 * - Erik Marklund, May 30, 2006 */
3933 /* Print out number of hbonds and distances */
3934 aver_nhb = 0;
3935 aver_dist = 0;
3936 fp = xvgropen(opt2fn("-num",NFILE,fnm),bContact ? "Contacts" :
3937 "Hydrogen Bonds",output_env_get_xvgr_tlabel(oenv),"Number",oenv);
3938 snew(leg,2);
3939 snew(leg[0],STRLEN);
3940 snew(leg[1],STRLEN);
3941 sprintf(leg[0],"%s",bContact?"Contacts":"Hydrogen bonds");
3942 sprintf(leg[1],"Pairs within %g nm",(r2cut>0)?r2cut:rcut);
3943 xvgr_legend(fp,2,(const char**)leg,oenv);
3944 sfree(leg[1]);
3945 sfree(leg[0]);
3946 sfree(leg);
3947 for(i=0; (i<nframes); i++) {
3948 fprintf(fp,"%10g %10d %10d\n",hb->time[i],hb->nhb[i],hb->ndist[i]);
3949 aver_nhb += hb->nhb[i];
3950 aver_dist += hb->ndist[i];
3952 ffclose(fp);
3953 aver_nhb /= nframes;
3954 aver_dist /= nframes;
3955 /* Print HB distance distribution */
3956 if (opt2bSet("-dist",NFILE,fnm)) {
3957 long sum;
3959 sum=0;
3960 for(i=0; i<nrbin; i++)
3961 sum+=rdist[i];
3963 fp = xvgropen(opt2fn("-dist",NFILE,fnm),
3964 "Hydrogen Bond Distribution",
3965 "Hydrogen - Acceptor Distance (nm)","",oenv);
3966 for(i=0; i<nrbin; i++)
3967 fprintf(fp,"%10g %10g\n",(i+0.5)*rbin,rdist[i]/(rbin*(real)sum));
3968 ffclose(fp);
3971 /* Print HB angle distribution */
3972 if (opt2bSet("-ang",NFILE,fnm)) {
3973 long sum;
3975 sum=0;
3976 for(i=0; i<nabin; i++)
3977 sum+=adist[i];
3979 fp = xvgropen(opt2fn("-ang",NFILE,fnm),
3980 "Hydrogen Bond Distribution",
3981 "Donor - Hydrogen - Acceptor Angle (\\SO\\N)","",oenv);
3982 for(i=0; i<nabin; i++)
3983 fprintf(fp,"%10g %10g\n",(i+0.5)*abin,adist[i]/(abin*(real)sum));
3984 ffclose(fp);
3987 /* Print HB in alpha-helix */
3988 if (opt2bSet("-hx",NFILE,fnm)) {
3989 fp = xvgropen(opt2fn("-hx",NFILE,fnm),
3990 "Hydrogen Bonds",output_env_get_xvgr_tlabel(oenv),"Count",oenv);
3991 xvgr_legend(fp,NRHXTYPES,hxtypenames,oenv);
3992 for(i=0; i<nframes; i++) {
3993 fprintf(fp,"%10g",hb->time[i]);
3994 for (j=0; j<max_hx; j++)
3995 fprintf(fp," %6d",hb->nhx[i][j]);
3996 fprintf(fp,"\n");
3998 ffclose(fp);
4000 if (!bNN)
4001 printf("Average number of %s per timeframe %.3f out of %g possible\n",
4002 bContact ? "contacts" : "hbonds",
4003 bContact ? aver_dist : aver_nhb, max_nhb);
4005 /* Do Autocorrelation etc. */
4006 if (hb->bHBmap) {
4008 Added support for -contact in ac and hbm calculations below.
4009 - Erik Marklund, May 29, 2006
4011 ivec itmp;
4012 rvec rtmp;
4013 if (opt2bSet("-ac",NFILE,fnm) || opt2bSet("-life",NFILE,fnm))
4014 please_cite(stdout,"Spoel2006b");
4015 if (opt2bSet("-ac",NFILE,fnm)) {
4016 char *gemstring=NULL;
4018 if (bGem || bNN) {
4019 params = init_gemParams(rcut, D, hb->time, hb->nframes/2, nFitPoints, fit_start, fit_end,
4020 gemBallistic, nBalExp, bBallisticDt);
4021 if (params == NULL)
4022 gmx_fatal(FARGS, "Could not initiate t_gemParams params.");
4024 gemstring = strdup(gemType[hb->per->gemtype]);
4025 do_hbac(opt2fn("-ac",NFILE,fnm),hb,nDump,
4026 bMerge,bContact,fit_start,temp,r2cut>0,smooth_tail_start,oenv,
4027 params, gemstring, nThreads, NN, bBallistic, bGemFit);
4029 if (opt2bSet("-life",NFILE,fnm))
4030 do_hblife(opt2fn("-life",NFILE,fnm),hb,bMerge,bContact,oenv);
4031 if (opt2bSet("-hbm",NFILE,fnm)) {
4032 t_matrix mat;
4033 int id,ia,hh,x,y;
4035 mat.nx=nframes;
4036 mat.ny=(bContact ? hb->nrdist : hb->nrhb);
4038 snew(mat.matrix,mat.nx);
4039 for(x=0; (x<mat.nx); x++)
4040 snew(mat.matrix[x],mat.ny);
4041 y=0;
4042 for(id=0; (id<hb->d.nrd); id++)
4043 for(ia=0; (ia<hb->a.nra); ia++) {
4044 for(hh=0; (hh<hb->maxhydro); hh++) {
4045 if (hb->hbmap[id][ia]) {
4046 if (ISHB(hb->hbmap[id][ia]->history[hh])) {
4047 /* Changed '<' into '<=' in the for-statement below.
4048 * It fixed the previously undiscovered bug that caused
4049 * the last occurance of an hbond/contact to not be
4050 * set in mat.matrix. Have a look at any old -hbm-output
4051 * and you will notice that the last column is allways empty.
4052 * - Erik Marklund May 30, 2006
4054 for(x=0; (x<=hb->hbmap[id][ia]->nframes); x++) {
4055 int nn0 = hb->hbmap[id][ia]->n0;
4056 range_check(y,0,mat.ny);
4057 mat.matrix[x+nn0][y] = is_hb(hb->hbmap[id][ia]->h[hh],x);
4059 y++;
4064 mat.axis_x=hb->time;
4065 snew(mat.axis_y,mat.ny);
4066 for(j=0; j<mat.ny; j++)
4067 mat.axis_y[j]=j;
4068 sprintf(mat.title,bContact ? "Contact Existence Map":
4069 "Hydrogen Bond Existence Map");
4070 sprintf(mat.legend,bContact ? "Contacts" : "Hydrogen Bonds");
4071 sprintf(mat.label_x,"%s",output_env_get_xvgr_tlabel(oenv));
4072 sprintf(mat.label_y, bContact ? "Contact Index" : "Hydrogen Bond Index");
4073 mat.bDiscrete=TRUE;
4074 mat.nmap=2;
4075 snew(mat.map,mat.nmap);
4076 for(i=0; i<mat.nmap; i++) {
4077 mat.map[i].code.c1=hbmap[i];
4078 mat.map[i].desc=hbdesc[i];
4079 mat.map[i].rgb=hbrgb[i];
4081 fp = opt2FILE("-hbm",NFILE,fnm,"w");
4082 write_xpm_m(fp, mat);
4083 ffclose(fp);
4084 for(x=0; x<mat.nx; x++)
4085 sfree(mat.matrix[x]);
4086 sfree(mat.axis_y);
4087 sfree(mat.matrix);
4088 sfree(mat.map);
4092 if (bGem) {
4093 fprintf(stderr, "There were %i periodic shifts\n", hb->per->nper);
4094 fprintf(stderr, "Freeing pHist for all donors...\n");
4095 for (i=0; i<hb->d.nrd; i++) {
4096 fprintf(stderr, "\r%i",i);
4097 if (hb->per->pHist[i] != NULL) {
4098 for (j=0; j<hb->a.nra; j++)
4099 clearPshift(&(hb->per->pHist[i][j]));
4100 sfree(hb->per->pHist[i]);
4103 sfree(hb->per->pHist);
4104 sfree(hb->per->p2i);
4105 sfree(hb->per);
4106 fprintf(stderr, "...done.\n");
4109 #ifdef HAVE_NN_LOOPS
4110 if (bNN)
4111 free_hbEmap(hb);
4112 #endif
4114 if (hb->bDAnr) {
4115 int i,j,nleg;
4116 char **legnames;
4117 char buf[STRLEN];
4119 #define USE_THIS_GROUP(j) ( (j == gr0) || (bTwo && (j == gr1)) )
4121 fp = xvgropen(opt2fn("-dan",NFILE,fnm),
4122 "Donors and Acceptors",output_env_get_xvgr_tlabel(oenv),"Count",oenv);
4123 nleg = (bTwo?2:1)*2;
4124 snew(legnames,nleg);
4125 i=0;
4126 for(j=0; j<grNR; j++)
4127 if ( USE_THIS_GROUP(j) ) {
4128 sprintf(buf,"Donors %s",grpnames[j]);
4129 legnames[i++] = strdup(buf);
4130 sprintf(buf,"Acceptors %s",grpnames[j]);
4131 legnames[i++] = strdup(buf);
4133 if (i != nleg)
4134 gmx_incons("number of legend entries");
4135 xvgr_legend(fp,nleg,(const char**)legnames,oenv);
4136 for(i=0; i<nframes; i++) {
4137 fprintf(fp,"%10g",hb->time[i]);
4138 for (j=0; (j<grNR); j++)
4139 if ( USE_THIS_GROUP(j) )
4140 fprintf(fp," %6d",hb->danr[i][j]);
4141 fprintf(fp,"\n");
4143 ffclose(fp);
4146 thanx(stdout);
4148 return 0;