Parallel vs. sequentiual code: I get binary identical result (apart from comment...
[gromacs/qmmm-gamess-us.git] / src / tools / gmx_hbond.c
blobea892a6f6b3e504a9fd32e9409e2c0de8d1ff24c
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 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 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 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 dumpN(real *e, int nn)
223 /* For debugging only */
225 #define N_FILENAME "Nt.dat"
226 char *fn = N_FILENAME;
227 int i;
228 FILE *f;
229 f = fopen(fn, "w");
230 fprintf(f,
231 "@ type XY\n"
232 "@ xaxis label \"Frame\"\n"
233 "@ yaxis label \"N\"\n"
234 "@ s0 line type 3\n");
235 for (i=0; i<nn; i++)
236 fprintf(f, "%-10i %-g\n", i, e[i]);
237 fclose(f);
240 static void clearPshift(t_pShift *pShift)
242 if (pShift->len > 0) {
243 sfree(pShift->p);
244 sfree(pShift->frame);
245 pShift->len = 0;
249 static void calcBoxProjection(matrix B, matrix P)
251 const int vp[] = {XX,YY,ZZ};
252 int i,j;
253 int m,n;
254 matrix M, N, U;
256 for (i=0; i<3; i++){ m = vp[i];
257 for (j=0; j<3; j++){ n = vp[j];
258 U[m][n] = i==j ? 1:0;
261 m_inv(B,M);
262 for (i=0; i<3; i++){ m = vp[i];
263 mvmul(M, U[m], P[m]);
265 transpose(P,N);
268 static void calcBoxDistance(matrix P, rvec d, ivec ibd){
269 /* returns integer distance in box coordinates.
270 * P is the projection matrix from cartesian coordinates
271 * obtained with calcBoxProjection(). */
272 int i;
273 rvec bd;
274 mvmul(P, d, bd);
275 /* extend it by 0.5 in all directions since (int) rounds toward 0.*/
276 for (i=0;i<3;i++)
277 bd[i]=bd[i] + (bd[i]<0 ? -0.5 : 0.5);
278 ibd[XX] = (int)bd[XX];
279 ibd[YY] = (int)bd[YY];
280 ibd[ZZ] = (int)bd[ZZ];
283 /* Changed argument 'bMerge' into 'oneHB' below,
284 * since -contact should cause maxhydro to be 1,
285 * not just -merge.
286 * - Erik Marklund May 29, 2006
289 static PSTYPE periodicIndex(ivec r, t_gemPeriod *per, bool daSwap) {
290 /* Try to merge hbonds on the fly. That means that if the
291 * acceptor and donor are mergable, then:
292 * 1) store the hb-info so that acceptor id > donor id,
293 * 2) add the periodic shift in pairs, so that [-x,-y,-z] is
294 * stored in per.p2i[] whenever acceptor id < donor id.
295 * Note that [0,0,0] should already be the first element of per.p2i
296 * by the time this function is called. */
298 /* daSwap is TRUE if the donor and acceptor were swapped.
299 * If so, then the negative vector should be used. */
300 PSTYPE i;
302 if (per->p2i == NULL || per->nper == 0)
303 gmx_fatal(FARGS, "'per' not initialized properly.");
304 for (i=0; i<per->nper; i++) {
305 if (r[XX] == per->p2i[i][XX] &&
306 r[YY] == per->p2i[i][YY] &&
307 r[ZZ] == per->p2i[i][ZZ])
308 return i;
310 /* Not found apparently. Add it to the list! */
311 /* printf("New shift found: %i,%i,%i\n",r[XX],r[YY],r[ZZ]); */
313 /* Unfortunately this needs to be critical it seems. */
314 #ifdef HAVE_OPENMP
315 #pragma omp critical
316 #endif
318 if (!per->p2i) {
319 fprintf(stderr, "p2i not initialized. This shouldn't happen!\n");
320 snew(per->p2i, 1);
322 else
323 srenew(per->p2i, per->nper+2);
324 copy_ivec(r, per->p2i[per->nper]);
325 (per->nper)++;
327 /* Add the mirror too. It's rather likely that it'll be needed. */
328 per->p2i[per->nper][XX] = -r[XX];
329 per->p2i[per->nper][YY] = -r[YY];
330 per->p2i[per->nper][ZZ] = -r[ZZ];
331 (per->nper)++;
332 } /* omp critical */
333 return per->nper - 1 - (daSwap ? 0:1);
336 static t_hbdata *mk_hbdata(bool bHBmap,bool bDAnr,bool oneHB, bool bGem, int gemmode)
338 t_hbdata *hb;
340 snew(hb,1);
341 hb->wordlen = 8*sizeof(unsigned int);
342 hb->bHBmap = bHBmap;
343 hb->bDAnr = bDAnr;
344 hb->bGem = bGem;
345 if (oneHB)
346 hb->maxhydro = 1;
347 else
348 hb->maxhydro = MAXHYDRO;
349 snew(hb->per, 1);
350 hb->per->gemtype = bGem ? gemmode : 0;
352 return hb;
355 static void mk_hbmap(t_hbdata *hb,bool bTwo,bool bInsert)
357 int i,j;
359 snew(hb->hbmap,hb->d.nrd);
360 for(i=0; (i<hb->d.nrd); i++) {
361 snew(hb->hbmap[i],hb->a.nra);
362 if (hb->hbmap[i] == NULL)
363 gmx_fatal(FARGS,"Could not allocate enough memory for hbmap");
364 for (j=0; (j>hb->a.nra); j++)
365 hb->hbmap[i][j] == NULL;
369 /* Consider redoing pHist so that is only stores transitions between
370 * periodicities and not the periodicity for all frames. This eats heaps of memory. */
371 static void mk_per(t_hbdata *hb)
373 int i,j;
374 if (hb->bGem) {
375 snew(hb->per->pHist, hb->d.nrd);
376 for (i=0; i<hb->d.nrd; i++) {
377 snew(hb->per->pHist[i], hb->a.nra);
378 if (hb->per->pHist[i]==NULL)
379 gmx_fatal(FARGS,"Could not allocate enough memory for per->pHist");
380 for (j=0; j<hb->a.nra; j++) {
381 clearPshift(&(hb->per->pHist[i][j]));
384 /* add the [0,0,0] shift to element 0 of p2i. */
385 snew(hb->per->p2i, 1);
386 clear_ivec(hb->per->p2i[0]);
387 hb->per->nper = 1;
391 #ifdef HAVE_NN_LOOPS
392 static void mk_hbEmap (t_hbdata *hb, int n0)
394 int i, j, k;
395 hb->hbE.E = NULL;
396 hb->hbE.nframes = 0;
397 snew(hb->hbE.E, hb->d.nrd);
398 for (i=0; i<hb->d.nrd; i++)
400 snew(hb->hbE.E[i], hb->a.nra);
401 for (j=0; j<hb->a.nra; j++)
403 snew(hb->hbE.E[i][j], MAXHYDRO);
404 for (k=0; k<MAXHYDRO; k++)
405 hb->hbE.E[i][j][k] = NULL;
408 hb->hbE.Etot = NULL;
411 static void free_hbEmap (t_hbdata *hb)
413 int i, j, k;
414 for (i=0; i<hb->d.nrd; i++)
416 for (j=0; j<hb->a.nra; j++)
418 for (k=0; k<MAXHYDRO; k++)
419 sfree(hb->hbE.E[i][j][k]);
420 sfree(hb->hbE.E[i][j]);
422 sfree(hb->hbE.E[i]);
424 sfree(hb->hbE.E);
425 sfree(hb->hbE.Etot);
428 static void addFramesNN(t_hbdata *hb, int frame)
431 #define DELTAFRAMES_HBE 10
433 int d,a,h,nframes;
435 if (frame >= hb->hbE.nframes) {
436 nframes = hb->hbE.nframes + DELTAFRAMES_HBE;
437 srenew(hb->hbE.Etot, nframes);
439 for (d=0; d<hb->d.nrd; d++)
440 for (a=0; a<hb->a.nra; a++)
441 for (h=0; h<hb->d.nhydro[d]; h++)
442 srenew(hb->hbE.E[d][a][h], nframes);
444 hb->hbE.nframes += DELTAFRAMES_HBE;
448 static t_E calcHbEnergy(int d, int a, int h, rvec x[], t_EEst EEst,
449 matrix box, rvec hbox, t_donors *donors){
450 /* d - donor atom
451 * a - acceptor atom
452 * h - hydrogen
453 * alpha - angle between dipoles
454 * x[] - atomic positions
455 * EEst - the type of energy estimate (see enum in hbplugin.h)
456 * box - the box vectors \
457 * hbox - half box lengths _These two are only needed for the pbc correction
460 t_E E;
461 rvec dist;
462 rvec dipole[2], xmol[3], xmean[2];
463 int i;
464 real r, realE;
466 if (d == a)
467 /* Self-interaction */
468 return NONSENSE_E;
470 switch (EEst)
472 case NN_BINARY:
473 /* This is a simple binary existence function that sets E=1 whenever
474 * the distance between the oxygens is equal too or less than 0.35 nm.
476 rvec_sub(x[d], x[a], dist);
477 pbc_correct_gem(dist, box, hbox);
478 if (norm(dist) <= 0.35)
479 E = 1;
480 else
481 E = 0;
482 break;
484 case NN_1_over_r3:
485 /* Negative potential energy of a dipole.
486 * E = -cos(alpha) * 1/r^3 */
488 copy_rvec(x[d], xmol[0]); /* donor */
489 copy_rvec(x[donors->hydro[donors->dptr[d]][0]], xmol[1]); /* hydrogen */
490 copy_rvec(x[donors->hydro[donors->dptr[d]][1]], xmol[2]); /* hydrogen */
492 svmul(15.9994*(1/1.008), xmol[0], xmean[0]);
493 rvec_inc(xmean[0], xmol[1]);
494 rvec_inc(xmean[0], xmol[2]);
495 for(i=0; i<3; i++)
496 xmean[0][i] /= (15.9994 + 1.008 + 1.008)/1.008;
498 /* Assumes that all acceptors are also donors. */
499 copy_rvec(x[a], xmol[0]); /* acceptor */
500 copy_rvec(x[donors->hydro[donors->dptr[a]][0]], xmol[1]); /* hydrogen */
501 copy_rvec(x[donors->hydro[donors->dptr[a]][1]], xmol[2]); /* hydrogen */
504 svmul(15.9994*(1/1.008), xmol[0], xmean[1]);
505 rvec_inc(xmean[1], xmol[1]);
506 rvec_inc(xmean[1], xmol[2]);
507 for(i=0; i<3; i++)
508 xmean[1][i] /= (15.9994 + 1.008 + 1.008)/1.008;
510 rvec_sub(xmean[0], xmean[1], dist);
511 pbc_correct_gem(dist, box, hbox);
512 r = norm(dist);
514 realE = pow(r, -3.0);
515 E = (t_E)(SCALEFACTOR_E * realE);
516 break;
518 case NN_dipole:
519 /* Negative potential energy of a (unpolarizable) dipole.
520 * E = -cos(alpha) * 1/r^3 */
521 clear_rvec(dipole[1]);
522 clear_rvec(dipole[0]);
524 copy_rvec(x[d], xmol[0]); /* donor */
525 copy_rvec(x[donors->hydro[donors->dptr[d]][0]], xmol[1]); /* hydrogen */
526 copy_rvec(x[donors->hydro[donors->dptr[d]][1]], xmol[2]); /* hydrogen */
528 rvec_inc(dipole[0], xmol[1]);
529 rvec_inc(dipole[0], xmol[2]);
530 for (i=0; i<3; i++)
531 dipole[0][i] *= 0.5;
532 rvec_dec(dipole[0], xmol[0]);
534 svmul(15.9994*(1/1.008), xmol[0], xmean[0]);
535 rvec_inc(xmean[0], xmol[1]);
536 rvec_inc(xmean[0], xmol[2]);
537 for(i=0; i<3; i++)
538 xmean[0][i] /= (15.9994 + 1.008 + 1.008)/1.008;
540 /* Assumes that all acceptors are also donors. */
541 copy_rvec(x[a], xmol[0]); /* acceptor */
542 copy_rvec(x[donors->hydro[donors->dptr[a]][0]], xmol[1]); /* hydrogen */
543 copy_rvec(x[donors->hydro[donors->dptr[a]][2]], xmol[2]); /* hydrogen */
546 rvec_inc(dipole[1], xmol[1]);
547 rvec_inc(dipole[1], xmol[2]);
548 for (i=0; i<3; i++)
549 dipole[1][i] *= 0.5;
550 rvec_dec(dipole[1], xmol[0]);
552 svmul(15.9994*(1/1.008), xmol[0], xmean[1]);
553 rvec_inc(xmean[1], xmol[1]);
554 rvec_inc(xmean[1], xmol[2]);
555 for(i=0; i<3; i++)
556 xmean[1][i] /= (15.9994 + 1.008 + 1.008)/1.008;
558 rvec_sub(xmean[0], xmean[1], dist);
559 pbc_correct_gem(dist, box, hbox);
560 r = norm(dist);
562 double cosalpha = cos_angle(dipole[0],dipole[1]);
563 realE = cosalpha * pow(r, -3.0);
564 E = (t_E)(SCALEFACTOR_E * realE);
565 break;
567 default:
568 printf("Can't do that type of energy estimate: %i\n.", EEst);
569 E = NONSENSE_E;
572 return E;
575 static void storeHbEnergy(t_hbdata *hb, int d, int a, int h, t_E E, int frame){
576 /* hb - hbond data structure
577 d - donor
578 a - acceptor
579 h - hydrogen
580 E - estimate of the energy
581 frame - the current frame.
584 /* Store the estimated energy */
585 if (E == NONSENSE_E)
586 E = 0;
588 hb->hbE.E[d][a][h][frame] = E;
589 #ifdef HAVE_OPENMP
590 #pragma omp critical
591 #endif
593 hb->hbE.Etot[frame] += E;
596 #endif /* HAVE_NN_LOOPS */
599 /* Finds -v[] in the periodicity index */
600 static int findMirror(PSTYPE p, ivec v[], PSTYPE nper)
602 PSTYPE i;
603 ivec u;
604 for (i=0; i<nper; i++){
605 if (v[i][XX] == -(v[p][XX]) &&
606 v[i][YY] == -(v[p][YY]) &&
607 v[i][ZZ] == -(v[p][ZZ]))
608 return (int)i;
610 printf("Couldn't find mirror of [%i, %i, %i], index \n",
611 v[p][XX],
612 v[p][YY],
613 v[p][ZZ]);
614 return -1;
618 static void add_frames(t_hbdata *hb,int nframes)
620 int i,j,k,l;
622 if (nframes >= hb->max_frames) {
623 hb->max_frames += 4096;
624 srenew(hb->time,hb->max_frames);
625 srenew(hb->nhb,hb->max_frames);
626 srenew(hb->ndist,hb->max_frames);
627 srenew(hb->n_bound,hb->max_frames);
628 srenew(hb->nhx,hb->max_frames);
629 if (hb->bDAnr)
630 srenew(hb->danr,hb->max_frames);
632 hb->nframes=nframes;
635 #define OFFSET(frame) (frame / 32)
636 #define MASK(frame) (1 << (frame % 32))
638 static void _set_hb(unsigned int hbexist[],unsigned int frame,bool bValue)
640 if (bValue)
641 hbexist[OFFSET(frame)] |= MASK(frame);
642 else
643 hbexist[OFFSET(frame)] &= ~MASK(frame);
646 static bool is_hb(unsigned int hbexist[],int frame)
648 return ((hbexist[OFFSET(frame)] & MASK(frame)) != 0) ? 1 : 0;
651 static void set_hb(t_hbdata *hb,int id,int ih, int ia,int frame,int ihb)
653 unsigned int *ghptr=NULL;
655 if (ihb == hbHB)
656 ghptr = hb->hbmap[id][ia]->h[ih];
657 else if (ihb == hbDist)
658 ghptr = hb->hbmap[id][ia]->g[ih];
659 else
660 gmx_fatal(FARGS,"Incomprehensible iValue %d in set_hb",ihb);
662 _set_hb(ghptr,frame-hb->hbmap[id][ia]->n0,TRUE);
665 static void addPshift(t_pShift *pHist, PSTYPE p, int frame)
667 if (pHist->len == 0) {
668 snew(pHist->frame, 1);
669 snew(pHist->p, 1);
670 pHist->len = 1;
671 pHist->frame[0] = frame;
672 pHist->p[0] = p;
673 return;
674 } else
675 if (pHist->p[pHist->len-1] != p) {
676 pHist->len++;
677 srenew(pHist->frame, pHist->len);
678 srenew(pHist->p, pHist->len);
679 pHist->frame[pHist->len-1] = frame;
680 pHist->p[pHist->len-1] = p;
681 } /* Otherwise, there is no transition. */
682 return;
685 static PSTYPE getPshift(t_pShift pHist, int frame)
687 int f, i;
689 if (pHist.len == 0
690 || (pHist.len > 0 && pHist.frame[0]>frame))
691 return -1;
693 for (i=0; i<pHist.len; i++)
695 f = pHist.frame[i];
696 if (f==frame)
697 return pHist.p[i];
698 if (f>frame)
699 return pHist.p[i-1];
702 /* It seems that frame is after the last periodic transition. Return the last periodicity. */
703 return pHist.p[pHist.len-1];
706 static void add_ff(t_hbdata *hbd,int id,int h,int ia,int frame,int ihb, PSTYPE p)
708 int i,j,n;
709 t_hbond *hb = hbd->hbmap[id][ia];
710 int maxhydro = min(hbd->maxhydro,hbd->d.nhydro[id]);
711 int wlen = hbd->wordlen;
712 int delta = 32*wlen;
713 bool bGem = hbd->bGem;
715 if (!hb->h[0]) {
716 hb->n0 = frame;
717 hb->maxframes = delta;
718 for(i=0; (i<maxhydro); i++) {
719 snew(hb->h[i],hb->maxframes/wlen);
720 snew(hb->g[i],hb->maxframes/wlen);
722 } else {
723 hb->nframes = frame-hb->n0;
724 /* We need a while loop here because hbonds may be returning
725 * after a long time.
727 while (hb->nframes >= hb->maxframes) {
728 n = hb->maxframes + delta;
729 for(i=0; (i<maxhydro); i++) {
730 srenew(hb->h[i],n/wlen);
731 srenew(hb->g[i],n/wlen);
732 for(j=hb->maxframes/wlen; (j<n/wlen); j++) {
733 hb->h[i][j] = 0;
734 hb->g[i][j] = 0;
738 hb->maxframes = n;
741 if (frame >= 0) {
742 set_hb(hbd,id,h,ia,frame,ihb);
743 if (bGem) {
744 if (p>=hbd->per->nper)
745 gmx_fatal(FARGS, "invalid shift: p=%u, nper=%u", p, hbd->per->nper);
746 else
747 addPshift(&(hbd->per->pHist[id][ia]), p, frame);
753 static void inc_nhbonds(t_donors *ddd,int d, int h)
755 int j;
756 int dptr = ddd->dptr[d];
758 for(j=0; (j<ddd->nhydro[dptr]); j++)
759 if (ddd->hydro[dptr][j] == h) {
760 ddd->nhbonds[dptr][j]++;
761 break;
763 if (j == ddd->nhydro[dptr])
764 gmx_fatal(FARGS,"No such hydrogen %d on donor %d\n",h+1,d+1);
767 static int _acceptor_index(t_acceptors *a,int grp,atom_id i,
768 const char *file,int line)
770 int ai = a->aptr[i];
772 if (a->grp[ai] != grp) {
773 if (debug && bDebug)
774 fprintf(debug,"Acc. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
775 ai,a->grp[ai],grp,file,line);
776 return NOTSET;
778 else
779 return ai;
781 #define acceptor_index(a,grp,i) _acceptor_index(a,grp,i,__FILE__,__LINE__)
783 static int _donor_index(t_donors *d,int grp,atom_id i,const char *file,int line)
785 int di = d->dptr[i];
787 if (d->grp[di] != grp) {
788 if (debug && bDebug)
789 fprintf(debug,"Don. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
790 di,d->grp[di],grp,file,line);
791 return NOTSET;
793 else
794 return di;
796 #define donor_index(d,grp,i) _donor_index(d,grp,i,__FILE__,__LINE__)
798 static bool isInterchangable(t_hbdata *hb, int d, int a, int grpa, int grpd)
800 return
801 donor_index(&hb->d,grpd,a) != NOTSET
802 && acceptor_index(&hb->a,grpa,d) != NOTSET;
806 static void add_hbond(t_hbdata *hb,int d,int a,int h,int grpd,int grpa,
807 int frame,bool bInsert,bool bMerge,int ihb,bool bContact, PSTYPE p)
809 int k,id,ia,hh;
810 bool daSwap = FALSE;
812 if ((id = hb->d.dptr[d]) == NOTSET)
813 gmx_fatal(FARGS,"No donor atom %d",d+1);
814 else if (grpd != hb->d.grp[id])
815 gmx_fatal(FARGS,"Inconsistent donor groups, %d iso %d, atom %d",
816 grpd,hb->d.grp[id],d+1);
817 if ((ia = hb->a.aptr[a]) == NOTSET)
818 gmx_fatal(FARGS,"No acceptor atom %d",a+1);
819 else if (grpa != hb->a.grp[ia])
820 gmx_fatal(FARGS,"Inconsistent acceptor groups, %d iso %d, atom %d",
821 grpa,hb->a.grp[ia],a+1);
823 if (bMerge)
824 if ((daSwap = isInterchangable(hb, d, a, grpd, grpa) || bContact) && d>a)
825 /* Then swap identity so that the id of d is lower then that of a.
827 * This should really be redundant by now, as is_hbond() now ought to return
828 * hbNo in the cases where this conditional is TRUE. */
830 k = d;
831 d = a;
832 a = k;
834 /* Now repeat donor/acc check. */
835 if ((id = hb->d.dptr[d]) == NOTSET)
836 gmx_fatal(FARGS,"No donor atom %d",d+1);
837 else if (grpd != hb->d.grp[id])
838 gmx_fatal(FARGS,"Inconsistent donor groups, %d iso %d, atom %d",
839 grpd,hb->d.grp[id],d+1);
840 if ((ia = hb->a.aptr[a]) == NOTSET)
841 gmx_fatal(FARGS,"No acceptor atom %d",a+1);
842 else if (grpa != hb->a.grp[ia])
843 gmx_fatal(FARGS,"Inconsistent acceptor groups, %d iso %d, atom %d",
844 grpa,hb->a.grp[ia],a+1);
847 if (hb->hbmap) {
848 /* Loop over hydrogens to find which hydrogen is in this particular HB */
849 if ((ihb == hbHB) && !bMerge && !bContact) {
850 for(k=0; (k<hb->d.nhydro[id]); k++)
851 if (hb->d.hydro[id][k] == h)
852 break;
853 if (k == hb->d.nhydro[id])
854 gmx_fatal(FARGS,"Donor %d does not have hydrogen %d (a = %d)",
855 d+1,h+1,a+1);
857 else
858 k = 0;
860 if (hb->bHBmap) {
861 if (hb->hbmap[id][ia] == NULL) {
862 snew(hb->hbmap[id][ia],1);
863 snew(hb->hbmap[id][ia]->h,hb->maxhydro);
864 snew(hb->hbmap[id][ia]->g,hb->maxhydro);
866 add_ff(hb,id,k,ia,frame,ihb,p);
869 /* Strange construction with frame >=0 is a relic from old code
870 * for selected hbond analysis. It may be necessary again if that
871 * is made to work again.
873 if (frame >= 0) {
874 hh = hb->hbmap[id][ia]->history[k];
875 if (ihb == hbHB) {
876 hb->nhb[frame]++;
877 if (!(ISHB(hh))) {
878 hb->hbmap[id][ia]->history[k] = hh | 2;
879 hb->nrhb++;
882 else
884 if (ihb == hbDist) {
885 hb->ndist[frame]++;
886 if (!(ISDIST(hh))) {
887 hb->hbmap[id][ia]->history[k] = hh | 1;
888 hb->nrdist++;
893 } else {
894 if (frame >= 0) {
895 if (ihb == hbHB) {
896 hb->nhb[frame]++;
897 } else {
898 if (ihb == hbDist) {
899 hb->ndist[frame]++;
904 if (bMerge && daSwap)
905 h = hb->d.hydro[id][0];
906 /* Increment number if HBonds per H */
907 if (ihb == hbHB && !bContact)
908 inc_nhbonds(&(hb->d),d,h);
911 /* Now a redundant function. It might find use at some point though. */
912 static bool in_list(atom_id selection,int isize,atom_id *index)
914 int i;
915 bool bFound;
917 bFound=FALSE;
918 for(i=0; (i<isize) && !bFound; i++)
919 if(selection == index[i])
920 bFound=TRUE;
922 return bFound;
925 static char *mkatomname(t_atoms *atoms,int i)
927 static char buf[32];
928 int rnr;
930 rnr = atoms->atom[i].resind;
931 sprintf(buf,"%4s%d%-4s",
932 *atoms->resinfo[rnr].name,atoms->resinfo[rnr].nr,*atoms->atomname[i]);
934 return buf;
937 static void gen_datable(atom_id *index, int isize, unsigned char *datable, int natoms){
938 /* Generates table of all atoms and sets the ingroup bit for atoms in index[] */
939 int i;
941 for (i=0; i<isize; i++){
942 if (index[i] >= natoms)
943 gmx_fatal(FARGS,"Atom has index %d larger than number of atoms %d.",index[i],natoms);
944 datable[index[i]] |= INGROUP;
948 static void clear_datable_grp(unsigned char *datable, int size){
949 /* Clears group information from the table */
950 int i;
951 const char mask = !(char)INGROUP;
952 if (size > 0)
953 for (i=0;i<size;i++)
954 datable[i] &= mask;
957 static void add_acc(t_acceptors *a,int ia,int grp)
959 if (a->nra >= a->max_nra) {
960 a->max_nra += 16;
961 srenew(a->acc,a->max_nra);
962 srenew(a->grp,a->max_nra);
964 a->grp[a->nra] = grp;
965 a->acc[a->nra++] = ia;
968 static void search_acceptors(t_topology *top,int isize,
969 atom_id *index,t_acceptors *a,int grp,
970 bool bNitAcc,
971 bool bContact,bool bDoIt, unsigned char *datable)
973 int i,n;
975 if (bDoIt) {
976 for (i=0; (i<isize); i++) {
977 n = index[i];
978 if ((bContact ||
979 (((*top->atoms.atomname[n])[0] == 'O') ||
980 (bNitAcc && ((*top->atoms.atomname[n])[0] == 'N')))) &&
981 ISINGRP(datable[n])) {
982 datable[n] |= ACC; /* set the atom's acceptor flag in datable. */
983 add_acc(a,n,grp);
987 snew(a->aptr,top->atoms.nr);
988 for(i=0; (i<top->atoms.nr); i++)
989 a->aptr[i] = NOTSET;
990 for(i=0; (i<a->nra); i++)
991 a->aptr[a->acc[i]] = i;
994 static void add_h2d(int id,int ih,t_donors *ddd)
996 int i;
998 for(i=0; (i<ddd->nhydro[id]); i++)
999 if (ddd->hydro[id][i] == ih) {
1000 printf("Hm. This isn't first time I find this donor (%d,%d)\n",
1001 ddd->don[id],ih);
1002 break;
1004 if (i == ddd->nhydro[id]) {
1005 if (ddd->nhydro[id] >= MAXHYDRO)
1006 gmx_fatal(FARGS,"Donor %d has more than %d hydrogens!",
1007 ddd->don[id],MAXHYDRO);
1008 ddd->hydro[id][i] = ih;
1009 ddd->nhydro[id]++;
1013 static void add_dh(t_donors *ddd,int id,int ih,int grp, unsigned char *datable)
1015 int i;
1017 if (ISDON(datable[id]) || !datable) {
1018 if (ddd->dptr[id] == NOTSET) { /* New donor */
1019 i = ddd->nrd;
1020 ddd->dptr[id] = i;
1021 } else
1022 i = ddd->dptr[id];
1024 if (i == ddd->nrd) {
1025 if (ddd->nrd >= ddd->max_nrd) {
1026 ddd->max_nrd += 128;
1027 srenew(ddd->don,ddd->max_nrd);
1028 srenew(ddd->nhydro,ddd->max_nrd);
1029 srenew(ddd->hydro,ddd->max_nrd);
1030 srenew(ddd->nhbonds,ddd->max_nrd);
1031 srenew(ddd->grp,ddd->max_nrd);
1033 ddd->don[ddd->nrd] = id;
1034 ddd->nhydro[ddd->nrd] = 0;
1035 ddd->grp[ddd->nrd] = grp;
1036 ddd->nrd++;
1037 } else
1038 ddd->don[i] = id;
1039 add_h2d(i,ih,ddd);
1040 } else
1041 if (datable)
1042 printf("Warning: Atom %d is not in the d/a-table!\n", id);
1045 static void search_donors(t_topology *top, int isize, atom_id *index,
1046 t_donors *ddd,int grp,bool bContact,bool bDoIt,
1047 unsigned char *datable)
1049 int i,j,nra,n;
1050 t_functype func_type;
1051 t_ilist *interaction;
1052 atom_id nr1,nr2;
1053 bool stop;
1055 if (!ddd->dptr) {
1056 snew(ddd->dptr,top->atoms.nr);
1057 for(i=0; (i<top->atoms.nr); i++)
1058 ddd->dptr[i] = NOTSET;
1061 if (bContact) {
1062 if (bDoIt)
1063 for(i=0; (i<isize); i++) {
1064 datable[index[i]] |= DON;
1065 add_dh(ddd,index[i],-1,grp,datable);
1068 else {
1069 for(func_type=0; (func_type < F_NRE); func_type++) {
1070 interaction=&(top->idef.il[func_type]);
1071 for(i=0; i < interaction->nr;
1072 i+=interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms+1) {
1073 /* next function */
1074 if (func_type != top->idef.functype[interaction->iatoms[i]]) {
1075 fprintf(stderr,"Error in func_type %s",
1076 interaction_function[func_type].longname);
1077 continue;
1080 /* check out this functype */
1081 if (func_type == F_SETTLE) {
1082 nr1=interaction->iatoms[i+1];
1084 if (ISINGRP(datable[nr1])) {
1085 if (ISINGRP(datable[nr1+1])) {
1086 datable[nr1] |= DON;
1087 add_dh(ddd,nr1,nr1+1,grp,datable);
1089 if (ISINGRP(datable[nr1+2])) {
1090 datable[nr1] |= DON;
1091 add_dh(ddd,nr1,nr1+2,grp,datable);
1095 else if (IS_CHEMBOND(func_type)) {
1096 for (j=0; j<2; j++) {
1097 nr1=interaction->iatoms[i+1+j];
1098 nr2=interaction->iatoms[i+2-j];
1099 if ((*top->atoms.atomname[nr1][0] == 'H') &&
1100 ((*top->atoms.atomname[nr2][0] == 'O') ||
1101 (*top->atoms.atomname[nr2][0] == 'N')) &&
1102 ISINGRP(datable[nr1]) && ISINGRP(datable[nr2])) {
1103 datable[nr2] |= DON;
1104 add_dh(ddd,nr2,nr1,grp,datable);
1110 #ifdef SAFEVSITES
1111 for(func_type=0; func_type < F_NRE; func_type++) {
1112 interaction=&top->idef.il[func_type];
1113 for(i=0; i < interaction->nr;
1114 i+=interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms+1) {
1115 /* next function */
1116 if (func_type != top->idef.functype[interaction->iatoms[i]])
1117 gmx_incons("function type in search_donors");
1119 if ( interaction_function[func_type].flags & IF_VSITE ) {
1120 nr1=interaction->iatoms[i+1];
1121 if ( *top->atoms.atomname[nr1][0] == 'H') {
1122 nr2=nr1-1;
1123 stop=FALSE;
1124 while (!stop && ( *top->atoms.atomname[nr2][0] == 'H'))
1125 if (nr2)
1126 nr2--;
1127 else
1128 stop=TRUE;
1129 if ( !stop && ( ( *top->atoms.atomname[nr2][0] == 'O') ||
1130 ( *top->atoms.atomname[nr2][0] == 'N') ) &&
1131 ISINGRP(datable[nr1]) && ISINGRP(datable[nr2])) {
1132 datable[nr2] |= DON;
1133 add_dh(ddd,nr2,nr1,grp,datable);
1139 #endif
1143 static t_gridcell ***init_grid(bool bBox,rvec box[],real rcut,ivec ngrid)
1145 t_gridcell ***grid;
1146 int i,y,z;
1148 if (bBox)
1149 for(i=0; i<DIM; i++)
1150 ngrid[i]=(box[i][i]/(1.2*rcut));
1152 if ( !bBox || (ngrid[XX]<3) || (ngrid[YY]<3) || (ngrid[ZZ]<3) )
1153 for(i=0; i<DIM; i++)
1154 ngrid[i]=1;
1155 else
1156 printf("\nWill do grid-seach on %dx%dx%d grid, rcut=%g\n",
1157 ngrid[XX],ngrid[YY],ngrid[ZZ],rcut);
1158 snew(grid,ngrid[ZZ]);
1159 for (z=0; z<ngrid[ZZ]; z++) {
1160 snew((grid)[z],ngrid[YY]);
1161 for (y=0; y<ngrid[YY]; y++)
1162 snew((grid)[z][y],ngrid[XX]);
1164 return grid;
1167 static void control_pHist(t_hbdata *hb, int nframes)
1169 int i,j,k;
1170 PSTYPE p;
1171 for (i=0;i<hb->d.nrd;i++)
1172 for (j=0;j<hb->a.nra;j++)
1173 if (hb->per->pHist[i][j].len != 0)
1174 for (k=hb->hbmap[i][j][0].n0; k<nframes; k++) {
1175 p = getPshift(hb->per->pHist[i][j], k);
1176 if (p>hb->per->nper)
1177 fprintf(stderr, "Weird stuff in pHist[%i][%i].p at frame %i: p=%i\n",
1178 i,j,k,p);
1182 static void reset_nhbonds(t_donors *ddd)
1184 int i,j;
1186 for(i=0; (i<ddd->nrd); i++)
1187 for(j=0; (j<MAXHH); j++)
1188 ddd->nhbonds[i][j] = 0;
1191 void pbc_correct_gem(rvec dx,matrix box,rvec hbox);
1193 static void build_grid(t_hbdata *hb,rvec x[], rvec xshell,
1194 bool bBox, matrix box, rvec hbox,
1195 real rcut, real rshell,
1196 ivec ngrid, t_gridcell ***grid)
1198 int i,m,gr,xi,yi,zi,nr;
1199 atom_id *ad;
1200 ivec grididx;
1201 rvec invdelta,dshell,xtemp;
1202 t_ncell *newgrid;
1203 bool bDoRshell,bInShell,bAcc;
1204 real rshell2=0;
1205 int gx,gy,gz;
1206 int dum = -1;
1208 bDoRshell = (rshell > 0);
1209 rshell2 = sqr(rshell);
1210 bInShell = TRUE;
1212 #define DBB(x) if (debug && bDebug) fprintf(debug,"build_grid, line %d, %s = %d\n",__LINE__,#x,x)
1213 DBB(dum);
1214 for(m=0; m<DIM; m++) {
1215 hbox[m]=box[m][m]*0.5;
1216 if (bBox) {
1217 invdelta[m]=ngrid[m]/box[m][m];
1218 if (1/invdelta[m] < rcut)
1219 gmx_fatal(FARGS,"Your computational box has shrunk too much.\n"
1220 "%s can not handle this situation, sorry.\n",
1221 ShortProgram());
1222 } else
1223 invdelta[m]=0;
1225 grididx[XX]=0;
1226 grididx[YY]=0;
1227 grididx[ZZ]=0;
1228 DBB(dum);
1229 /* resetting atom counts */
1230 for(gr=0; (gr<grNR); gr++) {
1231 for (zi=0; zi<ngrid[ZZ]; zi++)
1232 for (yi=0; yi<ngrid[YY]; yi++)
1233 for (xi=0; xi<ngrid[XX]; xi++) {
1234 grid[zi][yi][xi].d[gr].nr=0;
1235 grid[zi][yi][xi].a[gr].nr=0;
1237 DBB(dum);
1239 /* put atoms in grid cells */
1240 for(bAcc=FALSE; (bAcc<=TRUE); bAcc++) {
1241 if (bAcc) {
1242 nr = hb->a.nra;
1243 ad = hb->a.acc;
1245 else {
1246 nr = hb->d.nrd;
1247 ad = hb->d.don;
1249 DBB(bAcc);
1250 for(i=0; (i<nr); i++) {
1251 /* check if we are inside the shell */
1252 /* if bDoRshell=FALSE then bInShell=TRUE always */
1253 DBB(i);
1254 if ( bDoRshell ) {
1255 bInShell=TRUE;
1256 rvec_sub(x[ad[i]],xshell,dshell);
1257 if (bBox) {
1258 if (FALSE && !hb->bGem) {
1259 for(m=DIM-1; m>=0 && bInShell; m--) {
1260 if ( dshell[m] < -hbox[m] )
1261 rvec_inc(dshell,box[m]);
1262 else if ( dshell[m] >= hbox[m] )
1263 dshell[m] -= 2*hbox[m];
1264 /* if we're outside the cube, we're outside the sphere also! */
1265 if ( (dshell[m]>rshell) || (-dshell[m]>rshell) )
1266 bInShell=FALSE;
1268 } else {
1269 bool bDone = FALSE;
1270 while (!bDone)
1272 bDone = TRUE;
1273 for(m=DIM-1; m>=0 && bInShell; m--) {
1274 if ( dshell[m] < -hbox[m] ) {
1275 bDone = FALSE;
1276 rvec_inc(dshell,box[m]);
1278 if ( dshell[m] >= hbox[m] ) {
1279 bDone = FALSE;
1280 dshell[m] -= 2*hbox[m];
1284 for(m=DIM-1; m>=0 && bInShell; m--) {
1285 /* if we're outside the cube, we're outside the sphere also! */
1286 if ( (dshell[m]>rshell) || (-dshell[m]>rshell) )
1287 bInShell=FALSE;
1291 /* if we're inside the cube, check if we're inside the sphere */
1292 if (bInShell)
1293 bInShell = norm2(dshell) < rshell2;
1295 DBB(i);
1296 if ( bInShell ) {
1297 if (bBox) {
1298 if (hb->bGem)
1299 copy_rvec(x[ad[i]], xtemp);
1300 pbc_correct_gem(x[ad[i]], box, hbox);
1302 for(m=DIM-1; m>=0; m--) {
1303 if (TRUE || !hb->bGem){
1304 /* put atom in the box */
1305 while( x[ad[i]][m] < 0 )
1306 rvec_inc(x[ad[i]],box[m]);
1307 while( x[ad[i]][m] >= box[m][m] )
1308 rvec_dec(x[ad[i]],box[m]);
1310 /* determine grid index of atom */
1311 grididx[m]=x[ad[i]][m]*invdelta[m];
1312 grididx[m] = (grididx[m]+ngrid[m]) % ngrid[m];
1314 if (hb->bGem)
1315 copy_rvec(xtemp, x[ad[i]]); /* copy back */
1316 gx = grididx[XX];
1317 gy = grididx[YY];
1318 gz = grididx[ZZ];
1319 range_check(gx,0,ngrid[XX]);
1320 range_check(gy,0,ngrid[YY]);
1321 range_check(gz,0,ngrid[ZZ]);
1322 DBB(gx);
1323 DBB(gy);
1324 DBB(gz);
1325 /* add atom to grid cell */
1326 if (bAcc)
1327 newgrid=&(grid[gz][gy][gx].a[gr]);
1328 else
1329 newgrid=&(grid[gz][gy][gx].d[gr]);
1330 if (newgrid->nr >= newgrid->maxnr) {
1331 newgrid->maxnr+=10;
1332 DBB(newgrid->maxnr);
1333 srenew(newgrid->atoms, newgrid->maxnr);
1335 DBB(newgrid->nr);
1336 newgrid->atoms[newgrid->nr]=ad[i];
1337 newgrid->nr++;
1344 static void count_da_grid(ivec ngrid, t_gridcell ***grid, t_icell danr)
1346 int gr,xi,yi,zi;
1348 for(gr=0; (gr<grNR); gr++) {
1349 danr[gr]=0;
1350 for (zi=0; zi<ngrid[ZZ]; zi++)
1351 for (yi=0; yi<ngrid[YY]; yi++)
1352 for (xi=0; xi<ngrid[XX]; xi++) {
1353 danr[gr] += grid[zi][yi][xi].d[gr].nr;
1358 /* The grid loop.
1359 * Without a box, the grid is 1x1x1, so all loops are 1 long.
1360 * With a rectangular box (bTric==FALSE) all loops are 3 long.
1361 * With a triclinic box all loops are 3 long, except when a cell is
1362 * located next to one of the box edges which is not parallel to the
1363 * x/y-plane, in that case all cells in a line or layer are searched.
1364 * This could be implemented slightly more efficient, but the code
1365 * would get much more complicated.
1367 #define B(n,x,bTric,bEdge) ((n==1) ? x : bTric&&(bEdge) ? 0 : (x-1))
1368 #define E(n,x,bTric,bEdge) ((n==1) ? x : bTric&&(bEdge) ? n-1 : (x+1))
1369 #define GRIDMOD(j,n) (j+n)%(n)
1370 #define LOOPGRIDINNER(x,y,z,xx,yy,zz,xo,yo,zo,n,bTric) \
1371 for(zz=B(n[ZZ],zo,bTric,FALSE); zz<=E(n[ZZ],zo,bTric,FALSE); zz++) { \
1372 z=GRIDMOD(zz,n[ZZ]); \
1373 for(yy=B(n[YY],yo,bTric,z==0||z==n[ZZ]-1); \
1374 yy<=E(n[YY],yo,bTric,z==0||z==n[ZZ]-1); yy++) { \
1375 y=GRIDMOD(yy,n[YY]); \
1376 for(xx=B(n[XX],xo,bTric,y==0||y==n[YY]-1||z==0||z==n[ZZ]-1); \
1377 xx<=E(n[XX],xo,bTric,y==0||y==n[YY]-1||z==0||z==n[ZZ]-1); xx++) { \
1378 x=GRIDMOD(xx,n[XX]);
1379 #define ENDLOOPGRIDINNER \
1385 static void dump_grid(FILE *fp, ivec ngrid, t_gridcell ***grid)
1387 int gr,x,y,z,sum[grNR];
1389 fprintf(fp,"grid %dx%dx%d\n",ngrid[XX],ngrid[YY],ngrid[ZZ]);
1390 for (gr=0; gr<grNR; gr++) {
1391 sum[gr]=0;
1392 fprintf(fp,"GROUP %d (%s)\n",gr,grpnames[gr]);
1393 for (z=0; z<ngrid[ZZ]; z+=2) {
1394 fprintf(fp,"Z=%d,%d\n",z,z+1);
1395 for (y=0; y<ngrid[YY]; y++) {
1396 for (x=0; x<ngrid[XX]; x++) {
1397 fprintf(fp,"%3d",grid[x][y][z].d[gr].nr);
1398 sum[gr]+=grid[z][y][x].d[gr].nr;
1399 fprintf(fp,"%3d",grid[x][y][z].a[gr].nr);
1400 sum[gr]+=grid[z][y][x].a[gr].nr;
1403 fprintf(fp," | ");
1404 if ( (z+1) < ngrid[ZZ] )
1405 for (x=0; x<ngrid[XX]; x++) {
1406 fprintf(fp,"%3d",grid[z+1][y][x].d[gr].nr);
1407 sum[gr]+=grid[z+1][y][x].d[gr].nr;
1408 fprintf(fp,"%3d",grid[z+1][y][x].a[gr].nr);
1409 sum[gr]+=grid[z+1][y][x].a[gr].nr;
1411 fprintf(fp,"\n");
1415 fprintf(fp,"TOTALS:");
1416 for (gr=0; gr<grNR; gr++)
1417 fprintf(fp," %d=%d",gr,sum[gr]);
1418 fprintf(fp,"\n");
1421 /* New GMX record! 5 * in a row. Congratulations!
1422 * Sorry, only four left.
1424 static void free_grid(ivec ngrid, t_gridcell ****grid)
1426 int y,z;
1427 t_gridcell ***g = *grid;
1429 for (z=0; z<ngrid[ZZ]; z++) {
1430 for (y=0; y<ngrid[YY]; y++) {
1431 sfree(g[z][y]);
1433 sfree(g[z]);
1435 sfree(g);
1436 g=NULL;
1439 static void pbc_correct(rvec dx,matrix box,rvec hbox)
1441 int m;
1442 for(m=DIM-1; m>=0; m--) {
1443 if ( dx[m] < -hbox[m] )
1444 rvec_inc(dx,box[m]);
1445 else if ( dx[m] >= hbox[m] )
1446 rvec_dec(dx,box[m]);
1450 void pbc_correct_gem(rvec dx,matrix box,rvec hbox)
1452 int m;
1453 bool bDone = FALSE;
1454 while (!bDone) {
1455 bDone = TRUE;
1456 for(m=DIM-1; m>=0; m--) {
1457 if ( dx[m] < -hbox[m] ) {
1458 bDone = FALSE;
1459 rvec_inc(dx,box[m]);
1461 if ( dx[m] >= hbox[m] ) {
1462 bDone = FALSE;
1463 rvec_dec(dx,box[m]);
1469 /* Added argument r2cut, changed contact and implemented
1470 * use of second cut-off.
1471 * - Erik Marklund, June 29, 2006
1473 static int is_hbond(t_hbdata *hb,int grpd,int grpa,int d,int a,
1474 real rcut, real r2cut, real ccut,
1475 rvec x[], bool bBox, matrix box,rvec hbox,
1476 real *d_ha, real *ang,bool bDA,int *hhh,
1477 bool bContact, bool bMerge, PSTYPE *p)
1479 int h,hh,id,ja,ihb;
1480 rvec r_da,r_ha,r_dh, r;
1481 ivec ri;
1482 real rc2,r2c2,rda2,rha2,ca;
1483 bool HAinrange = FALSE; /* If !bDA. Needed for returning hbDist in a correct way. */
1484 bool daSwap = FALSE;
1486 if (d == a)
1487 return hbNo;
1489 if (((id = donor_index(&hb->d,grpd,d)) == NOTSET) ||
1490 ((ja = acceptor_index(&hb->a,grpa,a)) == NOTSET))
1491 return hbNo;
1493 rc2 = rcut*rcut;
1494 r2c2 = r2cut*r2cut;
1496 rvec_sub(x[d],x[a],r_da);
1497 /* Insert projection code here */
1499 /* if (d>a && ((isInterchangable(hb, d, a, grpd, grpa) && bMerge) || bContact)) */
1500 /* /\* Then this hbond will be found again, or it has already been found. *\/ */
1501 /* return hbNo; */
1503 if (bBox){
1504 if (d>a && bMerge && (bContact || isInterchangable(hb, d, a, grpd, grpa))) { /* acceptor is also a donor and vice versa? */
1505 return hbNo;
1506 daSwap = TRUE; /* If so, then their history should be filed with donor and acceptor swapped. */
1508 if (hb->bGem) {
1509 copy_rvec(r_da, r); /* Save this for later */
1510 pbc_correct_gem(r_da,box,hbox);
1511 } else {
1512 pbc_correct_gem(r_da,box,hbox);
1515 rda2 = iprod(r_da,r_da);
1517 if (bContact) {
1518 if (daSwap)
1519 return hbNo;
1520 if (rda2 <= rc2){
1521 if (hb->bGem){
1522 calcBoxDistance(hb->per->P, r, ri);
1523 *p = periodicIndex(ri, hb->per, daSwap); /* find (or add) periodicity index. */
1525 return hbHB;
1527 else if (rda2 < r2c2)
1528 return hbDist;
1529 else
1530 return hbNo;
1532 *hhh = NOTSET;
1534 if (bDA && (rda2 > rc2))
1535 return hbNo;
1537 for(h=0; (h < hb->d.nhydro[id]); h++) {
1538 hh = hb->d.hydro[id][h];
1539 rha2 = rc2+1;
1540 if (!bDA) {
1541 rvec_sub(x[hh],x[a],r_ha);
1542 if (bBox) {
1543 pbc_correct_gem(r_ha,box,hbox);
1545 rha2 = iprod(r_ha,r_ha);
1548 if (hb->bGem) {
1549 calcBoxDistance(hb->per->P, r, ri);
1550 *p = periodicIndex(ri, hb->per, daSwap); /* find periodicity index. */
1553 if (bDA || (!bDA && (rha2 <= rc2))) {
1554 rvec_sub(x[d],x[hh],r_dh);
1555 if (bBox) {
1556 if (hb->bGem)
1557 pbc_correct_gem(r_dh,box,hbox);
1558 else
1559 pbc_correct_gem(r_dh,box,hbox);
1562 if (!bDA)
1563 HAinrange = TRUE;
1564 ca = cos_angle(r_dh,r_da);
1565 /* if angle is smaller, cos is larger */
1566 if (ca >= ccut) {
1567 *hhh = hh;
1568 *d_ha = sqrt(bDA?rda2:rha2);
1569 *ang = acos(ca);
1570 return hbHB;
1574 if (bDA || (!bDA && HAinrange))
1575 return hbDist;
1576 else
1577 return hbNo;
1580 /* Fixed previously undiscovered bug in the merge
1581 code, where the last frame of each hbond disappears.
1582 - Erik Marklund, June 1, 2006 */
1583 /* Added the following arguments:
1584 * ptmp[] - temporary periodicity hisory
1585 * a1 - identity of first acceptor/donor
1586 * a2 - identity of second acceptor/donor
1587 * - Erik Marklund, FEB 20 2010 */
1589 /* Merging is now done on the fly, so do_merge is most likely obsolete now.
1590 * Will do some more testing before removing the function entirely.
1591 * - Erik Marklund, MAY 10 2010 */
1592 static void do_merge(t_hbdata *hb,int ntmp,
1593 unsigned int htmp[],unsigned int gtmp[],PSTYPE ptmp[],
1594 t_hbond *hb0,t_hbond *hb1, int a1, int a2)
1596 /* Here we need to make sure we're treating periodicity in
1597 * the right way for the geminate recombination kinetics. */
1599 int m,mm,n00,n01,nn0,nnframes;
1600 PSTYPE pm;
1601 t_pShift *pShift;
1603 /* Decide where to start from when merging */
1604 n00 = hb0->n0;
1605 n01 = hb1->n0;
1606 nn0 = min(n00,n01);
1607 nnframes = max(n00 + hb0->nframes,n01 + hb1->nframes) - nn0;
1608 /* Initiate tmp arrays */
1609 for(m=0; (m<ntmp); m++) {
1610 htmp[m] = 0;
1611 gtmp[m] = 0;
1612 ptmp[m] = 0;
1614 /* Fill tmp arrays with values due to first HB */
1615 /* Once again '<' had to be replaced with '<='
1616 to catch the last frame in which the hbond
1617 appears.
1618 - Erik Marklund, June 1, 2006 */
1619 for(m=0; (m<=hb0->nframes); m++) {
1620 mm = m+n00-nn0;
1621 htmp[mm] = is_hb(hb0->h[0],m);
1622 if (hb->bGem) {
1623 pm = getPshift(hb->per->pHist[a1][a2], m+hb0->n0);
1624 if (pm > hb->per->nper)
1625 gmx_fatal(FARGS, "Illegal shift!");
1626 else
1627 ptmp[mm] = pm; /*hb->per->pHist[a1][a2][m];*/
1630 /* If we're doing geminate recompbination we usually don't need the distances.
1631 * Let's save some memory and time. */
1632 if (TRUE || !hb->bGem || hb->per->gemtype == gemAD){
1633 for(m=0; (m<=hb0->nframes); m++) {
1634 mm = m+n00-nn0;
1635 gtmp[mm] = is_hb(hb0->g[0],m);
1638 /* Next HB */
1639 for(m=0; (m<=hb1->nframes); m++) {
1640 mm = m+n01-nn0;
1641 htmp[mm] = htmp[mm] || is_hb(hb1->h[0],m);
1642 gtmp[mm] = gtmp[mm] || is_hb(hb1->g[0],m);
1643 if (hb->bGem /* && ptmp[mm] != 0 */) {
1645 /* If this hbond has been seen before with donor and acceptor swapped,
1646 * then we need to find the mirrored (*-1) periodicity vector to truely
1647 * merge the hbond history. */
1648 pm = findMirror(getPshift(hb->per->pHist[a2][a1],m+hb1->n0), hb->per->p2i, hb->per->nper);
1649 /* Store index of mirror */
1650 if (pm > hb->per->nper)
1651 gmx_fatal(FARGS, "Illegal shift!");
1652 ptmp[mm] = pm;
1655 /* Reallocate target array */
1656 if (nnframes > hb0->maxframes) {
1657 srenew(hb0->h[0],4+nnframes/hb->wordlen);
1658 srenew(hb0->g[0],4+nnframes/hb->wordlen);
1660 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,bool bTwo, 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,bool bMerge,bool bContact,
1768 const output_env_t oenv)
1770 FILE *fp;
1771 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","Time (ps)","()",oenv);
1832 else
1833 fp = xvgropen(fn,"Uninterrupted hydrogen bond lifetime","Time (ps)","()",
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,bool oneHB,int nDump)
1867 FILE *fp;
1868 int i,j,k,m,nd,ihb,idist;
1869 int nframes = hb->nframes;
1870 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 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,real aver_nhb,real aver_dist,
2269 int nDump,bool bMerge,bool bContact, real fit_start,
2270 real temp,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 bool bBallistic, const bool bGemFit)
2274 FILE *fp;
2275 int i,j,k,m,n,o,nd,ihb,idist,n2,nn,iter;
2276 static char *legNN[] = { "Ac(t)",
2277 "Ac'(t)"};
2278 static char **legGem;
2279 snew(legGem, 3);
2280 for (i=0;i<3;i++)
2281 snew(legGem[i], 128);
2282 sprintf(legGem[0], "Ac\\s%s\\v{}\\z{}(t)", gemType);
2283 sprintf(legGem[1], "Ac'(t)");
2284 sprintf(legGem[2], "Ac\\s%s,fit\\v{}\\z{}(t)", gemType);
2286 static char *legLuzar[] = { "Ac\\sfin sys\\v{}\\z{}(t)",
2287 "Ac(t)",
2288 "Cc\\scontact,hb\\v{}\\z{}(t)",
2289 "-dAc\\sfs\\v{}\\z{}/dt" };
2290 bool bNorm=FALSE;
2291 double nhb = 0;
2292 int nhbi=0;
2293 real *rhbex=NULL,*ht,*gt,*ght,*dght,*kt;
2294 real *ct,*p_ct,tail,tail2,dtail,ct_fac,ght_fac,*cct;
2295 const real tol = 1e-3;
2296 int nframes = hb->nframes,nf;
2297 unsigned int **h,**g;
2298 int nh,nhbonds,nhydro,ngh;
2299 t_hbond *hbh;
2300 PSTYPE p, *pfound = NULL, np;
2301 t_pShift *pHist;
2302 int *ptimes=NULL, *poff=NULL, anhb, n0, mMax=INT_MIN;
2303 real **rHbExGem = NULL;
2304 bool c;
2305 int acType;
2306 t_E *E;
2307 double *ctdouble, *timedouble, *fittedct;
2308 double fittolerance=0.1;
2310 enum {AC_NONE, AC_NN, AC_GEM, AC_LUZAR};
2313 #ifdef HAVE_OPENMP
2314 int *dondata=NULL, thisThread;
2315 #endif
2317 printf("Doing autocorrelation ");
2319 /* Decide what kind of ACF calculations to do. */
2320 if (NN > NN_NONE && NN < NN_NR) {
2321 #ifdef HAVE_NN_LOOPS
2322 acType = AC_NN;
2323 printf("using the energy estimate.\n");
2324 #else
2325 acType = AC_NONE;
2326 printf("Can't do the NN-loop. Yet.\n");
2327 #endif
2328 } else if (hb->bGem) {
2329 acType = AC_GEM;
2330 printf("according to the reversible geminate recombination model by Omer Markowitch.\n");
2331 } else {
2332 acType = AC_LUZAR;
2333 printf("according to the theory of Luzar and Chandler.\n");
2335 fflush(stdout);
2337 /* build hbexist matrix in reals for autocorr */
2338 /* Allocate memory for computing ACF (rhbex) and aggregating the ACF (ct) */
2339 n2=1;
2340 while (n2 < nframes)
2341 n2*=2;
2343 nn = nframes/2;
2345 if (acType != AC_NN ||
2346 #ifndef HAVE_OPENMP
2347 TRUE
2348 #else
2349 FALSE
2350 #endif
2352 snew(h,hb->maxhydro);
2353 snew(g,hb->maxhydro);
2356 /* Dump hbonds for debugging */
2357 dump_ac(hb,bMerge||bContact,nDump);
2359 /* Total number of hbonds analyzed here */
2360 nhbonds = 0;
2361 ngh = 0;
2362 anhb = 0;
2364 /* ------------------------------------------------
2365 * I got tired of waiting for the acf calculations
2366 * and parallelized it with openMP
2367 * set environment variable CFLAGS = "-fopenmp" when running
2368 * configure and define DOUSEOPENMP to make use of it.
2371 #ifdef HAVE_OPENMP /* ================================================= \
2372 * Set up the OpenMP stuff, |
2373 * like the number of threads and such |
2375 if (acType != AC_LUZAR)
2377 #if (_OPENMP >= 200805) /* =====================\ */
2378 nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_thread_limit());
2379 #else
2380 nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_num_procs());
2381 #endif /* _OPENMP >= 200805 ====================/ */
2383 omp_set_num_threads(nThreads);
2384 snew(dondata, nThreads);
2385 for (i=0; i<nThreads; i++)
2386 dondata[i] = -1;
2387 printf("ACF calculations parallelized with OpenMP using %i threads.\n"
2388 "Expect close to linear scaling over this donor-loop.\n", nThreads);
2389 fflush(stdout);
2390 fprintf(stderr, "Donors: [thread no]\n");
2392 char tmpstr[7];
2393 for (i=0;i<nThreads;i++) {
2394 snprintf(tmpstr, 7, "[%i]", i);
2395 fprintf(stderr, "%-7s", tmpstr);
2398 fprintf(stderr, "\n"); /* | */
2399 } /* | */
2400 #endif /* HAVE_OPENMP ===================================================/ */
2403 /* Build the ACF according to acType */
2404 switch (acType)
2407 case AC_NN:
2408 #ifdef HAVE_NN_LOOPS
2409 /* Here we're using the estimated energy for the hydrogen bonds. */
2410 snew(ct,nn);
2411 #ifdef HAVE_OPENMP /* ==================================\ */
2412 #pragma omp parallel \
2413 private(i, j, k, nh, E, rhbex, thisThread), \
2414 default(shared)
2416 #pragma omp barrier
2417 thisThread = omp_get_thread_num();
2418 rhbex = NULL;
2419 #endif /* ==============================================/ */
2421 snew(rhbex, n2);
2422 memset(rhbex, 0, n2*sizeof(real)); /* Trust no-one, not even malloc()! */
2424 #ifdef HAVE_OPENMP /* ################################################## \
2428 #pragma omp barrier
2429 #pragma omp for schedule (dynamic)
2430 #endif
2431 for (i=0; i<hb->d.nrd; i++) /* loop over donors */
2433 #ifdef HAVE_OPENMP /* ====== Write some output ======\ */
2434 #pragma omp critical
2436 dondata[thisThread] = i;
2437 parallel_print(dondata, nThreads);
2439 #else
2440 fprintf(stderr, "\r %i", i);
2441 #endif /* ===========================================/ */
2443 for (j=0; j<hb->a.nra; j++) /* loop over acceptors */
2445 for (nh=0; nh<hb->d.nhydro[i]; nh++) /* loop over donors' hydrogens */
2447 E = hb->hbE.E[i][j][nh];
2448 if (E != NULL)
2450 for (k=0; k<nframes; k++)
2452 if (E[k] != NONSENSE_E)
2453 rhbex[k] = (real)E[k];
2456 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&(rhbex),hb->time[1]-hb->time[0],
2457 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2458 #ifdef HAVE_OPENMP
2459 #pragma omp critical
2460 #endif
2462 for(k=0; (k<nn); k++)
2463 ct[k] += rhbex[k];
2466 } /* k loop */
2467 } /* j loop */
2468 } /* i loop */
2469 sfree(rhbex);
2470 #pragma omp barrier
2471 #ifdef HAVE_OPENMP
2472 /* # */
2473 } /* End of parallel block # */
2474 /* ##############################################################/ */
2475 sfree(dondata);
2476 #endif
2477 normalizeACF(ct, NULL, nn);
2478 snew(ctdouble, nn);
2479 snew(timedouble, nn);
2480 for (j=0; j<nn; j++)
2482 timedouble[j]=(double)(hb->time[j]);
2483 ctdouble[j]=(double)(ct[j]);
2486 /* Remove ballistic term */
2487 if (params->ballistic/params->tDelta >= params->nExpFit*2+1)
2488 takeAwayBallistic(ctdouble, timedouble, nn, params->ballistic, params->nExpFit, params->bDt);
2489 else
2490 printf("\nNumber of data points is less than the number of parameters to fit\n."
2491 "The system is underdetermined, hence no ballistic term can be found.\n\n");
2493 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation","Time (ps)","C(t)");
2494 xvgr_legend(fp,asize(legNN),legNN);
2496 for(j=0; (j<nn); j++)
2497 fprintf(fp,"%10g %10g %10g\n",
2498 hb->time[j]-hb->time[0],
2499 ct[j],
2500 ctdouble[j]);
2501 fclose(fp);
2502 sfree(ct);
2503 sfree(ctdouble);
2504 sfree(timedouble);
2505 #endif /* HAVE_NN_LOOPS */
2506 break; /* case AC_NN */
2508 case AC_GEM:
2509 snew(ct,2*n2);
2510 memset(ct,0,2*n2*sizeof(real));
2511 #ifndef HAVE_OPENMP
2512 fprintf(stderr, "Donor:\n");
2513 #define __ACDATA ct
2514 #else
2515 #define __ACDATA p_ct
2516 #endif
2518 #ifdef HAVE_OPENMP /* =========================================\
2519 * */
2520 #pragma omp parallel default(none) \
2521 private(i, k, nh, hbh, pHist, h, g, n0, nf, np, j, m, \
2522 pfound, poff, rHbExGem, p, ihb, mMax, \
2523 thisThread, p_ct) \
2524 shared(hb, dondata, ct, nn, nThreads, n2, stderr, bNorm, \
2525 nframes, bMerge, bContact)
2526 { /* ########## THE START OF THE ENORMOUS PARALLELIZED BLOCK! ########## */
2527 h = NULL;
2528 g = NULL;
2529 thisThread = omp_get_thread_num();
2530 snew(h,hb->maxhydro);
2531 snew(g,hb->maxhydro);
2532 mMax = INT_MIN;
2533 rHbExGem = NULL;
2534 poff = NULL;
2535 pfound = NULL;
2536 p_ct = NULL;
2537 snew(p_ct,2*n2);
2538 memset(p_ct,0,2*n2*sizeof(real));
2540 /* I'm using a chunk size of 1, since I expect \
2541 * the overhead to be really small compared \
2542 * to the actual calculations \ */
2543 #pragma omp for schedule(dynamic,1) nowait /* \ */
2544 #endif /* HAVE_OPENMP =========================================/ */
2546 for (i=0; i<hb->d.nrd; i++) {
2547 #ifdef HAVE_OPENMP
2548 #pragma omp critical
2550 dondata[thisThread] = i;
2551 parallel_print(dondata, nThreads);
2553 #else
2554 fprintf(stderr, "\r %i", i);
2555 #endif
2557 for (k=0; k<hb->a.nra; k++) {
2558 for (nh=0; nh < ((bMerge || bContact) ? 1 : hb->d.nhydro[i]); nh++) {
2559 hbh = hb->hbmap[i][k];
2560 if (hbh) {
2561 /* Note that if hb->per->gemtype==gemDD, then distances will be stored in
2562 * hb->hbmap[d][a].h array anyway, because the contact flag will be set.
2563 * hence, it's only with the gemAD mode that hb->hbmap[d][a].g will be used. */
2564 pHist = &(hb->per->pHist[i][k]);
2565 if (ISHB(hbh->history[nh]) && pHist->len != 0) {
2567 /* No need for a critical section */
2568 /* #ifdef HAVE_OPENMP */
2569 /* #pragma omp critical */
2570 /* #endif */
2572 h[nh] = hbh->h[nh];
2573 g[nh] = hb->per->gemtype==gemAD ? hbh->g[nh] : NULL;
2575 n0 = hbh->n0;
2576 nf = hbh->nframes;
2577 /* count the number of periodic shifts encountered and store
2578 * them in separate arrays. */
2579 np = 0;
2580 for (j=0; j<pHist->len; j++)
2582 p = pHist->p[j];
2583 for (m=0; m<=np; m++) {
2584 if (m == np) { /* p not recognized in list. Add it and set up new array. */
2585 np++;
2586 if (np>hb->per->nper)
2587 gmx_fatal(FARGS, "Too many pshifts. Something's utterly wrong here.");
2588 if (m>=mMax) { /* Extend the arrays.
2589 * Doing it like this, using mMax to keep track of the sizes,
2590 * eleviates the need for freeing and re-allocating the arrays
2591 * when taking on the next donor-acceptor pair */
2592 mMax = m;
2593 srenew(pfound,np); /* The list of found periodic shifts. */
2594 srenew(rHbExGem,np); /* The hb existence functions (-aver_hb). */
2595 snew(rHbExGem[m],2*n2);
2596 srenew(poff,np);
2599 /* This shouldn't have to be critical, right? */
2600 /* #ifdef HAVE_OPENMP */
2601 /* #pragma omp critical */
2602 /* #endif */
2604 if (rHbExGem != NULL && rHbExGem[m] != NULL) {
2605 /* This must be done, as this array was most likey
2606 * used to store stuff in some previous iteration. */
2607 memset(rHbExGem[m], 0, (sizeof(real)) * (2*n2));
2609 else
2610 fprintf(stderr, "rHbExGem not initialized! m = %i\n", m);
2612 pfound[m] = p;
2613 poff[m] = -1;
2615 break;
2616 } /* m==np */
2617 if (p == pfound[m])
2618 break;
2619 } /* m: Loop over found shifts */
2620 } /* j: Loop over shifts */
2622 /* Now unpack and disentangle the existence funtions. */
2623 for (j=0; j<nf; j++) {
2624 /* i: donor,
2625 * k: acceptor
2626 * nh: hydrogen
2627 * j: time
2628 * p: periodic shift
2629 * pfound: list of periodic shifts found for this pair.
2630 * poff: list of frame offsets; that is, the first
2631 * frame a hbond has a particular periodic shift. */
2632 p = getPshift(*pHist, j+n0);
2633 if (p != -1)
2635 for (m=0; m<np; m++)
2637 if (pfound[m] == p)
2638 break;
2639 if (m==(np-1))
2640 gmx_fatal(FARGS,"Shift not found, but must be there.");
2643 ihb = is_hb(h[nh],j) || ((hb->per->gemtype!=gemAD || j==0) ? FALSE : is_hb(g[nh],j));
2644 if (ihb)
2646 if (poff[m] == -1)
2647 poff[m] = j; /* Here's where the first hbond with shift p is,
2648 * relative to the start of h[0].*/
2649 if (j<poff[m])
2650 gmx_fatal(FARGS, "j<poff[m]");
2651 rHbExGem[m][j-poff[m]] += 1;
2656 /* Now, build ac. */
2657 for (m=0; m<np; m++) {
2658 if (rHbExGem[m][0]>0/* && n0+poff[n]<nn */) {
2659 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&(rHbExGem[m]),hb->time[1]-hb->time[0],
2660 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2661 for(j=0; (j<nn); j++)
2662 __ACDATA[j] += rHbExGem[m][j];
2664 } /* Building of ac. */
2665 } /* if (ISHB(...*/
2666 } /* if (hbh) */
2667 } /* hydrogen loop */
2668 } /* acceptor loop */
2669 } /* donor loop */
2671 for (m=0; m<=mMax; m++) {
2672 sfree(rHbExGem[m]);
2674 sfree(pfound);
2675 sfree(poff);
2676 sfree(rHbExGem);
2678 sfree(h);
2679 sfree(g);
2680 #ifdef HAVE_OPENMP /* =======================================\ */
2681 #pragma omp critical
2683 for (i=0; i<nn; i++)
2684 ct[i] += p_ct[i];
2686 sfree(p_ct);
2688 } /* ########## THE END OF THE ENORMOUS PARALLELIZED BLOCK ########## */
2689 sfree(dondata);
2690 #endif /* HAVE_OPENMP =======================================/ */
2692 normalizeACF(ct, NULL, nn);
2694 fprintf(stderr, "\n\nACF successfully calculated.\n");
2696 /* Use this part to fit to geminate recombination - JCP 129, 84505 (2008) */
2698 snew(ctdouble, nn);
2699 snew(timedouble, nn);
2700 snew(fittedct, nn);
2702 for (j=0;j<nn;j++){
2703 timedouble[j]=(double)(hb->time[j]);
2704 ctdouble[j]=(double)(ct[j]);
2707 /* Remove ballistic term */
2708 if (bBallistic) {
2709 if (params->ballistic/params->tDelta >= params->nExpFit*2+1)
2710 takeAwayBallistic(ctdouble, timedouble, nn, params->ballistic, params->nExpFit, params->bDt);
2711 else
2712 printf("\nNumber of data points is less than the number of parameters to fit\n."
2713 "The system is underdetermined, hence no ballistic term can be found.\n\n");
2715 if (bGemFit)
2716 fitGemRecomb(ctdouble, timedouble, &fittedct, nn, params);
2719 if (bContact)
2720 fp = xvgropen(fn, "Contact Autocorrelation","Time (ps)","C(t)",oenv);
2721 else
2722 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation","Time (ps)","C(t)",oenv);
2723 xvgr_legend(fp,asize(legGem),legGem,oenv);
2725 for(j=0; (j<nn); j++)
2726 fprintf(fp,"%10g %10g %10g %10g\n",
2727 hb->time[j]-hb->time[0],ct[j],ctdouble[j],fittedct[j]);
2728 fclose(fp);
2730 sfree(ctdouble);
2731 sfree(timedouble);
2732 sfree(fittedct);
2733 sfree(ct);
2735 break; /* case AC_GEM */
2737 case AC_LUZAR:
2738 snew(rhbex,2*n2);
2739 snew(ct,2*n2);
2740 snew(gt,2*n2);
2741 snew(ht,2*n2);
2742 snew(ght,2*n2);
2743 snew(dght,2*n2);
2745 snew(kt,nn);
2746 snew(cct,nn);
2748 for(i=0; (i<hb->d.nrd); i++) {
2749 for(k=0; (k<hb->a.nra); k++) {
2750 nhydro = 0;
2751 hbh = hb->hbmap[i][k];
2753 if (hbh) {
2754 if (bMerge || bContact) {
2755 if (ISHB(hbh->history[0])) {
2756 h[0] = hbh->h[0];
2757 g[0] = hbh->g[0];
2758 nhydro = 1;
2761 else {
2762 for(m=0; (m<hb->maxhydro); m++)
2763 if (bContact ? ISDIST(hbh->history[m]) : ISHB(hbh->history[m])) {
2764 g[nhydro] = hbh->g[m];
2765 h[nhydro] = hbh->h[m];
2766 nhydro++;
2770 nf = hbh->nframes;
2771 for(nh=0; (nh<nhydro); nh++) {
2772 int nrint = bContact ? hb->nrdist : hb->nrhb;
2773 if ((((nhbonds+1) % 10) == 0) || (nhbonds+1 == nrint))
2774 fprintf(stderr,"\rACF %d/%d",nhbonds+1,nrint);
2775 nhbonds++;
2776 for(j=0; (j<nframes); j++) {
2777 /* Changed '<' into '<=' below, just like I did in
2778 the hbm-output-loop in the gmx_hbond() block.
2779 - Erik Marklund, May 31, 2006 */
2780 if (j <= nf) {
2781 ihb = is_hb(h[nh],j);
2782 idist = is_hb(g[nh],j);
2784 else {
2785 ihb = idist = 0;
2787 rhbex[j] = ihb-aver_nhb;
2788 /* For contacts: if a second cut-off is provided, use it,
2789 * otherwise use g(t) = 1-h(t) */
2790 if (!R2 && bContact)
2791 gt[j] = 1-ihb;
2792 else
2793 gt[j] = idist*(1-ihb);
2794 ht[j] = rhbex[j];
2795 nhb += ihb;
2799 /* The autocorrelation function is normalized after summation only */
2800 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&rhbex,hb->time[1]-hb->time[0],
2801 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2803 /* Cross correlation analysis for thermodynamics */
2804 for(j=nframes; (j<n2); j++) {
2805 ht[j] = 0;
2806 gt[j] = 0;
2809 cross_corr(n2,ht,gt,dght);
2811 for(j=0; (j<nn); j++) {
2812 ct[j] += rhbex[j];
2813 ght[j] += dght[j];
2819 fprintf(stderr,"\n");
2820 sfree(h);
2821 sfree(g);
2822 normalizeACF(ct, gt, nn);
2824 /* Determine tail value for statistics */
2825 tail = 0;
2826 tail2 = 0;
2827 for(j=nn/2; (j<nn); j++) {
2828 tail += ct[j];
2829 tail2 += ct[j]*ct[j];
2831 tail /= (nn - nn/2);
2832 tail2 /= (nn - nn/2);
2833 dtail = sqrt(tail2-tail*tail);
2835 /* Check whether the ACF is long enough */
2836 if (dtail > tol) {
2837 printf("\nWARNING: Correlation function is probably not long enough\n"
2838 "because the standard deviation in the tail of C(t) > %g\n"
2839 "Tail value (average C(t) over second half of acf): %g +/- %g\n",
2840 tol,tail,dtail);
2842 for(j=0; (j<nn); j++) {
2843 cct[j] = ct[j];
2844 ct[j] = (cct[j]-tail)/(1-tail);
2846 /* Compute negative derivative k(t) = -dc(t)/dt */
2847 compute_derivative(nn,hb->time,ct,kt);
2848 for(j=0; (j<nn); j++)
2849 kt[j] = -kt[j];
2852 if (bContact)
2853 fp = xvgropen(fn, "Contact Autocorrelation","Time (ps)","C(t)",oenv);
2854 else
2855 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation","Time (ps)","C(t)",oenv);
2856 xvgr_legend(fp,asize(legLuzar),legLuzar, oenv);
2859 for(j=0; (j<nn); j++)
2860 fprintf(fp,"%10g %10g %10g %10g %10g\n",
2861 hb->time[j]-hb->time[0],ct[j],cct[j],ght[j],kt[j]);
2862 ffclose(fp);
2864 analyse_corr(nn,hb->time,ct,ght,kt,NULL,NULL,NULL,
2865 fit_start,temp,smooth_tail_start,oenv);
2867 do_view(oenv,fn,NULL);
2868 sfree(rhbex);
2869 sfree(ct);
2870 sfree(gt);
2871 sfree(ht);
2872 sfree(ght);
2873 sfree(dght);
2874 sfree(cct);
2875 sfree(kt);
2876 /* sfree(h); */
2877 /* sfree(g); */
2879 break; /* case AC_LUZAR */
2881 default:
2882 gmx_fatal(FARGS, "Unrecognized type of ACF-calulation. acType = %i.", acType);
2883 } /* switch (acType) */
2886 static void init_hbframe(t_hbdata *hb,int nframes,real t)
2888 int i,j,m;
2890 hb->time[nframes] = t;
2891 hb->nhb[nframes] = 0;
2892 hb->ndist[nframes] = 0;
2893 for (i=0; (i<max_hx); i++)
2894 hb->nhx[nframes][i]=0;
2895 /* Loop invalidated */
2896 if (hb->bHBmap && 0)
2897 for (i=0; (i<hb->d.nrd); i++)
2898 for (j=0; (j<hb->a.nra); j++)
2899 for (m=0; (m<hb->maxhydro); m++)
2900 if (hb->hbmap[i][j] && hb->hbmap[i][j]->h[m])
2901 set_hb(hb,i,m,j,nframes,HB_NO);
2902 /*set_hb(hb->hbmap[i][j]->h[m],nframes-hb->hbmap[i][j]->n0,HB_NO);*/
2905 static void analyse_donor_props(const char *fn,t_hbdata *hb,int nframes,real t,
2906 const output_env_t oenv)
2908 static FILE *fp = NULL;
2909 char *leg[] = { "Nbound", "Nfree" };
2910 int i,j,k,nbound,nb,nhtot;
2912 if (!fn)
2913 return;
2914 if (!fp) {
2915 fp = xvgropen(fn,"Donor properties","Time (ps)","Number",oenv);
2916 xvgr_legend(fp,asize(leg),leg,oenv);
2918 nbound = 0;
2919 nhtot = 0;
2920 for(i=0; (i<hb->d.nrd); i++) {
2921 for(k=0; (k<hb->d.nhydro[i]); k++) {
2922 nb = 0;
2923 nhtot ++;
2924 for(j=0; (j<hb->a.nra) && (nb == 0); j++) {
2925 if (hb->hbmap[i][j] && hb->hbmap[i][j]->h[k] &&
2926 is_hb(hb->hbmap[i][j]->h[k],nframes))
2927 nb = 1;
2929 nbound += nb;
2932 fprintf(fp,"%10.3e %6d %6d\n",t,nbound,nhtot-nbound);
2935 static void dump_hbmap(t_hbdata *hb,
2936 int nfile,t_filenm fnm[],bool bTwo,bool bInsert,
2937 bool bContact, int isize[],int *index[],char *grpnames[],
2938 t_atoms *atoms)
2940 FILE *fp,*fplog;
2941 int ddd,hhh,aaa,i,j,k,m,grp;
2942 char ds[32],hs[32],as[32];
2943 bool first;
2945 fp = opt2FILE("-hbn",nfile,fnm,"w");
2946 if (opt2bSet("-g",nfile,fnm)) {
2947 fplog = ffopen(opt2fn("-g",nfile,fnm),"w");
2948 if (bContact)
2949 fprintf(fplog,"# %10s %12s %12s\n","Donor","Hydrogen","Acceptor");
2950 else
2951 fprintf(fplog,"# %10s %12s %12s\n","Donor","Hydrogen","Acceptor");
2953 else
2954 fplog = NULL;
2955 for (grp=gr0; grp<=(bTwo?gr1:gr0); grp++) {
2956 fprintf(fp,"[ %s ]",grpnames[grp]);
2957 for (i=0; i<isize[grp]; i++) {
2958 fprintf(fp,(i%15)?" ":"\n");
2959 fprintf(fp," %4u",index[grp][i]+1);
2961 fprintf(fp,"\n");
2963 Added -contact support below.
2964 - Erik Marklund, May 29, 2006
2966 if (!bContact) {
2967 fprintf(fp,"[ donors_hydrogens_%s ]\n",grpnames[grp]);
2968 for (i=0; (i<hb->d.nrd); i++) {
2969 if (hb->d.grp[i] == grp) {
2970 for(j=0; (j<hb->d.nhydro[i]); j++)
2971 fprintf(fp," %4u %4u",hb->d.don[i]+1,
2972 hb->d.hydro[i][j]+1);
2973 fprintf(fp,"\n");
2976 first = TRUE;
2977 fprintf(fp,"[ acceptors_%s ]",grpnames[grp]);
2978 for (i=0; (i<hb->a.nra); i++) {
2979 if (hb->a.grp[i] == grp) {
2980 fprintf(fp,(i%15 && !first)?" ":"\n");
2981 fprintf(fp," %4u",hb->a.acc[i]+1);
2982 first = FALSE;
2985 fprintf(fp,"\n");
2988 if (bTwo)
2989 fprintf(fp,bContact ? "[ contacts_%s-%s ]\n" :
2990 "[ hbonds_%s-%s ]\n",grpnames[0],grpnames[1]);
2991 else
2992 fprintf(fp,bContact ? "[ contacts_%s ]" : "[ hbonds_%s ]\n",grpnames[0]);
2994 for(i=0; (i<hb->d.nrd); i++) {
2995 ddd = hb->d.don[i];
2996 for(k=0; (k<hb->a.nra); k++) {
2997 aaa = hb->a.acc[k];
2998 for(m=0; (m<hb->d.nhydro[i]); m++) {
2999 if (hb->hbmap[i][k] && ISHB(hb->hbmap[i][k]->history[m])) {
3000 sprintf(ds,"%s",mkatomname(atoms,ddd));
3001 sprintf(as,"%s",mkatomname(atoms,aaa));
3002 if (bContact) {
3003 fprintf(fp," %6u %6u\n",ddd+1,aaa+1);
3004 if (fplog)
3005 fprintf(fplog,"%12s %12s\n",ds,as);
3006 } else {
3007 hhh = hb->d.hydro[i][m];
3008 sprintf(hs,"%s",mkatomname(atoms,hhh));
3009 fprintf(fp," %6u %6u %6u\n",ddd+1,hhh+1,aaa+1);
3010 if (fplog)
3011 fprintf(fplog,"%12s %12s %12s\n",ds,hs,as);
3017 if (bInsert) {
3018 if (bTwo)
3019 fprintf(fp,"[ insert_%s->%s-%s ]",
3020 grpnames[2],grpnames[0],grpnames[1]);
3021 else
3022 fprintf(fp,"[ insert_%s->%s ]",grpnames[2],grpnames[0]);
3023 j=0;
3025 /* for(i=0; (i<hb->nrhb); i++) {
3026 if (hb->hb[i].bInsert) {
3027 fprintf(fp,(j%15)?" ":"\n");
3028 fprintf(fp,"%4d",i+1);
3029 j++;
3032 fprintf(fp,"\n");
3034 ffclose(fp);
3035 if (fplog)
3036 ffclose(fplog);
3039 #ifdef HAVE_OPENMP
3040 /* sync_hbdata() updates the parallel t_hbdata p_hb using hb as template.
3041 * It mimics add_frames() and init_frame() to some extent. */
3042 static void sync_hbdata(t_hbdata *hb, t_hbdata *p_hb,
3043 int nframes, real t)
3045 int i;
3046 if (nframes >= p_hb->max_frames)
3048 p_hb->max_frames += 4096;
3049 srenew(p_hb->nhb, p_hb->max_frames);
3050 srenew(p_hb->ndist, p_hb->max_frames);
3051 srenew(p_hb->n_bound, p_hb->max_frames);
3052 srenew(p_hb->nhx, p_hb->max_frames);
3053 if (p_hb->bDAnr)
3054 srenew(p_hb->danr, p_hb->max_frames);
3055 memset(&(p_hb->nhb[nframes]), 0, sizeof(int) * (p_hb->max_frames-nframes));
3056 memset(&(p_hb->ndist[nframes]), 0, sizeof(int) * (p_hb->max_frames-nframes));
3057 p_hb->nhb[nframes] = 0;
3058 p_hb->ndist[nframes] = 0;
3061 p_hb->nframes = nframes;
3062 /* for (i=0;) */
3063 /* { */
3064 /* p_hb->nhx[nframes][i] */
3065 /* } */
3066 memset(&(p_hb->nhx[nframes]), 0 ,sizeof(int)*max_hx); /* zero the helix count for this frame */
3068 /* hb->per will remain constant througout the frame loop,
3069 * even though the data its members point to will change,
3070 * hence no need for re-syncing. */
3072 #endif
3074 int gmx_hbond(int argc,char *argv[])
3076 const char *desc[] = {
3077 "g_hbond computes and analyzes hydrogen bonds. Hydrogen bonds are",
3078 "determined based on cutoffs for the angle Acceptor - Donor - Hydrogen",
3079 "(zero is extended) and the distance Hydrogen - Acceptor.",
3080 "OH and NH groups are regarded as donors, O is an acceptor always,",
3081 "N is an acceptor by default, but this can be switched using",
3082 "[TT]-nitacc[tt]. Dummy hydrogen atoms are assumed to be connected",
3083 "to the first preceding non-hydrogen atom.[PAR]",
3085 "You need to specify two groups for analysis, which must be either",
3086 "identical or non-overlapping. All hydrogen bonds between the two",
3087 "groups are analyzed.[PAR]",
3089 "If you set -shell, you will be asked for an additional index group",
3090 "which should contain exactly one atom. In this case, only hydrogen",
3091 "bonds between atoms within the shell distance from the one atom are",
3092 "considered.[PAR]"
3094 /* "It is also possible to analyse specific hydrogen bonds with",
3095 "[TT]-sel[tt]. This index file must contain a group of atom triplets",
3096 "Donor Hydrogen Acceptor, in the following way:[PAR]",
3098 "[TT]",
3099 "[ selected ][BR]",
3100 " 20 21 24[BR]",
3101 " 25 26 29[BR]",
3102 " 1 3 6[BR]",
3103 "[tt][BR]",
3104 "Note that the triplets need not be on separate lines.",
3105 "Each atom triplet specifies a hydrogen bond to be analyzed,",
3106 "note also that no check is made for the types of atoms.[PAR]",
3108 "[TT]-ins[tt] turns on computing solvent insertion into hydrogen bonds.",
3109 "In this case an additional group must be selected, specifying the",
3110 "solvent molecules.[PAR]",
3112 "[BB]Output:[bb][BR]",
3113 "[TT]-num[tt]: number of hydrogen bonds as a function of time.[BR]",
3114 "[TT]-ac[tt]: average over all autocorrelations of the existence",
3115 "functions (either 0 or 1) of all hydrogen bonds.[BR]",
3116 "[TT]-dist[tt]: distance distribution of all hydrogen bonds.[BR]",
3117 "[TT]-ang[tt]: angle distribution of all hydrogen bonds.[BR]",
3118 "[TT]-hx[tt]: the number of n-n+i hydrogen bonds as a function of time",
3119 "where n and n+i stand for residue numbers and i ranges from 0 to 6.",
3120 "This includes the n-n+3, n-n+4 and n-n+5 hydrogen bonds associated",
3121 "with helices in proteins.[BR]",
3122 "[TT]-hbn[tt]: all selected groups, donors, hydrogens and acceptors",
3123 "for selected groups, all hydrogen bonded atoms from all groups and",
3124 "all solvent atoms involved in insertion.[BR]",
3125 "[TT]-hbm[tt]: existence matrix for all hydrogen bonds over all",
3126 "frames, this also contains information on solvent insertion",
3127 "into hydrogen bonds. Ordering is identical to that in [TT]-hbn[tt]",
3128 "index file.[BR]",
3129 "[TT]-dan[tt]: write out the number of donors and acceptors analyzed for",
3130 "each timeframe. This is especially usefull when using [TT]-shell[tt].[BR]",
3131 "[TT]-nhbdist[tt]: compute the number of HBonds per hydrogen in order to",
3132 "compare results to Raman Spectroscopy.",
3133 "[PAR]",
3134 "Note: options [TT]-ac[tt], [TT]-life[tt], [TT]-hbn[tt] and [TT]-hbm[tt]",
3135 "require an amount of memory proportional to the total numbers of donors",
3136 "times the total number of acceptors in the selected group(s)."
3139 static real acut=30, abin=1, rcut=0.35, r2cut=0, rbin=0.005, rshell=-1;
3140 static real maxnhb=0,fit_start=1,temp=298.15,smooth_tail_start=-1, D=-1;
3141 static bool bNitAcc=TRUE,bInsert=FALSE,bDA=TRUE,bMerge=TRUE;
3142 static int nDump=0;
3143 static int nThreads = 0, nBalExp=4;
3145 static bool bContact=FALSE, bBallistic=FALSE, bBallisticDt=FALSE, bGemFit=FALSE;
3146 static real logAfterTime = 10, gemBallistic = 0.2; /* ps */
3147 static char *NNtype[] = {NULL, "none", "binary", "oneOverR3", "dipole", NULL};
3149 /* options */
3150 t_pargs pa [] = {
3151 { "-ins", FALSE, etBOOL, {&bInsert},
3152 "Analyze solvent insertion" },
3153 { "-a", FALSE, etREAL, {&acut},
3154 "Cutoff angle (degrees, Acceptor - Donor - Hydrogen)" },
3155 { "-r", FALSE, etREAL, {&rcut},
3156 "Cutoff radius (nm, X - Acceptor, see next option)" },
3157 { "-da", FALSE, etBOOL, {&bDA},
3158 "Use distance Donor-Acceptor (if TRUE) or Hydrogen-Acceptor (FALSE)" },
3159 { "-r2", FALSE, etREAL, {&r2cut},
3160 "Second cutoff radius. Mainly useful with -contact and -ac"},
3161 { "-abin", FALSE, etREAL, {&abin},
3162 "Binwidth angle distribution (degrees)" },
3163 { "-rbin", FALSE, etREAL, {&rbin},
3164 "Binwidth distance distribution (nm)" },
3165 { "-nitacc",FALSE, etBOOL, {&bNitAcc},
3166 "Regard nitrogen atoms as acceptors" },
3167 { "-contact",FALSE,etBOOL, {&bContact},
3168 "Do not look for hydrogen bonds, but merely for contacts within the cut-off distance" },
3169 { "-shell", FALSE, etREAL, {&rshell},
3170 "when > 0, only calculate hydrogen bonds within # nm shell around "
3171 "one particle" },
3172 { "-fitstart", FALSE, etREAL, {&fit_start},
3173 "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" },
3174 { "-temp", FALSE, etREAL, {&temp},
3175 "Temperature (K) for computing the Gibbs energy corresponding to HB breaking and reforming" },
3176 { "-smooth",FALSE, etREAL, {&smooth_tail_start},
3177 "If >= 0, the tail of the ACF will be smoothed by fitting it to an exponential function: y = A exp(-x/tau)" },
3178 { "-dump", FALSE, etINT, {&nDump},
3179 "Dump the first N hydrogen bond ACFs in a single xvg file for debugging" },
3180 { "-max_hb",FALSE, etREAL, {&maxnhb},
3181 "Theoretical maximum number of hydrogen bonds used for normalizing HB autocorrelation function. Can be useful in case the program estimates it wrongly" },
3182 { "-merge", FALSE, etBOOL, {&bMerge},
3183 "H-bonds between the same donor and acceptor, but with different hydrogen are treated as a single H-bond. Mainly important for the ACF." },
3184 { "-geminate", FALSE, etENUM, {gemType},
3185 "Use reversible geminate recombination for the kinetics/thermodynamics calclations. See Markovitch et al., J. Chem. Phys 129, 084505 (2008) for details."},
3186 { "-diff", FALSE, etREAL, {&D},
3187 "Dffusion coefficient to use in the rev. gem. recomb. kinetic model. If non-positive, then it will be fitted to the ACF along with ka and kd."},
3188 #ifdef HAVE_OPENMP
3189 { "-nthreads", FALSE, etINT, {&nThreads},
3190 "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)"},
3191 #endif
3192 { "-NN", FALSE, etENUM, {NNtype},
3193 "HIDDENDo a full all vs all loop and estimsate the interaction energy instead of having a binary existence function for hydrogen bonds. NOT FULLY TESTED YET! DON'T USE IT!"},
3194 { "-gemfit", FALSE, etBOOL, {&bGemFit},
3195 "With -gemainate != none: fit ka and kd to the ACF"},
3196 { "-gemlogstart", FALSE, etREAL, {&logAfterTime},
3197 "HIDDENWith -gemfit: After this time (ps) the data points fitted to will be equidistant in log-time."},
3198 { "-ballistic", FALSE, etBOOL, {&bBallistic},
3199 "Calculate and remove ultrafast \"ballistic\" component in the ACF."},
3200 { "-ballisticlen", FALSE, etREAL, {&gemBallistic},
3201 "HIDDENFitting interval for the ultrafast \"ballistic\" component in ACF."},
3202 { "-nbalexp", FALSE, etINT, {&nBalExp},
3203 "HIDDENNumber of exponentials to fit when removing the ballistic component."},
3204 { "-ballisticDt", FALSE, etBOOL, {&bBallisticDt},
3205 "HIDDENIf TRUE, finding of the fastest ballistic component will be based on the time derivative at t=0, "
3206 "while if FALSE, it will be based on the exponent alone (like in Markovitch 2008)."}
3208 const char *bugs[] = {
3209 "The option [TT]-sel[tt] that used to work on selected hbonds is out of order, and therefore not available for the time being."
3211 t_filenm fnm[] = {
3212 { efTRX, "-f", NULL, ffREAD },
3213 { efTPX, NULL, NULL, ffREAD },
3214 { efNDX, NULL, NULL, ffOPTRD },
3215 /* { efNDX, "-sel", "select", ffOPTRD },*/
3216 { efXVG, "-num", "hbnum", ffWRITE },
3217 { efLOG, "-g", "hbond", ffOPTWR },
3218 { efXVG, "-ac", "hbac", ffOPTWR },
3219 { efXVG, "-dist","hbdist", ffOPTWR },
3220 { efXVG, "-ang", "hbang", ffOPTWR },
3221 { efXVG, "-hx", "hbhelix",ffOPTWR },
3222 { efNDX, "-hbn", "hbond", ffOPTWR },
3223 { efXPM, "-hbm", "hbmap", ffOPTWR },
3224 { efXVG, "-don", "donor", ffOPTWR },
3225 { efXVG, "-dan", "danum", ffOPTWR },
3226 { efXVG, "-life","hblife", ffOPTWR },
3227 { efXVG, "-nhbdist", "nhbdist",ffOPTWR }
3230 #define NFILE asize(fnm)
3232 char hbmap [HB_NR]={ ' ', 'o', '-', '*' };
3233 char *hbdesc[HB_NR]={ "None", "Present", "Inserted", "Present & Inserted" };
3234 t_rgb hbrgb [HB_NR]={ {1,1,1},{1,0,0}, {0,0,1}, {1,0,1} };
3236 int status, trrStatus=1;
3237 t_topology top;
3238 t_inputrec ir;
3239 t_pargs *ppa;
3240 int npargs,natoms,nframes=0,shatom;
3241 int *isize;
3242 char **grpnames;
3243 atom_id **index;
3244 rvec *x,hbox;
3245 matrix box;
3246 real t,ccut,dist,ang;
3247 double max_nhb,aver_nhb,aver_dist;
3248 int h,i,j,k,l,start,end,id,ja,ogrp,nsel;
3249 int xi,yi,zi,ai;
3250 int xj,yj,zj,aj,xjj,yjj,zjj;
3251 int xk,yk,zk,ak,xkk,ykk,zkk;
3252 bool bSelected,bHBmap,bStop,bTwo,was,bBox,bTric;
3253 int *adist,*rdist;
3254 int grp,nabin,nrbin,bin,resdist,ihb;
3255 char **leg;
3256 t_hbdata *hb;
3257 FILE *fp,*fpins=NULL,*fpnhb=NULL;
3258 t_gridcell ***grid;
3259 t_ncell *icell,*jcell,*kcell;
3260 ivec ngrid;
3261 unsigned char *datable;
3262 output_env_t oenv;
3263 int gemmode, NN;
3264 PSTYPE peri;
3265 t_E E;
3266 int ii, jj, hh, actual_nThreads, threadNr;
3267 bool bGem, bNN, bParallel;
3268 t_gemParams *params;
3270 CopyRight(stdout,argv[0]);
3272 npargs = asize(pa);
3273 ppa = add_acf_pargs(&npargs,pa);
3275 parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_BE_NICE,NFILE,fnm,npargs,
3276 ppa,asize(desc),desc,asize(bugs),bugs,&oenv);
3278 /* NN-loop? If so, what estimator to use ?*/
3279 NN = 1;
3280 while (NN < NN_NR && strcasecmp(NNtype[0], NNtype[NN])!=0)
3281 NN++;
3282 if (NN == NN_NR)
3283 gmx_fatal(FARGS, "Invalid NN-loop type.");
3285 bNN = FALSE;
3286 for (i=2; bNN==FALSE && i<NN_NR; i++)
3287 bNN = bNN || NN == i;
3289 if (NN > NN_NONE && bMerge)
3290 bMerge = FALSE;
3292 /* geminate recombination? If so, which flavor? */
3293 gemmode = 1;
3294 while (gemmode < gemNR && strcasecmp(gemType[0], gemType[gemmode])!=0)
3295 gemmode++;
3296 if (gemmode == gemNR)
3297 gmx_fatal(FARGS, "Invalid recombination type.");
3299 bGem = FALSE;
3300 for (i=2; bGem==FALSE && i<gemNR; i++)
3301 bGem = bGem || gemmode == i;
3303 if (bGem) {
3304 printf("Geminate recombination: %s\n" ,gemType[gemmode]);
3305 #ifndef HAVE_LIBGSL
3306 printf("Note that some aspects of reversible geminate recombination won't work without gsl.\n");
3307 #endif
3308 if (bContact) {
3309 if (gemmode != gemDD) {
3310 printf("Turning off -contact option...\n");
3311 bContact = FALSE;
3313 } else {
3314 if (gemmode == gemDD) {
3315 printf("Turning on -contact option...\n");
3316 bContact = TRUE;
3319 if (bMerge) {
3320 if (gemmode == gemAA) {
3321 printf("Turning off -merge option...\n");
3322 bMerge = FALSE;
3324 } else {
3325 if (gemmode != gemAA) {
3326 printf("Turning on -merge option...\n");
3327 bMerge = TRUE;
3330 } else
3331 printf("No geminate recombination.\n");
3333 /* process input */
3334 bSelected = opt2bSet("-sel",NFILE,fnm);
3335 ccut = cos(acut*DEG2RAD);
3337 if (bContact) {
3338 if (bSelected)
3339 gmx_fatal(FARGS,"Can not analyze selected contacts: turn off -sel");
3340 if (bInsert)
3341 gmx_fatal(FARGS,"Can not analyze inserted contacts: turn off -ins");
3342 if (!bDA) {
3343 gmx_fatal(FARGS,"Can not analyze contact between H and A: turn off -noda");
3347 #ifndef HAVE_LIBGSL
3348 printf("NO GSL! Can't find and take away ballistic term in ACF without GSL\n.");
3349 #endif
3351 /* Initiate main data structure! */
3352 bHBmap = (opt2bSet("-ac",NFILE,fnm) ||
3353 opt2bSet("-life",NFILE,fnm) ||
3354 opt2bSet("-hbn",NFILE,fnm) ||
3355 opt2bSet("-hbm",NFILE,fnm) ||
3356 bGem);
3358 #ifdef HAVE_OPENMP
3359 printf("Compiled with OpenMP (%i)\n", _OPENMP);
3360 #endif
3362 /* if (bContact && bGem) */
3363 /* gmx_fatal(FARGS, "Can't do reversible geminate recombination with -contact yet."); */
3365 if (opt2bSet("-nhbdist",NFILE,fnm)) {
3366 char *leg[MAXHH+1] = { "0 HBs", "1 HB", "2 HBs", "3 HBs", "Total" };
3367 fpnhb = xvgropen(opt2fn("-nhbdist",NFILE,fnm),
3368 "Number of donor-H with N HBs","Time (ps)","N",oenv);
3369 xvgr_legend(fpnhb,asize(leg),leg,oenv);
3372 hb = mk_hbdata(bHBmap,opt2bSet("-dan",NFILE,fnm),bMerge || bContact, bGem, gemmode);
3374 /* get topology */
3375 read_tpx_top(ftp2fn(efTPX,NFILE,fnm),&ir,box,&natoms,NULL,NULL,NULL,&top);
3377 snew(grpnames,grNR);
3378 snew(index,grNR);
3379 snew(isize,grNR);
3380 /* Make Donor-Acceptor table */
3381 snew(datable, top.atoms.nr);
3382 gen_datable(index[0],isize[0],datable,top.atoms.nr);
3384 if (bSelected) {
3385 /* analyze selected hydrogen bonds */
3386 printf("Select group with selected atoms:\n");
3387 get_index(&(top.atoms),opt2fn("-sel",NFILE,fnm),
3388 1,&nsel,index,grpnames);
3389 if (nsel % 3)
3390 gmx_fatal(FARGS,"Number of atoms in group '%s' not a multiple of 3\n"
3391 "and therefore cannot contain triplets of "
3392 "Donor-Hydrogen-Acceptor",grpnames[0]);
3393 bTwo=FALSE;
3395 for(i=0; (i<nsel); i+=3) {
3396 int dd = index[0][i];
3397 /* int */ hh = index[0][i+1];
3398 int aa = index[0][i+2];
3399 add_dh (&hb->d,dd,hh,i,datable);
3400 add_acc(&hb->a,aa,i);
3401 /* Should this be here ? */
3402 snew(hb->d.dptr,top.atoms.nr);
3403 snew(hb->a.aptr,top.atoms.nr);
3404 add_hbond(hb,dd,aa,hh,gr0,gr0,0,FALSE,bMerge,0,bContact,peri);
3406 printf("Analyzing %d selected hydrogen bonds from '%s'\n",
3407 isize[0],grpnames[0]);
3409 else {
3410 /* analyze all hydrogen bonds: get group(s) */
3411 printf("Specify 2 groups to analyze:\n");
3412 get_index(&(top.atoms),ftp2fn_null(efNDX,NFILE,fnm),
3413 2,isize,index,grpnames);
3415 /* check if we have two identical or two non-overlapping groups */
3416 bTwo = isize[0] != isize[1];
3417 for (i=0; (i<isize[0]) && !bTwo; i++)
3418 bTwo = index[0][i] != index[1][i];
3419 if (bTwo) {
3420 printf("Checking for overlap in atoms between %s and %s\n",
3421 grpnames[0],grpnames[1]);
3422 for (i=0; i<isize[1];i++)
3423 if (ISINGRP(datable[index[1][i]]))
3424 gmx_fatal(FARGS,"Partial overlap between groups '%s' and '%s'",
3425 grpnames[0],grpnames[1]);
3427 printf("Checking for overlap in atoms between %s and %s\n",
3428 grpnames[0],grpnames[1]);
3429 for (i=0; i<isize[0]; i++)
3430 for (j=0; j<isize[1]; j++)
3431 if (index[0][i] == index[1][j])
3432 gmx_fatal(FARGS,"Partial overlap between groups '%s' and '%s'",
3433 grpnames[0],grpnames[1]);
3436 if (bTwo)
3437 printf("Calculating %s "
3438 "between %s (%d atoms) and %s (%d atoms)\n",
3439 bContact ? "contacts" : "hydrogen bonds",
3440 grpnames[0],isize[0],grpnames[1],isize[1]);
3441 else
3442 fprintf(stderr,"Calculating %s in %s (%d atoms)\n",
3443 bContact?"contacts":"hydrogen bonds",grpnames[0],isize[0]);
3445 sfree(datable);
3446 if (bInsert) {
3447 printf("Specify group for insertion analysis:\n");
3448 get_index(&(top.atoms),ftp2fn_null(efNDX,NFILE,fnm),
3449 1,&(isize[grI]),&(index[grI]),&(grpnames[grI]));
3450 printf("Checking for overlap...\n");
3451 for (i=0; i<isize[grI]; i++)
3452 for (grp=0; grp<(bTwo?2:1); grp++)
3453 for (j=0; j<isize[grp]; j++)
3454 if (index[grI][i] == index[grp][j])
3455 gmx_fatal(FARGS,"Partial overlap between groups '%s' and '%s'",
3456 grpnames[grp],grpnames[grI]);
3457 fpins=ffopen("insert.dat","w");
3458 fprintf(fpins,"%4s: %15s -> %15s (%7s) - %15s (%7s)\n",
3459 "time","insert","donor","distang","acceptor","distang");
3462 /* search donors and acceptors in groups */
3463 snew(datable, top.atoms.nr);
3464 for (i=0; (i<grNR); i++)
3465 if ( ((i==gr0) && !bSelected ) ||
3466 ((i==gr1) && bTwo ) ||
3467 ((i==grI) && bInsert ) ) {
3468 gen_datable(index[i],isize[i],datable,top.atoms.nr);
3469 if (bContact) {
3470 search_acceptors(&top,isize[i],index[i],&hb->a,i,
3471 bNitAcc,TRUE,(bTwo && (i==gr0)) || !bTwo, datable);
3472 search_donors (&top,isize[i],index[i],&hb->d,i,
3473 TRUE,(bTwo && (i==gr1)) || !bTwo, datable);
3475 else {
3476 search_acceptors(&top,isize[i],index[i],&hb->a,i,bNitAcc,FALSE,TRUE, datable);
3477 search_donors (&top,isize[i],index[i],&hb->d,i,FALSE,TRUE, datable);
3479 if (bTwo)
3480 clear_datable_grp(datable,top.atoms.nr);
3482 sfree(datable);
3483 printf("Found %d donors and %d acceptors\n",hb->d.nrd,hb->a.nra);
3484 /*if (bSelected)
3485 snew(donors[gr0D], dons[gr0D].nrd);*/
3487 if (bHBmap) {
3488 printf("Making hbmap structure...");
3489 /* Generate hbond data structure */
3490 mk_hbmap(hb,bTwo,bInsert);
3491 printf("done.\n");
3494 #ifdef HAVE_NN_LOOPS
3495 if (bNN)
3496 mk_hbEmap(hb, 0);
3497 #endif
3499 if (bGem) {
3500 printf("Making per structure...");
3501 /* Generate hbond data structure */
3502 mk_per(hb);
3503 printf("done.\n");
3506 /* check input */
3507 bStop=FALSE;
3508 if (hb->d.nrd + hb->a.nra == 0) {
3509 printf("No Donors or Acceptors found\n");
3510 bStop=TRUE;
3512 if (!bStop) {
3513 if (hb->d.nrd == 0) {
3514 printf("No Donors found\n");
3515 bStop=TRUE;
3517 if (hb->a.nra == 0) {
3518 printf("No Acceptors found\n");
3519 bStop=TRUE;
3522 if (bStop)
3523 gmx_fatal(FARGS,"Nothing to be done");
3525 shatom=0;
3526 if (rshell > 0) {
3527 int shisz;
3528 atom_id *shidx;
3529 char *shgrpnm;
3530 /* get index group with atom for shell */
3531 do {
3532 printf("Select atom for shell (1 atom):\n");
3533 get_index(&(top.atoms),ftp2fn_null(efNDX,NFILE,fnm),
3534 1,&shisz,&shidx,&shgrpnm);
3535 if (shisz != 1)
3536 printf("group contains %d atoms, should be 1 (one)\n",shisz);
3537 } while(shisz != 1);
3538 shatom = shidx[0];
3539 printf("Will calculate hydrogen bonds within a shell "
3540 "of %g nm around atom %i\n",rshell,shatom+1);
3543 /* Analyze trajectory */
3544 natoms=read_first_x(oenv,&status,ftp2fn(efTRX,NFILE,fnm),&t,&x,box);
3545 if ( natoms > top.atoms.nr )
3546 gmx_fatal(FARGS,"Topology (%d atoms) does not match trajectory (%d atoms)",
3547 top.atoms.nr,natoms);
3549 bBox = ir.ePBC!=epbcNONE;
3550 grid = init_grid(bBox, box, (rcut>r2cut)?rcut:r2cut, ngrid);
3551 nabin = acut/abin;
3552 nrbin = rcut/rbin;
3553 snew(adist,nabin+1);
3554 snew(rdist,nrbin+1);
3556 if (bGem && !bBox)
3557 gmx_fatal(FARGS, "Can't do geminate recombination without periodic box.");
3559 bParallel = FALSE;
3561 #ifndef HAVE_OPENMP
3562 #define __ADIST adist
3563 #define __RDIST rdist
3564 #define __HBDATA hb
3565 #else /* HAVE_OPENMP ================================================== \
3566 * Set up the OpenMP stuff, |
3567 * like the number of threads and such |
3568 * Also start the parallel loop. |
3570 #define __ADIST p_adist[threadNr]
3571 #define __RDIST p_rdist[threadNr]
3572 #define __HBDATA p_hb[threadNr]
3574 bParallel = !bSelected && !bInsert;
3576 if (bParallel)
3578 #if (_OPENMP > 200805)
3579 actual_nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_thread_limit());
3580 #else
3581 actual_nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_num_procs());
3582 #endif
3583 omp_set_num_threads(actual_nThreads);
3584 printf("Frame loop parallelized with OpenMP using %i threads.\n", actual_nThreads);
3585 fflush(stdout);
3587 else
3589 actual_nThreads = 1;
3592 t_hbdata **p_hb; /* one per thread, then merge after the frame loop */
3593 int **p_adist, **p_rdist; /* a histogram for each thread. */
3594 snew(p_hb, actual_nThreads);
3595 snew(p_adist, actual_nThreads);
3596 snew(p_rdist, actual_nThreads);
3597 for (i=0; i<actual_nThreads; i++)
3599 snew(p_hb[i], 1);
3600 snew(p_adist[i], nabin+1);
3601 snew(p_rdist[i], nrbin+1);
3603 p_hb[i]->max_frames = 0;
3604 p_hb[i]->nhb = NULL;
3605 p_hb[i]->ndist = NULL;
3606 p_hb[i]->n_bound = NULL;
3607 p_hb[i]->time = NULL;
3608 p_hb[i]->nhx = NULL;
3610 p_hb[i]->bHBmap = hb->bHBmap;
3611 p_hb[i]->bDAnr = hb->bDAnr;
3612 p_hb[i]->bGem = hb->bGem;
3613 p_hb[i]->wordlen = hb->wordlen;
3614 p_hb[i]->nframes = hb->nframes;
3615 p_hb[i]->maxhydro = hb->maxhydro;
3616 p_hb[i]->danr = hb->danr;
3617 p_hb[i]->d = hb->d;
3618 p_hb[i]->a = hb->a;
3619 p_hb[i]->hbmap = hb->hbmap;
3620 p_hb[i]->time = hb->time; /* This may need re-syncing at every frame. */
3621 p_hb[i]->per = hb->per;
3623 #ifdef HAVE_NN_LOOPS
3624 p_hb[i]->hbE = hb->hbE;
3625 #endif
3627 p_hb[i]->nrhb = 0;
3628 p_hb[i]->nrdist = 0;
3631 /* Make a thread pool here,
3632 * instead of forking anew at every frame. */
3634 #pragma omp parallel \
3635 private(i, j, h, ii, jj, hh, E, \
3636 xi, yi, zi, xj, yj, zj, threadNr, \
3637 dist, ang, peri, icell, jcell, \
3638 grp, ogrp, ai, aj, xjj, yjj, zjj, \
3639 xk, yk, zk, ihb, id, resdist, \
3640 xkk, ykk, zkk, kcell, ak, k, bTric) \
3641 default(none) \
3642 shared(hb, p_hb, p_adist, p_rdist, actual_nThreads, \
3643 x, bBox, box, hbox, rcut, r2cut, rshell, \
3644 shatom, ngrid, grid, nframes, t, \
3645 bParallel, bNN, index, bMerge, bContact, \
3646 bTwo, bDA,ccut, abin, rbin, top, \
3647 bInsert, bSelected, bDebug, stderr, nsel, \
3648 bGem, oenv, fnm, fpnhb, trrStatus, natoms, \
3649 status, nabin, nrbin, adist, rdist, debug)
3650 { /* Start of parallel region */
3651 threadNr = omp_get_thread_num();
3652 #endif /* HAVE_OPENMP ================================================= */
3655 bTric = bBox && TRICLINIC(box);
3657 #ifdef HAVE_OPENMP
3658 sync_hbdata(hb, p_hb[threadNr], nframes, t);
3659 #pragma omp single
3660 #endif
3662 build_grid(hb,x,x[shatom], bBox,box,hbox, (rcut>r2cut)?rcut:r2cut,
3663 rshell, ngrid,grid);
3664 reset_nhbonds(&(hb->d));
3666 if (debug && bDebug)
3667 dump_grid(debug, ngrid, grid);
3669 add_frames(hb,nframes);
3670 init_hbframe(hb,nframes,t);
3672 if (hb->bDAnr)
3673 count_da_grid(ngrid, grid, hb->danr[nframes]);
3674 } /* omp single */
3676 #ifdef HAVE_OPENMP
3677 p_hb[threadNr]->time = hb->time; /* This pointer may have changed. */
3678 #endif
3679 if (bNN)
3681 #ifdef HAVE_NN_LOOPS /* Unlock this feature when testing */
3682 /* Loop over all atom pairs and estimate interaction energy */
3683 #ifdef HAVE_OPENMP /* ------- */
3684 #pragma omp single
3685 #endif /* HAVE_OPENMP ------- */
3687 addFramesNN(hb, nframes);
3689 #ifdef HAVE_OPENMP /* ---------------- */
3690 #pragma omp barrier
3691 #pragma omp for schedule(dynamic)
3692 #endif /* HAVE_OPENMP ---------------- */
3693 for (i=0; i<hb->d.nrd; i++)
3695 for(j=0;j<hb->a.nra; j++)
3697 for (h=0;
3698 h < (bContact ? 1 : hb->d.nhydro[i]);
3699 h++)
3701 if (i==hb->d.nrd || j==hb->a.nra)
3702 gmx_fatal(FARGS, "out of bounds");
3704 /* Get the real atom ids */
3705 ii = hb->d.don[i];
3706 jj = hb->a.acc[j];
3707 hh = hb->d.hydro[i][h];
3709 /* Estimate the energy from the geometry */
3710 E = calcHbEnergy(ii, jj, hh, x, NN, box, hbox, &(hb->d));
3711 /* Store the energy */
3712 storeHbEnergy(hb, i, j, h, E, nframes);
3716 #endif /* HAVE_NN_LOOPS */
3717 } /* if (bNN)*/
3718 else
3720 if (bSelected)
3722 #ifdef HAVE_OPENMP
3723 #pragma omp single
3724 #endif
3726 /* Do not parallelize this just yet. */
3727 /* int ii; */
3728 for(ii=0; (ii<nsel); ii++) {
3729 int dd = index[0][i];
3730 /* int */ hh = index[0][i+1];
3731 int aa = index[0][i+2];
3732 ihb = is_hbond(hb,ii,ii,dd,aa,rcut,r2cut,ccut,x,bBox,box,
3733 hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
3735 if (ihb) {
3736 /* add to index if not already there */
3737 /* Add a hbond */
3738 add_hbond(hb,dd,aa,hh,ii,ii,nframes,FALSE,bMerge,ihb,bContact,peri);
3741 } /* omp single */
3742 } /* if (bSelected) */
3743 else
3745 #ifdef HAVE_OPENMP
3746 #pragma omp single
3748 #endif
3749 if (bGem)
3750 calcBoxProjection(box, hb->per->P);
3752 /* loop over all gridcells (xi,yi,zi) */
3753 /* Removed confusing macro, DvdS 27/12/98 */
3754 #ifdef HAVE_OPENMP
3756 /* The outer grid loop will have to do for now. */
3757 #pragma omp for schedule(dynamic)
3758 #endif
3759 for(xi=0; (xi<ngrid[XX]); xi++)
3760 for(yi=0; (yi<ngrid[YY]); yi++)
3761 for(zi=0; (zi<ngrid[ZZ]); zi++) {
3763 /* loop over donor groups gr0 (always) and gr1 (if necessary) */
3764 for (grp=gr0; (grp <= (bTwo?gr1:gr0)); grp++) {
3765 icell=&(grid[zi][yi][xi].d[grp]);
3767 if (bTwo)
3768 ogrp = 1-grp;
3769 else
3770 ogrp = grp;
3772 /* loop over all hydrogen atoms from group (grp)
3773 * in this gridcell (icell)
3775 for (ai=0; (ai<icell->nr); ai++) {
3776 i = icell->atoms[ai];
3778 /* loop over all adjacent gridcells (xj,yj,zj) */
3779 /* This is a macro!!! */
3780 LOOPGRIDINNER(xj,yj,zj,xjj,yjj,zjj,xi,yi,zi,ngrid,bTric) {
3781 jcell=&(grid[zj][yj][xj].a[ogrp]);
3782 /* loop over acceptor atoms from other group (ogrp)
3783 * in this adjacent gridcell (jcell)
3785 for (aj=0; (aj<jcell->nr); aj++) {
3786 j = jcell->atoms[aj];
3788 /* check if this once was a h-bond */
3789 peri = -1;
3790 ihb = is_hbond(__HBDATA,grp,ogrp,i,j,rcut,r2cut,ccut,x,bBox,box,
3791 hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
3793 if (ihb) {
3794 /* add to index if not already there */
3795 /* Add a hbond */
3796 add_hbond(__HBDATA,i,j,h,grp,ogrp,nframes,FALSE,bMerge,ihb,bContact,peri);
3798 /* make angle and distance distributions */
3799 if (ihb == hbHB && !bContact) {
3800 if (dist>rcut)
3801 gmx_fatal(FARGS,"distance is higher than what is allowed for an hbond: %f",dist);
3802 ang*=RAD2DEG;
3803 __ADIST[(int)( ang/abin)]++;
3804 __RDIST[(int)(dist/rbin)]++;
3805 if (!bTwo) {
3806 int id,ia;
3807 if ((id = donor_index(&hb->d,grp,i)) == NOTSET)
3808 gmx_fatal(FARGS,"Invalid donor %d",i);
3809 if ((ia = acceptor_index(&hb->a,ogrp,j)) == NOTSET)
3810 gmx_fatal(FARGS,"Invalid acceptor %d",j);
3811 resdist=abs(top.atoms.atom[i].resind-
3812 top.atoms.atom[j].resind);
3813 if (resdist >= max_hx)
3814 resdist = max_hx-1;
3815 __HBDATA->nhx[nframes][resdist]++;
3819 if (bInsert && bSelected && MASTER_THREAD_ONLY(threadNr)) {
3820 /* this has been a h-bond, or we are analyzing
3821 selected bonds: check for inserted */
3822 bool ins_d, ins_a;
3823 real ins_d_dist, ins_d_ang, ins_a_dist, ins_a_ang;
3824 int ins_d_k=0,ins_a_k=0;
3826 ins_d=ins_a=FALSE;
3827 ins_d_dist=ins_d_ang=ins_a_dist=ins_a_ang=1e6;
3829 /* loop over gridcells adjacent to i (xk,yk,zk) */
3830 LOOPGRIDINNER(xk,yk,zk,xkk,ykk,zkk,xi,yi,zi,ngrid,bTric){
3831 kcell=&(grid[zk][yk][xk].a[grI]);
3832 /* loop over acceptor atoms from ins group
3833 in this adjacent gridcell (kcell) */
3834 for (ak=0; (ak<kcell->nr); ak++) {
3835 k=kcell->atoms[ak];
3836 ihb = is_hbond(hb,grp,grI,i,k,rcut,r2cut,ccut,x,
3837 bBox,box,hbox,&dist,&ang,bDA,&h,
3838 bContact,bMerge,&peri);
3839 if (ihb == hbHB) {
3840 if (dist < ins_d_dist) {
3841 ins_d=TRUE;
3842 ins_d_dist=dist;
3843 ins_d_ang =ang ;
3844 ins_d_k =k ;
3849 ENDLOOPGRIDINNER;
3850 /* loop over gridcells adjacent to j (xk,yk,zk) */
3851 LOOPGRIDINNER(xk,yk,zk,xkk,ykk,zkk,xj,yj,zj,ngrid,bTric){
3852 kcell=&grid[zk][yk][xk].d[grI];
3853 /* loop over hydrogen atoms from ins group
3854 in this adjacent gridcell (kcell) */
3855 for (ak=0; ak<kcell->nr; ak++) {
3856 k = kcell->atoms[ak];
3857 ihb = is_hbond(hb,grI,ogrp,k,j,rcut,r2cut,ccut,x,
3858 bBox,box,hbox,&dist,&ang,bDA,&h,
3859 bContact,bMerge,&peri);
3860 if (ihb == hbHB) {
3861 if (dist<ins_a_dist) {
3862 ins_a=TRUE;
3863 ins_a_dist=dist;
3864 ins_a_ang =ang ;
3865 ins_a_k =k ;
3870 ENDLOOPGRIDINNER;
3873 ihb = is_hbond(hb,grI,grI,ins_d_k,ins_a_k,rcut,r2cut,ccut,x,
3874 bBox,box,hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
3875 if (ins_d && ins_a && ihb) {
3876 /* add to hbond index if not already there */
3877 add_hbond(hb,ins_d_k,ins_a_k,h,grI,ogrp,
3878 nframes,TRUE,bMerge,ihb,bContact,peri);
3880 /* print insertion info to file */
3881 /*fprintf(fpins,
3882 "%4g: %4u:%3.3s%4d%3.3s -> "
3883 "%4u:%3.3s%4d%3.3s (%4.2f,%2.0f) - "
3884 "%4u:%3.3s%4d%3.3s (%4.2f,%2.0f)\n",t,
3885 a[grIA][ins_d_k]+1,
3886 *top.atoms.resname[top.atoms.atom[a[grIA][ins_d_k]].resnr],
3887 top.atoms.atom[a[grIA][ins_d_k]].resnr+1,
3888 *top.atoms.atomname[a[grIA][ins_d_k]],
3889 a[grp+grD][i]+1,
3890 *top.atoms.resname[top.atoms.atom[a[grp+grD][i]].resnr],
3891 top.atoms.atom[a[grp+grD][i]].resnr+1,
3892 *top.atoms.atomname[a[grp+grD][i]],
3893 ins_d_dist,ins_d_ang*RAD2DEG,
3894 a[ogrp+grA][j]+1,
3895 *top.atoms.resname[top.atoms.atom[a[ogrp+grA][j]].resnr],
3896 top.atoms.atom[a[ogrp+grA][j]].resnr+1,
3897 *top.atoms.atomname[a[ogrp+grA][j]],
3898 ins_a_dist,ins_a_ang*RAD2DEG);*/
3901 } /* if (bInsert && bSelected), omp single */
3903 } /* for aj */
3905 ENDLOOPGRIDINNER;
3906 } /* for ai */
3907 } /* for grp */
3908 } /* for xi,yi,zi */
3909 } /* if (bSelected) {...} else */
3911 #ifdef HAVE_OPENMP /* ---------------------------- */
3912 /* Better wait for all threads to finnish using x[] before updating it. */
3913 k = nframes; /* */
3914 #pragma omp barrier /* */
3915 #pragma omp critical /* */
3916 { /* */
3917 /* Sum up histograms and counts from p_hb[] into hb */
3918 { /* */
3919 hb->nhb[k] += p_hb[threadNr]->nhb[k];
3920 hb->ndist[k] += p_hb[threadNr]->ndist[k];
3921 for (j=0; j<max_hx; j++) /**/
3922 hb->nhx[k][j] += p_hb[threadNr]->nhx[k][j];
3923 } /* */
3924 } /* */
3925 /* */
3926 /* Here are a handful of single constructs
3927 * to share the workload a bit. The most
3928 * important one is of course the last one,
3929 * where there's a potential bottleneck in form
3930 * of slow I/O. */
3931 #pragma omp single /* ++++++++++++++++, */
3932 #endif /* HAVE_OPENMP ----------------+------------*/
3933 { /* + */
3934 analyse_donor_props(opt2fn_null("-don",NFILE,fnm),hb,k,t,oenv);
3935 } /* + */
3936 #ifdef HAVE_OPENMP /* + */
3937 #pragma omp single /* +++ +++ */
3938 #endif /* + */
3939 { /* + */
3940 if (fpnhb) /* + */
3941 do_nhb_dist(fpnhb,hb,t);
3942 } /* + */
3943 } /* if (bNN) {...} else + */
3944 #ifdef HAVE_OPENMP /* + */
3945 #pragma omp single /* +++ +++ */
3946 #endif /* + */
3947 { /* + */
3948 trrStatus = (read_next_x(oenv,status,&t,natoms,x,box));
3949 nframes++; /* + */
3950 } /* + */
3951 #ifdef HAVE_OPENMP /* ++++++++++++++++´ */
3952 #pragma omp barrier
3953 #endif
3954 } while (trrStatus);
3956 #ifdef HAVE_OPENMP
3957 #pragma omp critical
3959 hb->nrhb += p_hb[threadNr]->nrhb;
3960 hb->nrdist += p_hb[threadNr]->nrdist;
3962 /* Free parallel datastructures */
3963 sfree(p_hb[threadNr]->nhb);
3964 sfree(p_hb[threadNr]->ndist);
3965 sfree(p_hb[threadNr]->nhx);
3967 #pragma omp for
3968 for (i=0; i<nabin; i++)
3969 for (j=0; j<actual_nThreads; j++)
3971 adist[i] += p_adist[j][i];
3972 #pragma omp for
3973 for (i=0; i<=nrbin; i++)
3974 for (j=0; j<actual_nThreads; j++)
3975 rdist[i] += p_rdist[j][i];
3977 sfree(p_adist[threadNr]);
3978 sfree(p_rdist[threadNr]);
3979 } /* End of parallel region */
3980 sfree(p_adist);
3981 sfree(p_rdist);
3982 #endif
3985 free_grid(ngrid,&grid);
3987 close_trj(status);
3988 if (bInsert)
3989 ffclose(fpins);
3990 if (fpnhb)
3991 ffclose(fpnhb);
3993 /* Compute maximum possible number of different hbonds */
3994 if (maxnhb > 0)
3995 max_nhb = maxnhb;
3996 else {
3997 max_nhb = 0.5*(hb->d.nrd*hb->a.nra);
3999 /* Added support for -contact below.
4000 * - Erik Marklund, May 29-31, 2006 */
4001 /* Changed contact code.
4002 * - Erik Marklund, June 29, 2006 */
4003 if (bHBmap && !bNN) {
4004 if (hb->nrhb==0) {
4005 printf("No %s found!!\n", bContact ? "contacts" : "hydrogen bonds");
4006 } else {
4007 printf("Found %d different %s in trajectory\n"
4008 "Found %d different atom-pairs within %s distance\n",
4009 hb->nrhb, bContact?"contacts":"hydrogen bonds",
4010 hb->nrdist,(r2cut>0)?"second cut-off":"hydrogen bonding");
4012 /*Control the pHist.*/
4014 if (bMerge)
4015 merge_hb(hb,bTwo,bContact);
4017 if (opt2bSet("-hbn",NFILE,fnm))
4018 dump_hbmap(hb,NFILE,fnm,bTwo,bInsert,bContact,isize,index,grpnames,&top.atoms);
4020 /* Moved the call to merge_hb() to a line BEFORE dump_hbmap
4021 * to make the -hbn and -hmb output match eachother.
4022 * - Erik Marklund, May 30, 2006 */
4025 /* Print out number of hbonds and distances */
4026 aver_nhb = 0;
4027 aver_dist = 0;
4028 fp = xvgropen(opt2fn("-num",NFILE,fnm),bContact ? "Contacts" :
4029 "Hydrogen Bonds","Time","Number",oenv);
4030 snew(leg,2);
4031 snew(leg[0],STRLEN);
4032 snew(leg[1],STRLEN);
4033 sprintf(leg[0],"%s",bContact?"Contacts":"Hydrogen bonds");
4034 sprintf(leg[1],"Pairs within %g nm",(r2cut>0)?r2cut:rcut);
4035 xvgr_legend(fp,2,leg,oenv);
4036 sfree(leg[1]);
4037 sfree(leg[0]);
4038 sfree(leg);
4039 for(i=0; (i<nframes); i++) {
4040 fprintf(fp,"%10g %10d %10d\n",hb->time[i],hb->nhb[i],hb->ndist[i]);
4041 aver_nhb += hb->nhb[i];
4042 aver_dist += hb->ndist[i];
4044 ffclose(fp);
4045 aver_nhb /= nframes;
4046 aver_dist /= nframes;
4047 /* Print HB distance distribution */
4048 if (opt2bSet("-dist",NFILE,fnm)) {
4049 long sum;
4051 sum=0;
4052 for(i=0; i<nrbin; i++)
4053 sum+=rdist[i];
4055 fp = xvgropen(opt2fn("-dist",NFILE,fnm),
4056 "Hydrogen Bond Distribution",
4057 "Hydrogen - Acceptor Distance (nm)","",oenv);
4058 for(i=0; i<nrbin; i++)
4059 fprintf(fp,"%10g %10g\n",(i+0.5)*rbin,rdist[i]/(rbin*(real)sum));
4060 ffclose(fp);
4063 /* Print HB angle distribution */
4064 if (opt2bSet("-ang",NFILE,fnm)) {
4065 long sum;
4067 sum=0;
4068 for(i=0; i<nabin; i++)
4069 sum+=adist[i];
4071 fp = xvgropen(opt2fn("-ang",NFILE,fnm),
4072 "Hydrogen Bond Distribution",
4073 "Donor - Hydrogen - Acceptor Angle (\\SO\\N)","",oenv);
4074 for(i=0; i<nabin; i++)
4075 fprintf(fp,"%10g %10g\n",(i+0.5)*abin,adist[i]/(abin*(real)sum));
4076 ffclose(fp);
4079 /* Print HB in alpha-helix */
4080 if (opt2bSet("-hx",NFILE,fnm)) {
4081 fp = xvgropen(opt2fn("-hx",NFILE,fnm),
4082 "Hydrogen Bonds","Time(ps)","Count",oenv);
4083 xvgr_legend(fp,NRHXTYPES,hxtypenames,oenv);
4084 for(i=0; i<nframes; i++) {
4085 fprintf(fp,"%10g",hb->time[i]);
4086 for (j=0; j<max_hx; j++)
4087 fprintf(fp," %6d",hb->nhx[i][j]);
4088 fprintf(fp,"\n");
4090 ffclose(fp);
4092 if (!bNN)
4093 printf("Average number of %s per timeframe %.3f out of %g possible\n",
4094 bContact ? "contacts" : "hbonds",
4095 bContact ? aver_dist : aver_nhb, max_nhb);
4097 /* Do Autocorrelation etc. */
4098 if (hb->bHBmap) {
4100 Added support for -contact in ac and hbm calculations below.
4101 - Erik Marklund, May 29, 2006
4103 ivec itmp;
4104 rvec rtmp;
4105 if (opt2bSet("-ac",NFILE,fnm) || opt2bSet("-life",NFILE,fnm))
4106 please_cite(stdout,"Spoel2006b");
4107 if (opt2bSet("-ac",NFILE,fnm)) {
4108 if (bGem || bNN) {
4109 params = init_gemParams(rcut, D, hb->time, logAfterTime, hb->nframes/2, gemBallistic, nBalExp, bBallisticDt);
4110 if (params == NULL)
4111 gmx_fatal(FARGS, "Could not initiate t_gemParams params.");
4113 char *gemstring=NULL;
4114 gemstring = strdup(gemType[hb->per->gemtype]);
4115 do_hbac(opt2fn("-ac",NFILE,fnm),hb,aver_nhb/max_nhb,aver_dist,nDump,
4116 bMerge,bContact,fit_start,temp,r2cut>0,smooth_tail_start,oenv,
4117 params, gemstring, nThreads, NN, bBallistic, bGemFit);
4119 if (opt2bSet("-life",NFILE,fnm))
4120 do_hblife(opt2fn("-life",NFILE,fnm),hb,bMerge,bContact,oenv);
4121 if (opt2bSet("-hbm",NFILE,fnm)) {
4122 t_matrix mat;
4123 int id,ia,hh,x,y;
4125 mat.nx=nframes;
4126 mat.ny=(bContact ? hb->nrdist : hb->nrhb);
4128 snew(mat.matrix,mat.nx);
4129 for(x=0; (x<mat.nx); x++)
4130 snew(mat.matrix[x],mat.ny);
4131 y=0;
4132 for(id=0; (id<hb->d.nrd); id++)
4133 for(ia=0; (ia<hb->a.nra); ia++) {
4134 for(hh=0; (hh<hb->maxhydro); hh++) {
4135 if (hb->hbmap[id][ia]) {
4136 if (ISHB(hb->hbmap[id][ia]->history[hh])) {
4137 /* Changed '<' into '<=' in the for-statement below.
4138 * It fixed the previously undiscovered bug that caused
4139 * the last occurance of an hbond/contact to not be
4140 * set in mat.matrix. Have a look at any old -hbm-output
4141 * and you will notice that the last column is allways empty.
4142 * - Erik Marklund May 30, 2006
4144 for(x=0; (x<=hb->hbmap[id][ia]->nframes); x++) {
4145 int nn0 = hb->hbmap[id][ia]->n0;
4146 range_check(y,0,mat.ny);
4147 mat.matrix[x+nn0][y] = is_hb(hb->hbmap[id][ia]->h[hh],x);
4149 y++;
4154 mat.axis_x=hb->time;
4155 snew(mat.axis_y,mat.ny);
4156 for(j=0; j<mat.ny; j++)
4157 mat.axis_y[j]=j;
4158 sprintf(mat.title,bContact ? "Contact Existence Map":
4159 "Hydrogen Bond Existence Map");
4160 sprintf(mat.legend,bContact ? "Contacts" : "Hydrogen Bonds");
4161 sprintf(mat.label_x,"Time (ps)");
4162 sprintf(mat.label_y, bContact ? "Contact Index" : "Hydrogen Bond Index");
4163 mat.bDiscrete=TRUE;
4164 if (bInsert)
4165 mat.nmap=HB_NR;
4166 else
4167 mat.nmap=2;
4168 snew(mat.map,mat.nmap);
4169 for(i=0; i<mat.nmap; i++) {
4170 mat.map[i].code.c1=hbmap[i];
4171 mat.map[i].desc=hbdesc[i];
4172 mat.map[i].rgb=hbrgb[i];
4174 fp = opt2FILE("-hbm",NFILE,fnm,"w");
4175 write_xpm_m(fp, mat);
4176 ffclose(fp);
4177 for(x=0; x<mat.nx; x++)
4178 sfree(mat.matrix[x]);
4179 sfree(mat.axis_y);
4180 sfree(mat.matrix);
4181 sfree(mat.map);
4185 if (bGem) {
4186 fprintf(stderr, "There were %i periodic shifts\n", hb->per->nper);
4187 fprintf(stderr, "Freeing pHist for all donors...\n");
4188 for (i=0; i<hb->d.nrd; i++) {
4189 fprintf(stderr, "\r%i",i);
4190 if (hb->per->pHist[i] != NULL) {
4191 for (j=0; j<hb->a.nra; j++)
4192 clearPshift(&(hb->per->pHist[i][j]));
4193 sfree(hb->per->pHist[i]);
4196 sfree(hb->per->pHist);
4197 sfree(hb->per->p2i);
4198 sfree(hb->per);
4199 fprintf(stderr, "...done.\n");
4202 #ifdef HAVE_NN_LOOPS
4203 if (bNN)
4204 free_hbEmap(hb);
4205 #endif
4207 if (hb->bDAnr) {
4208 int i,j,nleg;
4209 char **legnames;
4210 char buf[STRLEN];
4212 #define USE_THIS_GROUP(j) ( (j == gr0) || (bTwo && (j == gr1)) || (bInsert && (j == grI)) )
4214 fp = xvgropen(opt2fn("-dan",NFILE,fnm),
4215 "Donors and Acceptors","Time(ps)","Count",oenv);
4216 nleg = (bTwo?2:1)*2 + (bInsert?2:0);
4217 snew(legnames,nleg);
4218 i=0;
4219 for(j=0; j<grNR; j++)
4220 if ( USE_THIS_GROUP(j) ) {
4221 sprintf(buf,"Donors %s",grpnames[j]);
4222 legnames[i++] = strdup(buf);
4223 sprintf(buf,"Acceptors %s",grpnames[j]);
4224 legnames[i++] = strdup(buf);
4226 if (i != nleg)
4227 gmx_incons("number of legend entries");
4228 xvgr_legend(fp,nleg,legnames,oenv);
4229 for(i=0; i<nframes; i++) {
4230 fprintf(fp,"%10g",hb->time[i]);
4231 for (j=0; (j<grNR); j++)
4232 if ( USE_THIS_GROUP(j) )
4233 fprintf(fp," %6d",hb->danr[i][j]);
4234 fprintf(fp,"\n");
4236 ffclose(fp);
4239 thanx(stdout);
4241 return 0;