Fixed make_edi.c
[gromacs/rigid-bodies.git] / src / tools / gmx_hbond.c
blob85b1b50129d159b2f9265b34d04d1eaf367421fb
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 clearPshift(t_pShift *pShift)
223 if (pShift->len > 0) {
224 sfree(pShift->p);
225 sfree(pShift->frame);
226 pShift->len = 0;
230 static void calcBoxProjection(matrix B, matrix P)
232 const int vp[] = {XX,YY,ZZ};
233 int i,j;
234 int m,n;
235 matrix M, N, U;
237 for (i=0; i<3; i++){ m = vp[i];
238 for (j=0; j<3; j++){ n = vp[j];
239 U[m][n] = i==j ? 1:0;
242 m_inv(B,M);
243 for (i=0; i<3; i++){ m = vp[i];
244 mvmul(M, U[m], P[m]);
246 transpose(P,N);
249 static void calcBoxDistance(matrix P, rvec d, ivec ibd){
250 /* returns integer distance in box coordinates.
251 * P is the projection matrix from cartesian coordinates
252 * obtained with calcBoxProjection(). */
253 int i;
254 rvec bd;
255 mvmul(P, d, bd);
256 /* extend it by 0.5 in all directions since (int) rounds toward 0.*/
257 for (i=0;i<3;i++)
258 bd[i]=bd[i] + (bd[i]<0 ? -0.5 : 0.5);
259 ibd[XX] = (int)bd[XX];
260 ibd[YY] = (int)bd[YY];
261 ibd[ZZ] = (int)bd[ZZ];
264 /* Changed argument 'bMerge' into 'oneHB' below,
265 * since -contact should cause maxhydro to be 1,
266 * not just -merge.
267 * - Erik Marklund May 29, 2006
270 static PSTYPE periodicIndex(ivec r, t_gemPeriod *per, bool daSwap) {
271 /* Try to merge hbonds on the fly. That means that if the
272 * acceptor and donor are mergable, then:
273 * 1) store the hb-info so that acceptor id > donor id,
274 * 2) add the periodic shift in pairs, so that [-x,-y,-z] is
275 * stored in per.p2i[] whenever acceptor id < donor id.
276 * Note that [0,0,0] should already be the first element of per.p2i
277 * by the time this function is called. */
279 /* daSwap is TRUE if the donor and acceptor were swapped.
280 * If so, then the negative vector should be used. */
281 PSTYPE i;
283 if (per->p2i == NULL || per->nper == 0)
284 gmx_fatal(FARGS, "'per' not initialized properly.");
285 for (i=0; i<per->nper; i++) {
286 if (r[XX] == per->p2i[i][XX] &&
287 r[YY] == per->p2i[i][YY] &&
288 r[ZZ] == per->p2i[i][ZZ])
289 return i;
291 /* Not found apparently. Add it to the list! */
292 /* printf("New shift found: %i,%i,%i\n",r[XX],r[YY],r[ZZ]); */
294 /* Unfortunately this needs to be critical it seems. */
295 #ifdef HAVE_OPENMP
296 #pragma omp critical
297 #endif
299 if (!per->p2i) {
300 fprintf(stderr, "p2i not initialized. This shouldn't happen!\n");
301 snew(per->p2i, 1);
303 else
304 srenew(per->p2i, per->nper+2);
305 copy_ivec(r, per->p2i[per->nper]);
306 (per->nper)++;
308 /* Add the mirror too. It's rather likely that it'll be needed. */
309 per->p2i[per->nper][XX] = -r[XX];
310 per->p2i[per->nper][YY] = -r[YY];
311 per->p2i[per->nper][ZZ] = -r[ZZ];
312 (per->nper)++;
313 } /* omp critical */
314 return per->nper - 1 - (daSwap ? 0:1);
317 static t_hbdata *mk_hbdata(bool bHBmap,bool bDAnr,bool oneHB, bool bGem, int gemmode)
319 t_hbdata *hb;
321 snew(hb,1);
322 hb->wordlen = 8*sizeof(unsigned int);
323 hb->bHBmap = bHBmap;
324 hb->bDAnr = bDAnr;
325 hb->bGem = bGem;
326 if (oneHB)
327 hb->maxhydro = 1;
328 else
329 hb->maxhydro = MAXHYDRO;
330 snew(hb->per, 1);
331 hb->per->gemtype = bGem ? gemmode : 0;
333 return hb;
336 static void mk_hbmap(t_hbdata *hb,bool bTwo,bool bInsert)
338 int i,j;
340 snew(hb->hbmap,hb->d.nrd);
341 for(i=0; (i<hb->d.nrd); i++) {
342 snew(hb->hbmap[i],hb->a.nra);
343 if (hb->hbmap[i] == NULL)
344 gmx_fatal(FARGS,"Could not allocate enough memory for hbmap");
345 for (j=0; (j>hb->a.nra); j++)
346 hb->hbmap[i][j] = NULL;
350 /* Consider redoing pHist so that is only stores transitions between
351 * periodicities and not the periodicity for all frames. This eats heaps of memory. */
352 static void mk_per(t_hbdata *hb)
354 int i,j;
355 if (hb->bGem) {
356 snew(hb->per->pHist, hb->d.nrd);
357 for (i=0; i<hb->d.nrd; i++) {
358 snew(hb->per->pHist[i], hb->a.nra);
359 if (hb->per->pHist[i]==NULL)
360 gmx_fatal(FARGS,"Could not allocate enough memory for per->pHist");
361 for (j=0; j<hb->a.nra; j++) {
362 clearPshift(&(hb->per->pHist[i][j]));
365 /* add the [0,0,0] shift to element 0 of p2i. */
366 snew(hb->per->p2i, 1);
367 clear_ivec(hb->per->p2i[0]);
368 hb->per->nper = 1;
372 #ifdef HAVE_NN_LOOPS
373 static void mk_hbEmap (t_hbdata *hb, int n0)
375 int i, j, k;
376 hb->hbE.E = NULL;
377 hb->hbE.nframes = 0;
378 snew(hb->hbE.E, hb->d.nrd);
379 for (i=0; i<hb->d.nrd; i++)
381 snew(hb->hbE.E[i], hb->a.nra);
382 for (j=0; j<hb->a.nra; j++)
384 snew(hb->hbE.E[i][j], MAXHYDRO);
385 for (k=0; k<MAXHYDRO; k++)
386 hb->hbE.E[i][j][k] = NULL;
389 hb->hbE.Etot = NULL;
392 static void free_hbEmap (t_hbdata *hb)
394 int i, j, k;
395 for (i=0; i<hb->d.nrd; i++)
397 for (j=0; j<hb->a.nra; j++)
399 for (k=0; k<MAXHYDRO; k++)
400 sfree(hb->hbE.E[i][j][k]);
401 sfree(hb->hbE.E[i][j]);
403 sfree(hb->hbE.E[i]);
405 sfree(hb->hbE.E);
406 sfree(hb->hbE.Etot);
409 static void addFramesNN(t_hbdata *hb, int frame)
412 #define DELTAFRAMES_HBE 10
414 int d,a,h,nframes;
416 if (frame >= hb->hbE.nframes) {
417 nframes = hb->hbE.nframes + DELTAFRAMES_HBE;
418 srenew(hb->hbE.Etot, nframes);
420 for (d=0; d<hb->d.nrd; d++)
421 for (a=0; a<hb->a.nra; a++)
422 for (h=0; h<hb->d.nhydro[d]; h++)
423 srenew(hb->hbE.E[d][a][h], nframes);
425 hb->hbE.nframes += DELTAFRAMES_HBE;
429 static t_E calcHbEnergy(int d, int a, int h, rvec x[], t_EEst EEst,
430 matrix box, rvec hbox, t_donors *donors){
431 /* d - donor atom
432 * a - acceptor atom
433 * h - hydrogen
434 * alpha - angle between dipoles
435 * x[] - atomic positions
436 * EEst - the type of energy estimate (see enum in hbplugin.h)
437 * box - the box vectors \
438 * hbox - half box lengths _These two are only needed for the pbc correction
441 t_E E;
442 rvec dist;
443 rvec dipole[2], xmol[3], xmean[2];
444 int i;
445 real r, realE;
447 if (d == a)
448 /* Self-interaction */
449 return NONSENSE_E;
451 switch (EEst)
453 case NN_BINARY:
454 /* This is a simple binary existence function that sets E=1 whenever
455 * the distance between the oxygens is equal too or less than 0.35 nm.
457 rvec_sub(x[d], x[a], dist);
458 pbc_correct_gem(dist, box, hbox);
459 if (norm(dist) <= 0.35)
460 E = 1;
461 else
462 E = 0;
463 break;
465 case NN_1_over_r3:
466 /* Negative potential energy of a dipole.
467 * E = -cos(alpha) * 1/r^3 */
469 copy_rvec(x[d], xmol[0]); /* donor */
470 copy_rvec(x[donors->hydro[donors->dptr[d]][0]], xmol[1]); /* hydrogen */
471 copy_rvec(x[donors->hydro[donors->dptr[d]][1]], xmol[2]); /* hydrogen */
473 svmul(15.9994*(1/1.008), xmol[0], xmean[0]);
474 rvec_inc(xmean[0], xmol[1]);
475 rvec_inc(xmean[0], xmol[2]);
476 for(i=0; i<3; i++)
477 xmean[0][i] /= (15.9994 + 1.008 + 1.008)/1.008;
479 /* Assumes that all acceptors are also donors. */
480 copy_rvec(x[a], xmol[0]); /* acceptor */
481 copy_rvec(x[donors->hydro[donors->dptr[a]][0]], xmol[1]); /* hydrogen */
482 copy_rvec(x[donors->hydro[donors->dptr[a]][1]], xmol[2]); /* hydrogen */
485 svmul(15.9994*(1/1.008), xmol[0], xmean[1]);
486 rvec_inc(xmean[1], xmol[1]);
487 rvec_inc(xmean[1], xmol[2]);
488 for(i=0; i<3; i++)
489 xmean[1][i] /= (15.9994 + 1.008 + 1.008)/1.008;
491 rvec_sub(xmean[0], xmean[1], dist);
492 pbc_correct_gem(dist, box, hbox);
493 r = norm(dist);
495 realE = pow(r, -3.0);
496 E = (t_E)(SCALEFACTOR_E * realE);
497 break;
499 case NN_dipole:
500 /* Negative potential energy of a (unpolarizable) dipole.
501 * E = -cos(alpha) * 1/r^3 */
502 clear_rvec(dipole[1]);
503 clear_rvec(dipole[0]);
505 copy_rvec(x[d], xmol[0]); /* donor */
506 copy_rvec(x[donors->hydro[donors->dptr[d]][0]], xmol[1]); /* hydrogen */
507 copy_rvec(x[donors->hydro[donors->dptr[d]][1]], xmol[2]); /* hydrogen */
509 rvec_inc(dipole[0], xmol[1]);
510 rvec_inc(dipole[0], xmol[2]);
511 for (i=0; i<3; i++)
512 dipole[0][i] *= 0.5;
513 rvec_dec(dipole[0], xmol[0]);
515 svmul(15.9994*(1/1.008), xmol[0], xmean[0]);
516 rvec_inc(xmean[0], xmol[1]);
517 rvec_inc(xmean[0], xmol[2]);
518 for(i=0; i<3; i++)
519 xmean[0][i] /= (15.9994 + 1.008 + 1.008)/1.008;
521 /* Assumes that all acceptors are also donors. */
522 copy_rvec(x[a], xmol[0]); /* acceptor */
523 copy_rvec(x[donors->hydro[donors->dptr[a]][0]], xmol[1]); /* hydrogen */
524 copy_rvec(x[donors->hydro[donors->dptr[a]][2]], xmol[2]); /* hydrogen */
527 rvec_inc(dipole[1], xmol[1]);
528 rvec_inc(dipole[1], xmol[2]);
529 for (i=0; i<3; i++)
530 dipole[1][i] *= 0.5;
531 rvec_dec(dipole[1], xmol[0]);
533 svmul(15.9994*(1/1.008), xmol[0], xmean[1]);
534 rvec_inc(xmean[1], xmol[1]);
535 rvec_inc(xmean[1], xmol[2]);
536 for(i=0; i<3; i++)
537 xmean[1][i] /= (15.9994 + 1.008 + 1.008)/1.008;
539 rvec_sub(xmean[0], xmean[1], dist);
540 pbc_correct_gem(dist, box, hbox);
541 r = norm(dist);
543 double cosalpha = cos_angle(dipole[0],dipole[1]);
544 realE = cosalpha * pow(r, -3.0);
545 E = (t_E)(SCALEFACTOR_E * realE);
546 break;
548 default:
549 printf("Can't do that type of energy estimate: %i\n.", EEst);
550 E = NONSENSE_E;
553 return E;
556 static void storeHbEnergy(t_hbdata *hb, int d, int a, int h, t_E E, int frame){
557 /* hb - hbond data structure
558 d - donor
559 a - acceptor
560 h - hydrogen
561 E - estimate of the energy
562 frame - the current frame.
565 /* Store the estimated energy */
566 if (E == NONSENSE_E)
567 E = 0;
569 hb->hbE.E[d][a][h][frame] = E;
570 #ifdef HAVE_OPENMP
571 #pragma omp critical
572 #endif
574 hb->hbE.Etot[frame] += E;
577 #endif /* HAVE_NN_LOOPS */
580 /* Finds -v[] in the periodicity index */
581 static int findMirror(PSTYPE p, ivec v[], PSTYPE nper)
583 PSTYPE i;
584 ivec u;
585 for (i=0; i<nper; i++){
586 if (v[i][XX] == -(v[p][XX]) &&
587 v[i][YY] == -(v[p][YY]) &&
588 v[i][ZZ] == -(v[p][ZZ]))
589 return (int)i;
591 printf("Couldn't find mirror of [%i, %i, %i], index \n",
592 v[p][XX],
593 v[p][YY],
594 v[p][ZZ]);
595 return -1;
599 static void add_frames(t_hbdata *hb,int nframes)
601 int i,j,k,l;
603 if (nframes >= hb->max_frames) {
604 hb->max_frames += 4096;
605 srenew(hb->time,hb->max_frames);
606 srenew(hb->nhb,hb->max_frames);
607 srenew(hb->ndist,hb->max_frames);
608 srenew(hb->n_bound,hb->max_frames);
609 srenew(hb->nhx,hb->max_frames);
610 if (hb->bDAnr)
611 srenew(hb->danr,hb->max_frames);
613 hb->nframes=nframes;
616 #define OFFSET(frame) (frame / 32)
617 #define MASK(frame) (1 << (frame % 32))
619 static void _set_hb(unsigned int hbexist[],unsigned int frame,bool bValue)
621 if (bValue)
622 hbexist[OFFSET(frame)] |= MASK(frame);
623 else
624 hbexist[OFFSET(frame)] &= ~MASK(frame);
627 static bool is_hb(unsigned int hbexist[],int frame)
629 return ((hbexist[OFFSET(frame)] & MASK(frame)) != 0) ? 1 : 0;
632 static void set_hb(t_hbdata *hb,int id,int ih, int ia,int frame,int ihb)
634 unsigned int *ghptr=NULL;
636 if (ihb == hbHB)
637 ghptr = hb->hbmap[id][ia]->h[ih];
638 else if (ihb == hbDist)
639 ghptr = hb->hbmap[id][ia]->g[ih];
640 else
641 gmx_fatal(FARGS,"Incomprehensible iValue %d in set_hb",ihb);
643 _set_hb(ghptr,frame-hb->hbmap[id][ia]->n0,TRUE);
646 static void addPshift(t_pShift *pHist, PSTYPE p, int frame)
648 if (pHist->len == 0) {
649 snew(pHist->frame, 1);
650 snew(pHist->p, 1);
651 pHist->len = 1;
652 pHist->frame[0] = frame;
653 pHist->p[0] = p;
654 return;
655 } else
656 if (pHist->p[pHist->len-1] != p) {
657 pHist->len++;
658 srenew(pHist->frame, pHist->len);
659 srenew(pHist->p, pHist->len);
660 pHist->frame[pHist->len-1] = frame;
661 pHist->p[pHist->len-1] = p;
662 } /* Otherwise, there is no transition. */
663 return;
666 static PSTYPE getPshift(t_pShift pHist, int frame)
668 int f, i;
670 if (pHist.len == 0
671 || (pHist.len > 0 && pHist.frame[0]>frame))
672 return -1;
674 for (i=0; i<pHist.len; i++)
676 f = pHist.frame[i];
677 if (f==frame)
678 return pHist.p[i];
679 if (f>frame)
680 return pHist.p[i-1];
683 /* It seems that frame is after the last periodic transition. Return the last periodicity. */
684 return pHist.p[pHist.len-1];
687 static void add_ff(t_hbdata *hbd,int id,int h,int ia,int frame,int ihb, PSTYPE p)
689 int i,j,n;
690 t_hbond *hb = hbd->hbmap[id][ia];
691 int maxhydro = min(hbd->maxhydro,hbd->d.nhydro[id]);
692 int wlen = hbd->wordlen;
693 int delta = 32*wlen;
694 bool bGem = hbd->bGem;
696 if (!hb->h[0]) {
697 hb->n0 = frame;
698 hb->maxframes = delta;
699 for(i=0; (i<maxhydro); i++) {
700 snew(hb->h[i],hb->maxframes/wlen);
701 snew(hb->g[i],hb->maxframes/wlen);
703 } else {
704 hb->nframes = frame-hb->n0;
705 /* We need a while loop here because hbonds may be returning
706 * after a long time.
708 while (hb->nframes >= hb->maxframes) {
709 n = hb->maxframes + delta;
710 for(i=0; (i<maxhydro); i++) {
711 srenew(hb->h[i],n/wlen);
712 srenew(hb->g[i],n/wlen);
713 for(j=hb->maxframes/wlen; (j<n/wlen); j++) {
714 hb->h[i][j] = 0;
715 hb->g[i][j] = 0;
719 hb->maxframes = n;
722 if (frame >= 0) {
723 set_hb(hbd,id,h,ia,frame,ihb);
724 if (bGem) {
725 if (p>=hbd->per->nper)
726 gmx_fatal(FARGS, "invalid shift: p=%u, nper=%u", p, hbd->per->nper);
727 else
728 addPshift(&(hbd->per->pHist[id][ia]), p, frame);
734 static void inc_nhbonds(t_donors *ddd,int d, int h)
736 int j;
737 int dptr = ddd->dptr[d];
739 for(j=0; (j<ddd->nhydro[dptr]); j++)
740 if (ddd->hydro[dptr][j] == h) {
741 ddd->nhbonds[dptr][j]++;
742 break;
744 if (j == ddd->nhydro[dptr])
745 gmx_fatal(FARGS,"No such hydrogen %d on donor %d\n",h+1,d+1);
748 static int _acceptor_index(t_acceptors *a,int grp,atom_id i,
749 const char *file,int line)
751 int ai = a->aptr[i];
753 if (a->grp[ai] != grp) {
754 if (debug && bDebug)
755 fprintf(debug,"Acc. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
756 ai,a->grp[ai],grp,file,line);
757 return NOTSET;
759 else
760 return ai;
762 #define acceptor_index(a,grp,i) _acceptor_index(a,grp,i,__FILE__,__LINE__)
764 static int _donor_index(t_donors *d,int grp,atom_id i,const char *file,int line)
766 int di = d->dptr[i];
768 if (di == NOTSET)
769 return NOTSET;
771 if (d->grp[di] != grp) {
772 if (debug && bDebug)
773 fprintf(debug,"Don. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
774 di,d->grp[di],grp,file,line);
775 return NOTSET;
777 else
778 return di;
780 #define donor_index(d,grp,i) _donor_index(d,grp,i,__FILE__,__LINE__)
782 static bool isInterchangable(t_hbdata *hb, int d, int a, int grpa, int grpd)
784 /* g_hbond doesn't allow overlapping groups */
785 if (grpa!=grpd)
786 return FALSE;
787 return
788 donor_index(&hb->d,grpd,a) != NOTSET
789 && acceptor_index(&hb->a,grpa,d) != NOTSET;
793 static void add_hbond(t_hbdata *hb,int d,int a,int h,int grpd,int grpa,
794 int frame,bool bInsert,bool bMerge,int ihb,bool bContact, PSTYPE p)
796 int k,id,ia,hh;
797 bool daSwap = FALSE;
799 if ((id = hb->d.dptr[d]) == NOTSET)
800 gmx_fatal(FARGS,"No donor atom %d",d+1);
801 else if (grpd != hb->d.grp[id])
802 gmx_fatal(FARGS,"Inconsistent donor groups, %d iso %d, atom %d",
803 grpd,hb->d.grp[id],d+1);
804 if ((ia = hb->a.aptr[a]) == NOTSET)
805 gmx_fatal(FARGS,"No acceptor atom %d",a+1);
806 else if (grpa != hb->a.grp[ia])
807 gmx_fatal(FARGS,"Inconsistent acceptor groups, %d iso %d, atom %d",
808 grpa,hb->a.grp[ia],a+1);
810 if (bMerge)
811 if ((daSwap = isInterchangable(hb, d, a, grpd, grpa) || bContact) && d>a)
812 /* Then swap identity so that the id of d is lower then that of a.
814 * This should really be redundant by now, as is_hbond() now ought to return
815 * hbNo in the cases where this conditional is TRUE. */
817 k = d;
818 d = a;
819 a = k;
821 /* Now repeat donor/acc check. */
822 if ((id = hb->d.dptr[d]) == NOTSET)
823 gmx_fatal(FARGS,"No donor atom %d",d+1);
824 else if (grpd != hb->d.grp[id])
825 gmx_fatal(FARGS,"Inconsistent donor groups, %d iso %d, atom %d",
826 grpd,hb->d.grp[id],d+1);
827 if ((ia = hb->a.aptr[a]) == NOTSET)
828 gmx_fatal(FARGS,"No acceptor atom %d",a+1);
829 else if (grpa != hb->a.grp[ia])
830 gmx_fatal(FARGS,"Inconsistent acceptor groups, %d iso %d, atom %d",
831 grpa,hb->a.grp[ia],a+1);
834 if (hb->hbmap) {
835 /* Loop over hydrogens to find which hydrogen is in this particular HB */
836 if ((ihb == hbHB) && !bMerge && !bContact) {
837 for(k=0; (k<hb->d.nhydro[id]); k++)
838 if (hb->d.hydro[id][k] == h)
839 break;
840 if (k == hb->d.nhydro[id])
841 gmx_fatal(FARGS,"Donor %d does not have hydrogen %d (a = %d)",
842 d+1,h+1,a+1);
844 else
845 k = 0;
847 if (hb->bHBmap) {
848 if (hb->hbmap[id][ia] == NULL) {
849 snew(hb->hbmap[id][ia],1);
850 snew(hb->hbmap[id][ia]->h,hb->maxhydro);
851 snew(hb->hbmap[id][ia]->g,hb->maxhydro);
853 add_ff(hb,id,k,ia,frame,ihb,p);
856 /* Strange construction with frame >=0 is a relic from old code
857 * for selected hbond analysis. It may be necessary again if that
858 * is made to work again.
860 if (frame >= 0) {
861 hh = hb->hbmap[id][ia]->history[k];
862 if (ihb == hbHB) {
863 hb->nhb[frame]++;
864 if (!(ISHB(hh))) {
865 hb->hbmap[id][ia]->history[k] = hh | 2;
866 hb->nrhb++;
869 else
871 if (ihb == hbDist) {
872 hb->ndist[frame]++;
873 if (!(ISDIST(hh))) {
874 hb->hbmap[id][ia]->history[k] = hh | 1;
875 hb->nrdist++;
880 } else {
881 if (frame >= 0) {
882 if (ihb == hbHB) {
883 hb->nhb[frame]++;
884 } else {
885 if (ihb == hbDist) {
886 hb->ndist[frame]++;
891 if (bMerge && daSwap)
892 h = hb->d.hydro[id][0];
893 /* Increment number if HBonds per H */
894 if (ihb == hbHB && !bContact)
895 inc_nhbonds(&(hb->d),d,h);
898 /* Now a redundant function. It might find use at some point though. */
899 static bool in_list(atom_id selection,int isize,atom_id *index)
901 int i;
902 bool bFound;
904 bFound=FALSE;
905 for(i=0; (i<isize) && !bFound; i++)
906 if(selection == index[i])
907 bFound=TRUE;
909 return bFound;
912 static char *mkatomname(t_atoms *atoms,int i)
914 static char buf[32];
915 int rnr;
917 rnr = atoms->atom[i].resind;
918 sprintf(buf,"%4s%d%-4s",
919 *atoms->resinfo[rnr].name,atoms->resinfo[rnr].nr,*atoms->atomname[i]);
921 return buf;
924 static void gen_datable(atom_id *index, int isize, unsigned char *datable, int natoms){
925 /* Generates table of all atoms and sets the ingroup bit for atoms in index[] */
926 int i;
928 for (i=0; i<isize; i++){
929 if (index[i] >= natoms)
930 gmx_fatal(FARGS,"Atom has index %d larger than number of atoms %d.",index[i],natoms);
931 datable[index[i]] |= INGROUP;
935 static void clear_datable_grp(unsigned char *datable, int size){
936 /* Clears group information from the table */
937 int i;
938 const char mask = !(char)INGROUP;
939 if (size > 0)
940 for (i=0;i<size;i++)
941 datable[i] &= mask;
944 static void add_acc(t_acceptors *a,int ia,int grp)
946 if (a->nra >= a->max_nra) {
947 a->max_nra += 16;
948 srenew(a->acc,a->max_nra);
949 srenew(a->grp,a->max_nra);
951 a->grp[a->nra] = grp;
952 a->acc[a->nra++] = ia;
955 static void search_acceptors(t_topology *top,int isize,
956 atom_id *index,t_acceptors *a,int grp,
957 bool bNitAcc,
958 bool bContact,bool bDoIt, unsigned char *datable)
960 int i,n;
962 if (bDoIt) {
963 for (i=0; (i<isize); i++) {
964 n = index[i];
965 if ((bContact ||
966 (((*top->atoms.atomname[n])[0] == 'O') ||
967 (bNitAcc && ((*top->atoms.atomname[n])[0] == 'N')))) &&
968 ISINGRP(datable[n])) {
969 datable[n] |= ACC; /* set the atom's acceptor flag in datable. */
970 add_acc(a,n,grp);
974 snew(a->aptr,top->atoms.nr);
975 for(i=0; (i<top->atoms.nr); i++)
976 a->aptr[i] = NOTSET;
977 for(i=0; (i<a->nra); i++)
978 a->aptr[a->acc[i]] = i;
981 static void add_h2d(int id,int ih,t_donors *ddd)
983 int i;
985 for(i=0; (i<ddd->nhydro[id]); i++)
986 if (ddd->hydro[id][i] == ih) {
987 printf("Hm. This isn't the first time I found this donor (%d,%d)\n",
988 ddd->don[id],ih);
989 break;
991 if (i == ddd->nhydro[id]) {
992 if (ddd->nhydro[id] >= MAXHYDRO)
993 gmx_fatal(FARGS,"Donor %d has more than %d hydrogens!",
994 ddd->don[id],MAXHYDRO);
995 ddd->hydro[id][i] = ih;
996 ddd->nhydro[id]++;
1000 static void add_dh(t_donors *ddd,int id,int ih,int grp, unsigned char *datable)
1002 int i;
1004 if (ISDON(datable[id]) || !datable) {
1005 if (ddd->dptr[id] == NOTSET) { /* New donor */
1006 i = ddd->nrd;
1007 ddd->dptr[id] = i;
1008 } else
1009 i = ddd->dptr[id];
1011 if (i == ddd->nrd) {
1012 if (ddd->nrd >= ddd->max_nrd) {
1013 ddd->max_nrd += 128;
1014 srenew(ddd->don,ddd->max_nrd);
1015 srenew(ddd->nhydro,ddd->max_nrd);
1016 srenew(ddd->hydro,ddd->max_nrd);
1017 srenew(ddd->nhbonds,ddd->max_nrd);
1018 srenew(ddd->grp,ddd->max_nrd);
1020 ddd->don[ddd->nrd] = id;
1021 ddd->nhydro[ddd->nrd] = 0;
1022 ddd->grp[ddd->nrd] = grp;
1023 ddd->nrd++;
1024 } else
1025 ddd->don[i] = id;
1026 add_h2d(i,ih,ddd);
1027 } else
1028 if (datable)
1029 printf("Warning: Atom %d is not in the d/a-table!\n", id);
1032 static void search_donors(t_topology *top, int isize, atom_id *index,
1033 t_donors *ddd,int grp,bool bContact,bool bDoIt,
1034 unsigned char *datable)
1036 int i,j,nra,n;
1037 t_functype func_type;
1038 t_ilist *interaction;
1039 atom_id nr1,nr2;
1040 bool stop;
1042 if (!ddd->dptr) {
1043 snew(ddd->dptr,top->atoms.nr);
1044 for(i=0; (i<top->atoms.nr); i++)
1045 ddd->dptr[i] = NOTSET;
1048 if (bContact) {
1049 if (bDoIt)
1050 for(i=0; (i<isize); i++) {
1051 datable[index[i]] |= DON;
1052 add_dh(ddd,index[i],-1,grp,datable);
1055 else {
1056 for(func_type=0; (func_type < F_NRE); func_type++) {
1057 interaction=&(top->idef.il[func_type]);
1058 if (func_type == F_POSRES)
1059 { /* The ilist looks strange for posre. Bug in grompp?
1060 * We don't need posre interactions for hbonds anyway.*/
1061 continue;
1063 for(i=0; i < interaction->nr;
1064 i+=interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms+1) {
1065 /* next function */
1066 if (func_type != top->idef.functype[interaction->iatoms[i]]) {
1067 fprintf(stderr,"Error in func_type %s",
1068 interaction_function[func_type].longname);
1069 continue;
1072 /* check out this functype */
1073 if (func_type == F_SETTLE) {
1074 nr1=interaction->iatoms[i+1];
1076 if (ISINGRP(datable[nr1])) {
1077 if (ISINGRP(datable[nr1+1])) {
1078 datable[nr1] |= DON;
1079 add_dh(ddd,nr1,nr1+1,grp,datable);
1081 if (ISINGRP(datable[nr1+2])) {
1082 datable[nr1] |= DON;
1083 add_dh(ddd,nr1,nr1+2,grp,datable);
1087 else if (IS_CHEMBOND(func_type)) {
1088 for (j=0; j<2; j++) {
1089 nr1=interaction->iatoms[i+1+j];
1090 nr2=interaction->iatoms[i+2-j];
1091 if ((*top->atoms.atomname[nr1][0] == 'H') &&
1092 ((*top->atoms.atomname[nr2][0] == 'O') ||
1093 (*top->atoms.atomname[nr2][0] == 'N')) &&
1094 ISINGRP(datable[nr1]) && ISINGRP(datable[nr2])) {
1095 datable[nr2] |= DON;
1096 add_dh(ddd,nr2,nr1,grp,datable);
1102 #ifdef SAFEVSITES
1103 for(func_type=0; func_type < F_NRE; func_type++) {
1104 interaction=&top->idef.il[func_type];
1105 for(i=0; i < interaction->nr;
1106 i+=interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms+1) {
1107 /* next function */
1108 if (func_type != top->idef.functype[interaction->iatoms[i]])
1109 gmx_incons("function type in search_donors");
1111 if ( interaction_function[func_type].flags & IF_VSITE ) {
1112 nr1=interaction->iatoms[i+1];
1113 if ( *top->atoms.atomname[nr1][0] == 'H') {
1114 nr2=nr1-1;
1115 stop=FALSE;
1116 while (!stop && ( *top->atoms.atomname[nr2][0] == 'H'))
1117 if (nr2)
1118 nr2--;
1119 else
1120 stop=TRUE;
1121 if ( !stop && ( ( *top->atoms.atomname[nr2][0] == 'O') ||
1122 ( *top->atoms.atomname[nr2][0] == 'N') ) &&
1123 ISINGRP(datable[nr1]) && ISINGRP(datable[nr2])) {
1124 datable[nr2] |= DON;
1125 add_dh(ddd,nr2,nr1,grp,datable);
1131 #endif
1135 static t_gridcell ***init_grid(bool bBox,rvec box[],real rcut,ivec ngrid)
1137 t_gridcell ***grid;
1138 int i,y,z;
1140 if (bBox)
1141 for(i=0; i<DIM; i++)
1142 ngrid[i]=(box[i][i]/(1.2*rcut));
1144 if ( !bBox || (ngrid[XX]<3) || (ngrid[YY]<3) || (ngrid[ZZ]<3) )
1145 for(i=0; i<DIM; i++)
1146 ngrid[i]=1;
1147 else
1148 printf("\nWill do grid-seach on %dx%dx%d grid, rcut=%g\n",
1149 ngrid[XX],ngrid[YY],ngrid[ZZ],rcut);
1150 snew(grid,ngrid[ZZ]);
1151 for (z=0; z<ngrid[ZZ]; z++) {
1152 snew((grid)[z],ngrid[YY]);
1153 for (y=0; y<ngrid[YY]; y++)
1154 snew((grid)[z][y],ngrid[XX]);
1156 return grid;
1159 static void control_pHist(t_hbdata *hb, int nframes)
1161 int i,j,k;
1162 PSTYPE p;
1163 for (i=0;i<hb->d.nrd;i++)
1164 for (j=0;j<hb->a.nra;j++)
1165 if (hb->per->pHist[i][j].len != 0)
1166 for (k=hb->hbmap[i][j][0].n0; k<nframes; k++) {
1167 p = getPshift(hb->per->pHist[i][j], k);
1168 if (p>hb->per->nper)
1169 fprintf(stderr, "Weird stuff in pHist[%i][%i].p at frame %i: p=%i\n",
1170 i,j,k,p);
1174 static void reset_nhbonds(t_donors *ddd)
1176 int i,j;
1178 for(i=0; (i<ddd->nrd); i++)
1179 for(j=0; (j<MAXHH); j++)
1180 ddd->nhbonds[i][j] = 0;
1183 void pbc_correct_gem(rvec dx,matrix box,rvec hbox);
1185 static void build_grid(t_hbdata *hb,rvec x[], rvec xshell,
1186 bool bBox, matrix box, rvec hbox,
1187 real rcut, real rshell,
1188 ivec ngrid, t_gridcell ***grid)
1190 int i,m,gr,xi,yi,zi,nr;
1191 atom_id *ad;
1192 ivec grididx;
1193 rvec invdelta,dshell,xtemp={0,0,0};
1194 t_ncell *newgrid;
1195 bool bDoRshell,bInShell,bAcc;
1196 real rshell2=0;
1197 int gx,gy,gz;
1198 int dum = -1;
1200 bDoRshell = (rshell > 0);
1201 rshell2 = sqr(rshell);
1202 bInShell = TRUE;
1204 #define DBB(x) if (debug && bDebug) fprintf(debug,"build_grid, line %d, %s = %d\n",__LINE__,#x,x)
1205 DBB(dum);
1206 for(m=0; m<DIM; m++) {
1207 hbox[m]=box[m][m]*0.5;
1208 if (bBox) {
1209 invdelta[m]=ngrid[m]/box[m][m];
1210 if (1/invdelta[m] < rcut)
1211 gmx_fatal(FARGS,"Your computational box has shrunk too much.\n"
1212 "%s can not handle this situation, sorry.\n",
1213 ShortProgram());
1214 } else
1215 invdelta[m]=0;
1217 grididx[XX]=0;
1218 grididx[YY]=0;
1219 grididx[ZZ]=0;
1220 DBB(dum);
1221 /* resetting atom counts */
1222 for(gr=0; (gr<grNR); gr++) {
1223 for (zi=0; zi<ngrid[ZZ]; zi++)
1224 for (yi=0; yi<ngrid[YY]; yi++)
1225 for (xi=0; xi<ngrid[XX]; xi++) {
1226 grid[zi][yi][xi].d[gr].nr=0;
1227 grid[zi][yi][xi].a[gr].nr=0;
1229 DBB(dum);
1231 /* put atoms in grid cells */
1232 for(bAcc=FALSE; (bAcc<=TRUE); bAcc++) {
1233 if (bAcc) {
1234 nr = hb->a.nra;
1235 ad = hb->a.acc;
1237 else {
1238 nr = hb->d.nrd;
1239 ad = hb->d.don;
1241 DBB(bAcc);
1242 for(i=0; (i<nr); i++) {
1243 /* check if we are inside the shell */
1244 /* if bDoRshell=FALSE then bInShell=TRUE always */
1245 DBB(i);
1246 if ( bDoRshell ) {
1247 bInShell=TRUE;
1248 rvec_sub(x[ad[i]],xshell,dshell);
1249 if (bBox) {
1250 if (FALSE && !hb->bGem) {
1251 for(m=DIM-1; m>=0 && bInShell; m--) {
1252 if ( dshell[m] < -hbox[m] )
1253 rvec_inc(dshell,box[m]);
1254 else if ( dshell[m] >= hbox[m] )
1255 dshell[m] -= 2*hbox[m];
1256 /* if we're outside the cube, we're outside the sphere also! */
1257 if ( (dshell[m]>rshell) || (-dshell[m]>rshell) )
1258 bInShell=FALSE;
1260 } else {
1261 bool bDone = FALSE;
1262 while (!bDone)
1264 bDone = TRUE;
1265 for(m=DIM-1; m>=0 && bInShell; m--) {
1266 if ( dshell[m] < -hbox[m] ) {
1267 bDone = FALSE;
1268 rvec_inc(dshell,box[m]);
1270 if ( dshell[m] >= hbox[m] ) {
1271 bDone = FALSE;
1272 dshell[m] -= 2*hbox[m];
1276 for(m=DIM-1; m>=0 && bInShell; m--) {
1277 /* if we're outside the cube, we're outside the sphere also! */
1278 if ( (dshell[m]>rshell) || (-dshell[m]>rshell) )
1279 bInShell=FALSE;
1283 /* if we're inside the cube, check if we're inside the sphere */
1284 if (bInShell)
1285 bInShell = norm2(dshell) < rshell2;
1287 DBB(i);
1288 if ( bInShell ) {
1289 if (bBox) {
1290 if (hb->bGem)
1291 copy_rvec(x[ad[i]], xtemp);
1292 pbc_correct_gem(x[ad[i]], box, hbox);
1294 for(m=DIM-1; m>=0; m--) {
1295 if (TRUE || !hb->bGem){
1296 /* put atom in the box */
1297 while( x[ad[i]][m] < 0 )
1298 rvec_inc(x[ad[i]],box[m]);
1299 while( x[ad[i]][m] >= box[m][m] )
1300 rvec_dec(x[ad[i]],box[m]);
1302 /* determine grid index of atom */
1303 grididx[m]=x[ad[i]][m]*invdelta[m];
1304 grididx[m] = (grididx[m]+ngrid[m]) % ngrid[m];
1306 if (hb->bGem)
1307 copy_rvec(xtemp, x[ad[i]]); /* copy back */
1308 gx = grididx[XX];
1309 gy = grididx[YY];
1310 gz = grididx[ZZ];
1311 range_check(gx,0,ngrid[XX]);
1312 range_check(gy,0,ngrid[YY]);
1313 range_check(gz,0,ngrid[ZZ]);
1314 DBB(gx);
1315 DBB(gy);
1316 DBB(gz);
1317 /* add atom to grid cell */
1318 if (bAcc)
1319 newgrid=&(grid[gz][gy][gx].a[gr]);
1320 else
1321 newgrid=&(grid[gz][gy][gx].d[gr]);
1322 if (newgrid->nr >= newgrid->maxnr) {
1323 newgrid->maxnr+=10;
1324 DBB(newgrid->maxnr);
1325 srenew(newgrid->atoms, newgrid->maxnr);
1327 DBB(newgrid->nr);
1328 newgrid->atoms[newgrid->nr]=ad[i];
1329 newgrid->nr++;
1336 static void count_da_grid(ivec ngrid, t_gridcell ***grid, t_icell danr)
1338 int gr,xi,yi,zi;
1340 for(gr=0; (gr<grNR); gr++) {
1341 danr[gr]=0;
1342 for (zi=0; zi<ngrid[ZZ]; zi++)
1343 for (yi=0; yi<ngrid[YY]; yi++)
1344 for (xi=0; xi<ngrid[XX]; xi++) {
1345 danr[gr] += grid[zi][yi][xi].d[gr].nr;
1350 /* The grid loop.
1351 * Without a box, the grid is 1x1x1, so all loops are 1 long.
1352 * With a rectangular box (bTric==FALSE) all loops are 3 long.
1353 * With a triclinic box all loops are 3 long, except when a cell is
1354 * located next to one of the box edges which is not parallel to the
1355 * x/y-plane, in that case all cells in a line or layer are searched.
1356 * This could be implemented slightly more efficient, but the code
1357 * would get much more complicated.
1359 #define B(n,x,bTric,bEdge) ((n==1) ? x : bTric&&(bEdge) ? 0 : (x-1))
1360 #define E(n,x,bTric,bEdge) ((n==1) ? x : bTric&&(bEdge) ? n-1 : (x+1))
1361 #define GRIDMOD(j,n) (j+n)%(n)
1362 #define LOOPGRIDINNER(x,y,z,xx,yy,zz,xo,yo,zo,n,bTric) \
1363 for(zz=B(n[ZZ],zo,bTric,FALSE); zz<=E(n[ZZ],zo,bTric,FALSE); zz++) { \
1364 z=GRIDMOD(zz,n[ZZ]); \
1365 for(yy=B(n[YY],yo,bTric,z==0||z==n[ZZ]-1); \
1366 yy<=E(n[YY],yo,bTric,z==0||z==n[ZZ]-1); yy++) { \
1367 y=GRIDMOD(yy,n[YY]); \
1368 for(xx=B(n[XX],xo,bTric,y==0||y==n[YY]-1||z==0||z==n[ZZ]-1); \
1369 xx<=E(n[XX],xo,bTric,y==0||y==n[YY]-1||z==0||z==n[ZZ]-1); xx++) { \
1370 x=GRIDMOD(xx,n[XX]);
1371 #define ENDLOOPGRIDINNER \
1377 static void dump_grid(FILE *fp, ivec ngrid, t_gridcell ***grid)
1379 int gr,x,y,z,sum[grNR];
1381 fprintf(fp,"grid %dx%dx%d\n",ngrid[XX],ngrid[YY],ngrid[ZZ]);
1382 for (gr=0; gr<grNR; gr++) {
1383 sum[gr]=0;
1384 fprintf(fp,"GROUP %d (%s)\n",gr,grpnames[gr]);
1385 for (z=0; z<ngrid[ZZ]; z+=2) {
1386 fprintf(fp,"Z=%d,%d\n",z,z+1);
1387 for (y=0; y<ngrid[YY]; y++) {
1388 for (x=0; x<ngrid[XX]; x++) {
1389 fprintf(fp,"%3d",grid[x][y][z].d[gr].nr);
1390 sum[gr]+=grid[z][y][x].d[gr].nr;
1391 fprintf(fp,"%3d",grid[x][y][z].a[gr].nr);
1392 sum[gr]+=grid[z][y][x].a[gr].nr;
1395 fprintf(fp," | ");
1396 if ( (z+1) < ngrid[ZZ] )
1397 for (x=0; x<ngrid[XX]; x++) {
1398 fprintf(fp,"%3d",grid[z+1][y][x].d[gr].nr);
1399 sum[gr]+=grid[z+1][y][x].d[gr].nr;
1400 fprintf(fp,"%3d",grid[z+1][y][x].a[gr].nr);
1401 sum[gr]+=grid[z+1][y][x].a[gr].nr;
1403 fprintf(fp,"\n");
1407 fprintf(fp,"TOTALS:");
1408 for (gr=0; gr<grNR; gr++)
1409 fprintf(fp," %d=%d",gr,sum[gr]);
1410 fprintf(fp,"\n");
1413 /* New GMX record! 5 * in a row. Congratulations!
1414 * Sorry, only four left.
1416 static void free_grid(ivec ngrid, t_gridcell ****grid)
1418 int y,z;
1419 t_gridcell ***g = *grid;
1421 for (z=0; z<ngrid[ZZ]; z++) {
1422 for (y=0; y<ngrid[YY]; y++) {
1423 sfree(g[z][y]);
1425 sfree(g[z]);
1427 sfree(g);
1428 g=NULL;
1431 static void pbc_correct(rvec dx,matrix box,rvec hbox)
1433 int m;
1434 for(m=DIM-1; m>=0; m--) {
1435 if ( dx[m] < -hbox[m] )
1436 rvec_inc(dx,box[m]);
1437 else if ( dx[m] >= hbox[m] )
1438 rvec_dec(dx,box[m]);
1442 void pbc_correct_gem(rvec dx,matrix box,rvec hbox)
1444 int m;
1445 bool bDone = FALSE;
1446 while (!bDone) {
1447 bDone = TRUE;
1448 for(m=DIM-1; m>=0; m--) {
1449 if ( dx[m] < -hbox[m] ) {
1450 bDone = FALSE;
1451 rvec_inc(dx,box[m]);
1453 if ( dx[m] >= hbox[m] ) {
1454 bDone = FALSE;
1455 rvec_dec(dx,box[m]);
1461 /* Added argument r2cut, changed contact and implemented
1462 * use of second cut-off.
1463 * - Erik Marklund, June 29, 2006
1465 static int is_hbond(t_hbdata *hb,int grpd,int grpa,int d,int a,
1466 real rcut, real r2cut, real ccut,
1467 rvec x[], bool bBox, matrix box,rvec hbox,
1468 real *d_ha, real *ang,bool bDA,int *hhh,
1469 bool bContact, bool bMerge, PSTYPE *p)
1471 int h,hh,id,ja,ihb;
1472 rvec r_da,r_ha,r_dh, r={0, 0, 0};
1473 ivec ri;
1474 real rc2,r2c2,rda2,rha2,ca;
1475 bool HAinrange = FALSE; /* If !bDA. Needed for returning hbDist in a correct way. */
1476 bool daSwap = FALSE;
1478 if (d == a)
1479 return hbNo;
1481 if (((id = donor_index(&hb->d,grpd,d)) == NOTSET) ||
1482 ((ja = acceptor_index(&hb->a,grpa,a)) == NOTSET))
1483 return hbNo;
1485 rc2 = rcut*rcut;
1486 r2c2 = r2cut*r2cut;
1488 rvec_sub(x[d],x[a],r_da);
1489 /* Insert projection code here */
1491 /* if (d>a && ((isInterchangable(hb, d, a, grpd, grpa) && bMerge) || bContact)) */
1492 /* /\* Then this hbond will be found again, or it has already been found. *\/ */
1493 /* return hbNo; */
1495 if (bBox){
1496 if (d>a && bMerge && (bContact || isInterchangable(hb, d, a, grpd, grpa))) { /* acceptor is also a donor and vice versa? */
1497 return hbNo;
1498 daSwap = TRUE; /* If so, then their history should be filed with donor and acceptor swapped. */
1500 if (hb->bGem) {
1501 copy_rvec(r_da, r); /* Save this for later */
1502 pbc_correct_gem(r_da,box,hbox);
1503 } else {
1504 pbc_correct_gem(r_da,box,hbox);
1507 rda2 = iprod(r_da,r_da);
1509 if (bContact) {
1510 if (daSwap)
1511 return hbNo;
1512 if (rda2 <= rc2){
1513 if (hb->bGem){
1514 calcBoxDistance(hb->per->P, r, ri);
1515 *p = periodicIndex(ri, hb->per, daSwap); /* find (or add) periodicity index. */
1517 return hbHB;
1519 else if (rda2 < r2c2)
1520 return hbDist;
1521 else
1522 return hbNo;
1524 *hhh = NOTSET;
1526 if (bDA && (rda2 > rc2))
1527 return hbNo;
1529 for(h=0; (h < hb->d.nhydro[id]); h++) {
1530 hh = hb->d.hydro[id][h];
1531 rha2 = rc2+1;
1532 if (!bDA) {
1533 rvec_sub(x[hh],x[a],r_ha);
1534 if (bBox) {
1535 pbc_correct_gem(r_ha,box,hbox);
1537 rha2 = iprod(r_ha,r_ha);
1540 if (hb->bGem) {
1541 calcBoxDistance(hb->per->P, r, ri);
1542 *p = periodicIndex(ri, hb->per, daSwap); /* find periodicity index. */
1545 if (bDA || (!bDA && (rha2 <= rc2))) {
1546 rvec_sub(x[d],x[hh],r_dh);
1547 if (bBox) {
1548 if (hb->bGem)
1549 pbc_correct_gem(r_dh,box,hbox);
1550 else
1551 pbc_correct_gem(r_dh,box,hbox);
1554 if (!bDA)
1555 HAinrange = TRUE;
1556 ca = cos_angle(r_dh,r_da);
1557 /* if angle is smaller, cos is larger */
1558 if (ca >= ccut) {
1559 *hhh = hh;
1560 *d_ha = sqrt(bDA?rda2:rha2);
1561 *ang = acos(ca);
1562 return hbHB;
1566 if (bDA || (!bDA && HAinrange))
1567 return hbDist;
1568 else
1569 return hbNo;
1572 /* Fixed previously undiscovered bug in the merge
1573 code, where the last frame of each hbond disappears.
1574 - Erik Marklund, June 1, 2006 */
1575 /* Added the following arguments:
1576 * ptmp[] - temporary periodicity hisory
1577 * a1 - identity of first acceptor/donor
1578 * a2 - identity of second acceptor/donor
1579 * - Erik Marklund, FEB 20 2010 */
1581 /* Merging is now done on the fly, so do_merge is most likely obsolete now.
1582 * Will do some more testing before removing the function entirely.
1583 * - Erik Marklund, MAY 10 2010 */
1584 static void do_merge(t_hbdata *hb,int ntmp,
1585 unsigned int htmp[],unsigned int gtmp[],PSTYPE ptmp[],
1586 t_hbond *hb0,t_hbond *hb1, int a1, int a2)
1588 /* Here we need to make sure we're treating periodicity in
1589 * the right way for the geminate recombination kinetics. */
1591 int m,mm,n00,n01,nn0,nnframes;
1592 PSTYPE pm;
1593 t_pShift *pShift;
1595 /* Decide where to start from when merging */
1596 n00 = hb0->n0;
1597 n01 = hb1->n0;
1598 nn0 = min(n00,n01);
1599 nnframes = max(n00 + hb0->nframes,n01 + hb1->nframes) - nn0;
1600 /* Initiate tmp arrays */
1601 for(m=0; (m<ntmp); m++) {
1602 htmp[m] = 0;
1603 gtmp[m] = 0;
1604 ptmp[m] = 0;
1606 /* Fill tmp arrays with values due to first HB */
1607 /* Once again '<' had to be replaced with '<='
1608 to catch the last frame in which the hbond
1609 appears.
1610 - Erik Marklund, June 1, 2006 */
1611 for(m=0; (m<=hb0->nframes); m++) {
1612 mm = m+n00-nn0;
1613 htmp[mm] = is_hb(hb0->h[0],m);
1614 if (hb->bGem) {
1615 pm = getPshift(hb->per->pHist[a1][a2], m+hb0->n0);
1616 if (pm > hb->per->nper)
1617 gmx_fatal(FARGS, "Illegal shift!");
1618 else
1619 ptmp[mm] = pm; /*hb->per->pHist[a1][a2][m];*/
1622 /* If we're doing geminate recompbination we usually don't need the distances.
1623 * Let's save some memory and time. */
1624 if (TRUE || !hb->bGem || hb->per->gemtype == gemAD){
1625 for(m=0; (m<=hb0->nframes); m++) {
1626 mm = m+n00-nn0;
1627 gtmp[mm] = is_hb(hb0->g[0],m);
1630 /* Next HB */
1631 for(m=0; (m<=hb1->nframes); m++) {
1632 mm = m+n01-nn0;
1633 htmp[mm] = htmp[mm] || is_hb(hb1->h[0],m);
1634 gtmp[mm] = gtmp[mm] || is_hb(hb1->g[0],m);
1635 if (hb->bGem /* && ptmp[mm] != 0 */) {
1637 /* If this hbond has been seen before with donor and acceptor swapped,
1638 * then we need to find the mirrored (*-1) periodicity vector to truely
1639 * merge the hbond history. */
1640 pm = findMirror(getPshift(hb->per->pHist[a2][a1],m+hb1->n0), hb->per->p2i, hb->per->nper);
1641 /* Store index of mirror */
1642 if (pm > hb->per->nper)
1643 gmx_fatal(FARGS, "Illegal shift!");
1644 ptmp[mm] = pm;
1647 /* Reallocate target array */
1648 if (nnframes > hb0->maxframes) {
1649 srenew(hb0->h[0],4+nnframes/hb->wordlen);
1650 srenew(hb0->g[0],4+nnframes/hb->wordlen);
1652 clearPshift(&(hb->per->pHist[a1][a2]));
1654 /* Copy temp array to target array */
1655 for(m=0; (m<=nnframes); m++) {
1656 _set_hb(hb0->h[0],m,htmp[m]);
1657 _set_hb(hb0->g[0],m,gtmp[m]);
1658 if (hb->bGem)
1659 addPshift(&(hb->per->pHist[a1][a2]), ptmp[m], m+nn0);
1662 /* Set scalar variables */
1663 hb0->n0 = nn0;
1664 hb0->maxframes = nnframes;
1667 /* Added argument bContact for nicer output.
1668 * Erik Marklund, June 29, 2006
1670 static void merge_hb(t_hbdata *hb,bool bTwo, bool bContact){
1671 int i,inrnew,indnew,j,ii,jj,m,id,ia,grp,ogrp,ntmp;
1672 unsigned int *htmp,*gtmp;
1673 PSTYPE *ptmp;
1674 t_hbond *hb0,*hb1;
1676 inrnew = hb->nrhb;
1677 indnew = hb->nrdist;
1679 /* Check whether donors are also acceptors */
1680 printf("Merging hbonds with Acceptor and Donor swapped\n");
1682 ntmp = 2*hb->max_frames;
1683 snew(gtmp,ntmp);
1684 snew(htmp,ntmp);
1685 snew(ptmp,ntmp);
1686 for(i=0; (i<hb->d.nrd); i++) {
1687 fprintf(stderr,"\r%d/%d",i+1,hb->d.nrd);
1688 id = hb->d.don[i];
1689 ii = hb->a.aptr[id];
1690 for(j=0; (j<hb->a.nra); j++) {
1691 ia = hb->a.acc[j];
1692 jj = hb->d.dptr[ia];
1693 if ((id != ia) && (ii != NOTSET) && (jj != NOTSET) &&
1694 (!bTwo || (bTwo && (hb->d.grp[i] != hb->a.grp[j])))) {
1695 hb0 = hb->hbmap[i][j];
1696 hb1 = hb->hbmap[jj][ii];
1697 if (hb0 && hb1 && ISHB(hb0->history[0]) && ISHB(hb1->history[0])) {
1698 do_merge(hb,ntmp,htmp,gtmp,ptmp,hb0,hb1,i,j);
1699 if (ISHB(hb1->history[0]))
1700 inrnew--;
1701 else if (ISDIST(hb1->history[0]))
1702 indnew--;
1703 else
1704 if (bContact)
1705 gmx_incons("No contact history");
1706 else
1707 gmx_incons("Neither hydrogen bond nor distance");
1708 sfree(hb1->h[0]);
1709 sfree(hb1->g[0]);
1710 if (hb->bGem) {
1711 clearPshift(&(hb->per->pHist[jj][ii]));
1713 hb1->h[0] = NULL;
1714 hb1->g[0] = NULL;
1715 hb1->history[0] = hbNo;
1720 fprintf(stderr,"\n");
1721 printf("- Reduced number of hbonds from %d to %d\n",hb->nrhb,inrnew);
1722 printf("- Reduced number of distances from %d to %d\n",hb->nrdist,indnew);
1723 hb->nrhb = inrnew;
1724 hb->nrdist = indnew;
1725 sfree(gtmp);
1726 sfree(htmp);
1727 sfree(ptmp);
1730 static void do_nhb_dist(FILE *fp,t_hbdata *hb,real t)
1732 int i,j,k,n_bound[MAXHH],nbtot;
1733 h_id nhb;
1736 /* Set array to 0 */
1737 for(k=0; (k<MAXHH); k++)
1738 n_bound[k] = 0;
1739 /* Loop over possible donors */
1740 for(i=0; (i<hb->d.nrd); i++) {
1741 for(j=0; (j<hb->d.nhydro[i]); j++)
1742 n_bound[hb->d.nhbonds[i][j]]++;
1744 fprintf(fp,"%12.5e",t);
1745 nbtot = 0;
1746 for(k=0; (k<MAXHH); k++) {
1747 fprintf(fp," %8d",n_bound[k]);
1748 nbtot += n_bound[k]*k;
1750 fprintf(fp," %8d\n",nbtot);
1753 /* Added argument bContact in do_hblife(...). Also
1754 * added support for -contact in function body.
1755 * - Erik Marklund, May 31, 2006 */
1756 /* Changed the contact code slightly.
1757 * - Erik Marklund, June 29, 2006
1759 static void do_hblife(const char *fn,t_hbdata *hb,bool bMerge,bool bContact,
1760 const output_env_t oenv)
1762 FILE *fp;
1763 char *leg[] = { "p(t)", "t p(t)" };
1764 int *histo;
1765 int i,j,j0,k,m,nh,ihb,ohb,nhydro,ndump=0;
1766 int nframes = hb->nframes;
1767 unsigned int **h;
1768 real t,x1,dt;
1769 double sum,integral;
1770 t_hbond *hbh;
1772 snew(h,hb->maxhydro);
1773 snew(histo,nframes+1);
1774 /* Total number of hbonds analyzed here */
1775 for(i=0; (i<hb->d.nrd); i++) {
1776 for(k=0; (k<hb->a.nra); k++) {
1777 hbh = hb->hbmap[i][k];
1778 if (hbh) {
1779 if (bMerge) {
1780 if (hbh->h[0]) {
1781 h[0] = hbh->h[0];
1782 nhydro = 1;
1784 else
1785 nhydro = 0;
1787 else {
1788 nhydro = 0;
1789 for(m=0; (m<hb->maxhydro); m++)
1790 if (hbh->h[m]) {
1791 h[nhydro++] = bContact ? hbh->g[m] : hbh->h[m];
1794 for(nh=0; (nh<nhydro); nh++) {
1795 ohb = 0;
1796 j0 = 0;
1798 /* Changed '<' into '<=' below, just like I
1799 did in the hbm-output-loop in the main code.
1800 - Erik Marklund, May 31, 2006
1802 for(j=0; (j<=hbh->nframes); j++) {
1803 ihb = is_hb(h[nh],j);
1804 if (debug && (ndump < 10))
1805 fprintf(debug,"%5d %5d\n",j,ihb);
1806 if (ihb != ohb) {
1807 if (ihb) {
1808 j0 = j;
1810 else {
1811 histo[j-j0]++;
1813 ohb = ihb;
1816 ndump++;
1821 fprintf(stderr,"\n");
1822 if (bContact)
1823 fp = xvgropen(fn,"Uninterrupted contact lifetime","Time (ps)","()",oenv);
1824 else
1825 fp = xvgropen(fn,"Uninterrupted hydrogen bond lifetime","Time (ps)","()",
1826 oenv);
1828 xvgr_legend(fp,asize(leg),leg,oenv);
1829 j0 = nframes-1;
1830 while ((j0 > 0) && (histo[j0] == 0))
1831 j0--;
1832 sum = 0;
1833 for(i=0; (i<=j0); i++)
1834 sum+=histo[i];
1835 dt = hb->time[1]-hb->time[0];
1836 sum = dt*sum;
1837 integral = 0;
1838 for(i=1; (i<=j0); i++) {
1839 t = hb->time[i] - hb->time[0] - 0.5*dt;
1840 x1 = t*histo[i]/sum;
1841 fprintf(fp,"%8.3f %10.3e %10.3e\n",t,histo[i]/sum,x1);
1842 integral += x1;
1844 integral *= dt;
1845 ffclose(fp);
1846 printf("%s lifetime = %.2f ps\n", bContact?"Contact":"HB", integral);
1847 printf("Note that the lifetime obtained in this manner is close to useless\n");
1848 printf("Use the -ac option instead and check the Forward lifetime\n");
1849 please_cite(stdout,"Spoel2006b");
1850 sfree(h);
1851 sfree(histo);
1854 /* Changed argument bMerge into oneHB to handle contacts properly.
1855 * - Erik Marklund, June 29, 2006
1857 static void dump_ac(t_hbdata *hb,bool oneHB,int nDump)
1859 FILE *fp;
1860 int i,j,k,m,nd,ihb,idist;
1861 int nframes = hb->nframes;
1862 bool bPrint;
1863 t_hbond *hbh;
1865 if (nDump <= 0)
1866 return;
1867 fp = ffopen("debug-ac.xvg","w");
1868 for(j=0; (j<nframes); j++) {
1869 fprintf(fp,"%10.3f",hb->time[j]);
1870 for(i=nd=0; (i<hb->d.nrd) && (nd < nDump); i++) {
1871 for(k=0; (k<hb->a.nra) && (nd < nDump); k++) {
1872 bPrint = FALSE;
1873 ihb = idist = 0;
1874 hbh = hb->hbmap[i][k];
1875 if (oneHB) {
1876 if (hbh->h[0]) {
1877 ihb = is_hb(hbh->h[0],j);
1878 idist = is_hb(hbh->g[0],j);
1879 bPrint = TRUE;
1882 else {
1883 for(m=0; (m<hb->maxhydro) && !ihb ; m++) {
1884 ihb = ihb || ((hbh->h[m]) && is_hb(hbh->h[m],j));
1885 idist = idist || ((hbh->g[m]) && is_hb(hbh->g[m],j));
1887 /* This is not correct! */
1888 /* What isn't correct? -Erik M */
1889 bPrint = TRUE;
1891 if (bPrint) {
1892 fprintf(fp," %1d-%1d",ihb,idist);
1893 nd++;
1897 fprintf(fp,"\n");
1899 ffclose(fp);
1902 static real calc_dg(real tau,real temp)
1904 real kbt;
1906 kbt = BOLTZ*temp;
1907 if (tau <= 0)
1908 return -666;
1909 else
1910 return kbt*log(kbt*tau/PLANCK);
1913 typedef struct {
1914 int n0,n1,nparams,ndelta;
1915 real kkk[2];
1916 real *t,*ct,*nt,*kt,*sigma_ct,*sigma_nt,*sigma_kt;
1917 } t_luzar;
1919 #ifdef HAVE_LIBGSL
1920 #include <gsl/gsl_multimin.h>
1921 #include <gsl/gsl_sf.h>
1922 #include <gsl/gsl_version.h>
1924 static double my_f(const gsl_vector *v,void *params)
1926 t_luzar *tl = (t_luzar *)params;
1927 int i;
1928 double tol=1e-16,chi2=0;
1929 double di;
1930 real k,kp;
1932 for(i=0; (i<tl->nparams); i++) {
1933 tl->kkk[i] = gsl_vector_get(v, i);
1935 k = tl->kkk[0];
1936 kp = tl->kkk[1];
1938 for(i=tl->n0; (i<tl->n1); i+=tl->ndelta) {
1939 di=sqr(k*tl->sigma_ct[i]) + sqr(kp*tl->sigma_nt[i]) + sqr(tl->sigma_kt[i]);
1940 /*di = 1;*/
1941 if (di > tol)
1942 chi2 += sqr(k*tl->ct[i]-kp*tl->nt[i]-tl->kt[i])/di;
1944 else
1945 fprintf(stderr,"WARNING: sigma_ct = %g, sigma_nt = %g, sigma_kt = %g\n"
1946 "di = %g k = %g kp = %g\n",tl->sigma_ct[i],
1947 tl->sigma_nt[i],tl->sigma_kt[i],di,k,kp);
1949 #ifdef DEBUG
1950 chi2 = 0.3*sqr(k-0.6)+0.7*sqr(kp-1.3);
1951 #endif
1952 return chi2;
1955 static real optimize_luzar_parameters(FILE *fp,t_luzar *tl,int maxiter,
1956 real tol)
1958 real size,d2;
1959 int iter = 0;
1960 int status = 0;
1961 int i;
1963 const gsl_multimin_fminimizer_type *T;
1964 gsl_multimin_fminimizer *s;
1966 gsl_vector *x,*dx;
1967 gsl_multimin_function my_func;
1969 my_func.f = &my_f;
1970 my_func.n = tl->nparams;
1971 my_func.params = (void *) tl;
1973 /* Starting point */
1974 x = gsl_vector_alloc (my_func.n);
1975 for(i=0; (i<my_func.n); i++)
1976 gsl_vector_set (x, i, tl->kkk[i]);
1978 /* Step size, different for each of the parameters */
1979 dx = gsl_vector_alloc (my_func.n);
1980 for(i=0; (i<my_func.n); i++)
1981 gsl_vector_set (dx, i, 0.01*tl->kkk[i]);
1983 T = gsl_multimin_fminimizer_nmsimplex;
1984 s = gsl_multimin_fminimizer_alloc (T, my_func.n);
1986 gsl_multimin_fminimizer_set (s, &my_func, x, dx);
1987 gsl_vector_free (x);
1988 gsl_vector_free (dx);
1990 if (fp)
1991 fprintf(fp,"%5s %12s %12s %12s %12s\n","Iter","k","kp","NM Size","Chi2");
1993 do {
1994 iter++;
1995 status = gsl_multimin_fminimizer_iterate (s);
1997 if (status != 0)
1998 gmx_fatal(FARGS,"Something went wrong in the iteration in minimizer %s",
1999 gsl_multimin_fminimizer_name(s));
2001 d2 = gsl_multimin_fminimizer_minimum(s);
2002 size = gsl_multimin_fminimizer_size(s);
2003 status = gsl_multimin_test_size(size,tol);
2005 if (status == GSL_SUCCESS)
2006 if (fp)
2007 fprintf(fp,"Minimum found using %s at:\n",
2008 gsl_multimin_fminimizer_name(s));
2010 if (fp) {
2011 fprintf(fp,"%5d", iter);
2012 for(i=0; (i<my_func.n); i++)
2013 fprintf(fp," %12.4e",gsl_vector_get (s->x,i));
2014 fprintf (fp," %12.4e %12.4e\n",size,d2);
2017 while ((status == GSL_CONTINUE) && (iter < maxiter));
2019 gsl_multimin_fminimizer_free (s);
2021 return d2;
2024 static real quality_of_fit(real chi2,int N)
2026 return gsl_sf_gamma_inc_Q((N-2)/2.0,chi2/2.0);
2029 #else
2030 static real optimize_luzar_parameters(FILE *fp,t_luzar *tl,int maxiter,
2031 real tol)
2033 fprintf(stderr,"This program needs the GNU scientific library to work.\n");
2035 return -1;
2037 static real quality_of_fit(real chi2,int N)
2039 fprintf(stderr,"This program needs the GNU scientific library to work.\n");
2041 return -1;
2044 #endif
2046 static real compute_weighted_rates(int n,real t[],real ct[],real nt[],
2047 real kt[],real sigma_ct[],real sigma_nt[],
2048 real sigma_kt[],real *k,real *kp,
2049 real *sigma_k,real *sigma_kp,
2050 real fit_start)
2052 #define NK 10
2053 int i,j;
2054 t_luzar tl;
2055 real kkk=0,kkp=0,kk2=0,kp2=0,chi2;
2057 *sigma_k = 0;
2058 *sigma_kp = 0;
2060 for(i=0; (i<n); i++) {
2061 if (t[i] >= fit_start)
2062 break;
2064 tl.n0 = i;
2065 tl.n1 = n;
2066 tl.nparams = 2;
2067 tl.ndelta = 1;
2068 tl.t = t;
2069 tl.ct = ct;
2070 tl.nt = nt;
2071 tl.kt = kt;
2072 tl.sigma_ct = sigma_ct;
2073 tl.sigma_nt = sigma_nt;
2074 tl.sigma_kt = sigma_kt;
2075 tl.kkk[0] = *k;
2076 tl.kkk[1] = *kp;
2078 chi2 = optimize_luzar_parameters(debug,&tl,1000,1e-3);
2079 *k = tl.kkk[0];
2080 *kp = tl.kkk[1] = *kp;
2081 tl.ndelta = NK;
2082 for(j=0; (j<NK); j++) {
2083 (void) optimize_luzar_parameters(debug,&tl,1000,1e-3);
2084 kkk += tl.kkk[0];
2085 kkp += tl.kkk[1];
2086 kk2 += sqr(tl.kkk[0]);
2087 kp2 += sqr(tl.kkk[1]);
2088 tl.n0++;
2090 *sigma_k = sqrt(kk2/NK - sqr(kkk/NK));
2091 *sigma_kp = sqrt(kp2/NK - sqr(kkp/NK));
2093 return chi2;
2096 static void smooth_tail(int n,real t[],real c[],real sigma_c[],real start,
2097 const output_env_t oenv)
2099 FILE *fp;
2100 real e_1,fitparm[4];
2101 int i;
2103 e_1 = exp(-1);
2104 for(i=0; (i<n); i++)
2105 if (c[i] < e_1)
2106 break;
2107 if (i < n)
2108 fitparm[0] = t[i];
2109 else
2110 fitparm[0] = 10;
2111 fitparm[1] = 0.95;
2112 do_lmfit(n,c,sigma_c,0,t,start,t[n-1],oenv,bDebugMode(),effnEXP2,fitparm,0);
2115 void analyse_corr(int n,real t[],real ct[],real nt[],real kt[],
2116 real sigma_ct[],real sigma_nt[],real sigma_kt[],
2117 real fit_start,real temp,real smooth_tail_start,
2118 const output_env_t oenv)
2120 int i0,i;
2121 real k=1,kp=1,kow=1;
2122 real Q=0,chi22,chi2,dg,dgp,tau_hb,dtau,tau_rlx,e_1,dt,sigma_k,sigma_kp,ddg;
2123 double tmp,sn2=0,sc2=0,sk2=0,scn=0,sck=0,snk=0;
2124 bool bError = (sigma_ct != NULL) && (sigma_nt != NULL) && (sigma_kt != NULL);
2126 if (smooth_tail_start >= 0) {
2127 smooth_tail(n,t,ct,sigma_ct,smooth_tail_start,oenv);
2128 smooth_tail(n,t,nt,sigma_nt,smooth_tail_start,oenv);
2129 smooth_tail(n,t,kt,sigma_kt,smooth_tail_start,oenv);
2131 for(i0=0; (i0<n-2) && ((t[i0]-t[0]) < fit_start); i0++)
2133 if (i0 < n-2) {
2134 for(i=i0; (i<n); i++) {
2135 sc2 += sqr(ct[i]);
2136 sn2 += sqr(nt[i]);
2137 sk2 += sqr(kt[i]);
2138 sck += ct[i]*kt[i];
2139 snk += nt[i]*kt[i];
2140 scn += ct[i]*nt[i];
2142 printf("Hydrogen bond thermodynamics at T = %g K\n",temp);
2143 tmp = (sn2*sc2-sqr(scn));
2144 if ((tmp > 0) && (sn2 > 0)) {
2145 k = (sn2*sck-scn*snk)/tmp;
2146 kp = (k*scn-snk)/sn2;
2147 if (bError) {
2148 chi2 = 0;
2149 for(i=i0; (i<n); i++) {
2150 chi2 += sqr(k*ct[i]-kp*nt[i]-kt[i]);
2152 chi22 = compute_weighted_rates(n,t,ct,nt,kt,sigma_ct,sigma_nt,
2153 sigma_kt,&k,&kp,
2154 &sigma_k,&sigma_kp,fit_start);
2155 Q = quality_of_fit(chi2,2);
2156 ddg = BOLTZ*temp*sigma_k/k;
2157 printf("Fitting paramaters chi^2 = %10g, Quality of fit = %10g\n",
2158 chi2,Q);
2159 printf("The Rate and Delta G are followed by an error estimate\n");
2160 printf("----------------------------------------------------------\n"
2161 "Type Rate (1/ps) Sigma Time (ps) DG (kJ/mol) Sigma\n");
2162 printf("Forward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
2163 k,sigma_k,1/k,calc_dg(1/k,temp),ddg);
2164 ddg = BOLTZ*temp*sigma_kp/kp;
2165 printf("Backward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
2166 kp,sigma_kp,1/kp,calc_dg(1/kp,temp),ddg);
2168 else {
2169 chi2 = 0;
2170 for(i=i0; (i<n); i++) {
2171 chi2 += sqr(k*ct[i]-kp*nt[i]-kt[i]);
2173 printf("Fitting parameters chi^2 = %10g\nQ = %10g\n",
2174 chi2,Q);
2175 printf("--------------------------------------------------\n"
2176 "Type Rate (1/ps) Time (ps) DG (kJ/mol) Chi^2\n");
2177 printf("Forward %10.3f %8.3f %10.3f %10g\n",
2178 k,1/k,calc_dg(1/k,temp),chi2);
2179 printf("Backward %10.3f %8.3f %10.3f\n",
2180 kp,1/kp,calc_dg(1/kp,temp));
2183 if (sc2 > 0) {
2184 kow = 2*sck/sc2;
2185 printf("One-way %10.3f %s%8.3f %10.3f\n",
2186 kow,bError ? " " : "",1/kow,calc_dg(1/kow,temp));
2188 else
2189 printf(" - Numerical problems computing HB thermodynamics:\n"
2190 "sc2 = %g sn2 = %g sk2 = %g sck = %g snk = %g scn = %g\n",
2191 sc2,sn2,sk2,sck,snk,scn);
2192 /* Determine integral of the correlation function */
2193 tau_hb = evaluate_integral(n,t,ct,NULL,(t[n-1]-t[0])/2,&dtau);
2194 printf("Integral %10.3f %s%8.3f %10.3f\n",1/tau_hb,
2195 bError ? " " : "",tau_hb,calc_dg(tau_hb,temp));
2196 e_1 = exp(-1);
2197 for(i=0; (i<n-2); i++) {
2198 if ((ct[i] > e_1) && (ct[i+1] <= e_1)) {
2199 break;
2202 if (i < n-2) {
2203 /* Determine tau_relax from linear interpolation */
2204 tau_rlx = t[i]-t[0] + (e_1-ct[i])*(t[i+1]-t[i])/(ct[i+1]-ct[i]);
2205 printf("Relaxation %10.3f %8.3f %s%10.3f\n",1/tau_rlx,
2206 tau_rlx,bError ? " " : "",
2207 calc_dg(tau_rlx,temp));
2210 else
2211 printf("Correlation functions too short to compute thermodynamics\n");
2214 void compute_derivative(int nn,real x[],real y[],real dydx[])
2216 int j;
2218 /* Compute k(t) = dc(t)/dt */
2219 for(j=1; (j<nn-1); j++)
2220 dydx[j] = (y[j+1]-y[j-1])/(x[j+1]-x[j-1]);
2221 /* Extrapolate endpoints */
2222 dydx[0] = 2*dydx[1] - dydx[2];
2223 dydx[nn-1] = 2*dydx[nn-2] - dydx[nn-3];
2226 static void parallel_print(int *data, int nThreads)
2228 /* This prints the donors on which each tread is currently working. */
2229 int i;
2231 fprintf(stderr, "\r");
2232 for (i=0; i<nThreads; i++)
2233 fprintf(stderr, "%-7i",data[i]);
2236 static void normalizeACF(real *ct, real *gt, int len)
2238 real ct_fac, gt_fac;
2239 int i;
2241 /* Xu and Berne use the same normalization constant */
2243 ct_fac = 1.0/ct[0];
2244 gt_fac = (gt!=NULL && gt[0]!=0) ? 1.0/gt[0] : 0;
2245 printf("Normalization for c(t) = %g for gh(t) = %g\n",ct_fac,gt_fac);
2246 for (i=0; i<len; i++)
2248 ct[i] *= ct_fac;
2249 if (gt != NULL)
2250 gt[i] *= gt_fac;
2254 /* Added argument bContact in do_hbac(...). Also
2255 * added support for -contact in the actual code.
2256 * - Erik Marklund, May 31, 2006 */
2257 /* Changed contact code and added argument R2
2258 * - Erik Marklund, June 29, 2006
2260 static void do_hbac(const char *fn,t_hbdata *hb,real aver_nhb,real aver_dist,
2261 int nDump,bool bMerge,bool bContact, real fit_start,
2262 real temp,bool R2,real smooth_tail_start, const output_env_t oenv,
2263 t_gemParams *params, const char *gemType, int nThreads,
2264 const int NN, const bool bBallistic, const bool bGemFit)
2266 FILE *fp;
2267 int i,j,k,m,n,o,nd,ihb,idist,n2,nn,iter,nSets;
2268 static char *legNN[] = { "Ac(t)",
2269 "Ac'(t)"};
2270 static char **legGem;
2272 static char *legLuzar[] = { "Ac\\sfin sys\\v{}\\z{}(t)",
2273 "Ac(t)",
2274 "Cc\\scontact,hb\\v{}\\z{}(t)",
2275 "-dAc\\sfs\\v{}\\z{}/dt" };
2276 bool bNorm=FALSE;
2277 double nhb = 0;
2278 int nhbi=0;
2279 real *rhbex=NULL,*ht,*gt,*ght,*dght,*kt;
2280 real *ct,*p_ct,tail,tail2,dtail,ct_fac,ght_fac,*cct;
2281 const real tol = 1e-3;
2282 int nframes = hb->nframes,nf;
2283 unsigned int **h,**g;
2284 int nh,nhbonds,nhydro,ngh;
2285 t_hbond *hbh;
2286 PSTYPE p, *pfound = NULL, np;
2287 t_pShift *pHist;
2288 int *ptimes=NULL, *poff=NULL, anhb, n0, mMax=INT_MIN;
2289 real **rHbExGem = NULL;
2290 bool c;
2291 int acType;
2292 t_E *E;
2293 double *ctdouble, *timedouble, *fittedct;
2294 double fittolerance=0.1;
2296 enum {AC_NONE, AC_NN, AC_GEM, AC_LUZAR};
2299 #ifdef HAVE_OPENMP
2300 int *dondata=NULL, thisThread;
2301 #endif
2304 printf("Doing autocorrelation ");
2306 /* Decide what kind of ACF calculations to do. */
2307 if (NN > NN_NONE && NN < NN_NR) {
2308 #ifdef HAVE_NN_LOOPS
2309 acType = AC_NN;
2310 printf("using the energy estimate.\n");
2311 #else
2312 acType = AC_NONE;
2313 printf("Can't do the NN-loop. Yet.\n");
2314 #endif
2315 } else if (hb->bGem) {
2316 acType = AC_GEM;
2317 printf("according to the reversible geminate recombination model by Omer Markowitch.\n");
2319 nSets = 1 + (bBallistic ? 1:0) + (bGemFit ? 1:0);
2320 snew(legGem, nSets);
2321 for (i=0;i<nSets;i++)
2322 snew(legGem[i], 128);
2323 sprintf(legGem[0], "Ac\\s%s\\v{}\\z{}(t)", gemType);
2324 if (bBallistic)
2325 sprintf(legGem[1], "Ac'(t)");
2326 if (bGemFit)
2327 sprintf(legGem[(bBallistic ? 3:2)], "Ac\\s%s,fit\\v{}\\z{}(t)", gemType);
2329 } else {
2330 acType = AC_LUZAR;
2331 printf("according to the theory of Luzar and Chandler.\n");
2333 fflush(stdout);
2335 /* build hbexist matrix in reals for autocorr */
2336 /* Allocate memory for computing ACF (rhbex) and aggregating the ACF (ct) */
2337 n2=1;
2338 while (n2 < nframes)
2339 n2*=2;
2341 nn = nframes/2;
2343 if (acType != AC_NN ||
2344 #ifndef HAVE_OPENMP
2345 TRUE
2346 #else
2347 FALSE
2348 #endif
2350 snew(h,hb->maxhydro);
2351 snew(g,hb->maxhydro);
2354 /* Dump hbonds for debugging */
2355 dump_ac(hb,bMerge||bContact,nDump);
2357 /* Total number of hbonds analyzed here */
2358 nhbonds = 0;
2359 ngh = 0;
2360 anhb = 0;
2362 /* ------------------------------------------------
2363 * I got tired of waiting for the acf calculations
2364 * and parallelized it with openMP
2365 * set environment variable CFLAGS = "-fopenmp" when running
2366 * configure and define DOUSEOPENMP to make use of it.
2369 #ifdef HAVE_OPENMP /* ================================================= \
2370 * Set up the OpenMP stuff, |
2371 * like the number of threads and such |
2373 if (acType != AC_LUZAR)
2375 #if (_OPENMP >= 200805) /* =====================\ */
2376 nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_thread_limit());
2377 #else
2378 nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_num_procs());
2379 #endif /* _OPENMP >= 200805 ====================/ */
2381 omp_set_num_threads(nThreads);
2382 snew(dondata, nThreads);
2383 for (i=0; i<nThreads; i++)
2384 dondata[i] = -1;
2385 printf("ACF calculations parallelized with OpenMP using %i threads.\n"
2386 "Expect close to linear scaling over this donor-loop.\n", nThreads);
2387 fflush(stdout);
2388 fprintf(stderr, "Donors: [thread no]\n");
2390 char tmpstr[7];
2391 for (i=0;i<nThreads;i++) {
2392 snprintf(tmpstr, 7, "[%i]", i);
2393 fprintf(stderr, "%-7s", tmpstr);
2396 fprintf(stderr, "\n"); /* | */
2397 } /* | */
2398 #endif /* HAVE_OPENMP ===================================================/ */
2401 /* Build the ACF according to acType */
2402 switch (acType)
2405 case AC_NN:
2406 #ifdef HAVE_NN_LOOPS
2407 /* Here we're using the estimated energy for the hydrogen bonds. */
2408 snew(ct,nn);
2409 #ifdef HAVE_OPENMP /* ==================================\ */
2410 #pragma omp parallel \
2411 private(i, j, k, nh, E, rhbex, thisThread), \
2412 default(shared)
2414 #pragma omp barrier
2415 thisThread = omp_get_thread_num();
2416 rhbex = NULL;
2417 #endif /* ==============================================/ */
2419 snew(rhbex, n2);
2420 memset(rhbex, 0, n2*sizeof(real)); /* Trust no-one, not even malloc()! */
2422 #ifdef HAVE_OPENMP /* ################################################## \
2426 #pragma omp barrier
2427 #pragma omp for schedule (dynamic)
2428 #endif
2429 for (i=0; i<hb->d.nrd; i++) /* loop over donors */
2431 #ifdef HAVE_OPENMP /* ====== Write some output ======\ */
2432 #pragma omp critical
2434 dondata[thisThread] = i;
2435 parallel_print(dondata, nThreads);
2437 #else
2438 fprintf(stderr, "\r %i", i);
2439 #endif /* ===========================================/ */
2441 for (j=0; j<hb->a.nra; j++) /* loop over acceptors */
2443 for (nh=0; nh<hb->d.nhydro[i]; nh++) /* loop over donors' hydrogens */
2445 E = hb->hbE.E[i][j][nh];
2446 if (E != NULL)
2448 for (k=0; k<nframes; k++)
2450 if (E[k] != NONSENSE_E)
2451 rhbex[k] = (real)E[k];
2454 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&(rhbex),hb->time[1]-hb->time[0],
2455 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2456 #ifdef HAVE_OPENMP
2457 #pragma omp critical
2458 #endif
2460 for(k=0; (k<nn); k++)
2461 ct[k] += rhbex[k];
2464 } /* k loop */
2465 } /* j loop */
2466 } /* i loop */
2467 sfree(rhbex);
2468 #pragma omp barrier
2469 #ifdef HAVE_OPENMP
2470 /* # */
2471 } /* End of parallel block # */
2472 /* ##############################################################/ */
2473 sfree(dondata);
2474 #endif
2475 normalizeACF(ct, NULL, nn);
2476 snew(ctdouble, nn);
2477 snew(timedouble, nn);
2478 for (j=0; j<nn; j++)
2480 timedouble[j]=(double)(hb->time[j]);
2481 ctdouble[j]=(double)(ct[j]);
2484 /* Remove ballistic term */
2485 if (params->ballistic/params->tDelta >= params->nExpFit*2+1)
2486 takeAwayBallistic(ctdouble, timedouble, nn, params->ballistic, params->nExpFit, params->bDt);
2487 else
2488 printf("\nNumber of data points is less than the number of parameters to fit\n."
2489 "The system is underdetermined, hence no ballistic term can be found.\n\n");
2491 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation","Time (ps)","C(t)");
2492 xvgr_legend(fp,asize(legNN),legNN);
2494 for(j=0; (j<nn); j++)
2495 fprintf(fp,"%10g %10g %10g\n",
2496 hb->time[j]-hb->time[0],
2497 ct[j],
2498 ctdouble[j]);
2499 fclose(fp);
2500 sfree(ct);
2501 sfree(ctdouble);
2502 sfree(timedouble);
2503 #endif /* HAVE_NN_LOOPS */
2504 break; /* case AC_NN */
2506 case AC_GEM:
2507 snew(ct,2*n2);
2508 memset(ct,0,2*n2*sizeof(real));
2509 #ifndef HAVE_OPENMP
2510 fprintf(stderr, "Donor:\n");
2511 #define __ACDATA ct
2512 #else
2513 #define __ACDATA p_ct
2514 #endif
2516 #ifdef HAVE_OPENMP /* =========================================\
2517 * */
2518 #pragma omp parallel default(none) \
2519 private(i, k, nh, hbh, pHist, h, g, n0, nf, np, j, m, \
2520 pfound, poff, rHbExGem, p, ihb, mMax, \
2521 thisThread, p_ct) \
2522 shared(hb, dondata, ct, nn, nThreads, n2, stderr, bNorm, \
2523 nframes, bMerge, bContact)
2524 { /* ########## THE START OF THE ENORMOUS PARALLELIZED BLOCK! ########## */
2525 h = NULL;
2526 g = NULL;
2527 thisThread = omp_get_thread_num();
2528 snew(h,hb->maxhydro);
2529 snew(g,hb->maxhydro);
2530 mMax = INT_MIN;
2531 rHbExGem = NULL;
2532 poff = NULL;
2533 pfound = NULL;
2534 p_ct = NULL;
2535 snew(p_ct,2*n2);
2536 memset(p_ct,0,2*n2*sizeof(real));
2538 /* I'm using a chunk size of 1, since I expect \
2539 * the overhead to be really small compared \
2540 * to the actual calculations \ */
2541 #pragma omp for schedule(dynamic,1) nowait /* \ */
2542 #endif /* HAVE_OPENMP =========================================/ */
2544 for (i=0; i<hb->d.nrd; i++) {
2545 #ifdef HAVE_OPENMP
2546 #pragma omp critical
2548 dondata[thisThread] = i;
2549 parallel_print(dondata, nThreads);
2551 #else
2552 fprintf(stderr, "\r %i", i);
2553 #endif
2555 for (k=0; k<hb->a.nra; k++) {
2556 for (nh=0; nh < ((bMerge || bContact) ? 1 : hb->d.nhydro[i]); nh++) {
2557 hbh = hb->hbmap[i][k];
2558 if (hbh) {
2559 /* Note that if hb->per->gemtype==gemDD, then distances will be stored in
2560 * hb->hbmap[d][a].h array anyway, because the contact flag will be set.
2561 * hence, it's only with the gemAD mode that hb->hbmap[d][a].g will be used. */
2562 pHist = &(hb->per->pHist[i][k]);
2563 if (ISHB(hbh->history[nh]) && pHist->len != 0) {
2565 /* No need for a critical section */
2566 /* #ifdef HAVE_OPENMP */
2567 /* #pragma omp critical */
2568 /* #endif */
2570 h[nh] = hbh->h[nh];
2571 g[nh] = hb->per->gemtype==gemAD ? hbh->g[nh] : NULL;
2573 n0 = hbh->n0;
2574 nf = hbh->nframes;
2575 /* count the number of periodic shifts encountered and store
2576 * them in separate arrays. */
2577 np = 0;
2578 for (j=0; j<pHist->len; j++)
2580 p = pHist->p[j];
2581 for (m=0; m<=np; m++) {
2582 if (m == np) { /* p not recognized in list. Add it and set up new array. */
2583 np++;
2584 if (np>hb->per->nper)
2585 gmx_fatal(FARGS, "Too many pshifts. Something's utterly wrong here.");
2586 if (m>=mMax) { /* Extend the arrays.
2587 * Doing it like this, using mMax to keep track of the sizes,
2588 * eleviates the need for freeing and re-allocating the arrays
2589 * when taking on the next donor-acceptor pair */
2590 mMax = m;
2591 srenew(pfound,np); /* The list of found periodic shifts. */
2592 srenew(rHbExGem,np); /* The hb existence functions (-aver_hb). */
2593 snew(rHbExGem[m],2*n2);
2594 srenew(poff,np);
2597 /* This shouldn't have to be critical, right? */
2598 /* #ifdef HAVE_OPENMP */
2599 /* #pragma omp critical */
2600 /* #endif */
2602 if (rHbExGem != NULL && rHbExGem[m] != NULL) {
2603 /* This must be done, as this array was most likey
2604 * used to store stuff in some previous iteration. */
2605 memset(rHbExGem[m], 0, (sizeof(real)) * (2*n2));
2607 else
2608 fprintf(stderr, "rHbExGem not initialized! m = %i\n", m);
2610 pfound[m] = p;
2611 poff[m] = -1;
2613 break;
2614 } /* m==np */
2615 if (p == pfound[m])
2616 break;
2617 } /* m: Loop over found shifts */
2618 } /* j: Loop over shifts */
2620 /* Now unpack and disentangle the existence funtions. */
2621 for (j=0; j<nf; j++) {
2622 /* i: donor,
2623 * k: acceptor
2624 * nh: hydrogen
2625 * j: time
2626 * p: periodic shift
2627 * pfound: list of periodic shifts found for this pair.
2628 * poff: list of frame offsets; that is, the first
2629 * frame a hbond has a particular periodic shift. */
2630 p = getPshift(*pHist, j+n0);
2631 if (p != -1)
2633 for (m=0; m<np; m++)
2635 if (pfound[m] == p)
2636 break;
2637 if (m==(np-1))
2638 gmx_fatal(FARGS,"Shift not found, but must be there.");
2641 ihb = is_hb(h[nh],j) || ((hb->per->gemtype!=gemAD || j==0) ? FALSE : is_hb(g[nh],j));
2642 if (ihb)
2644 if (poff[m] == -1)
2645 poff[m] = j; /* Here's where the first hbond with shift p is,
2646 * relative to the start of h[0].*/
2647 if (j<poff[m])
2648 gmx_fatal(FARGS, "j<poff[m]");
2649 rHbExGem[m][j-poff[m]] += 1;
2654 /* Now, build ac. */
2655 for (m=0; m<np; m++) {
2656 if (rHbExGem[m][0]>0 && n0+poff[m]<nn/* && m==0 */) {
2657 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&(rHbExGem[m]),hb->time[1]-hb->time[0],
2658 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2659 for(j=0; (j<nn); j++)
2660 __ACDATA[j] += rHbExGem[m][j];
2662 } /* Building of ac. */
2663 } /* if (ISHB(...*/
2664 } /* if (hbh) */
2665 } /* hydrogen loop */
2666 } /* acceptor loop */
2667 } /* donor loop */
2669 for (m=0; m<=mMax; m++) {
2670 sfree(rHbExGem[m]);
2672 sfree(pfound);
2673 sfree(poff);
2674 sfree(rHbExGem);
2676 sfree(h);
2677 sfree(g);
2678 #ifdef HAVE_OPENMP /* =======================================\ */
2679 #pragma omp critical
2681 for (i=0; i<nn; i++)
2682 ct[i] += p_ct[i];
2684 sfree(p_ct);
2686 } /* ########## THE END OF THE ENORMOUS PARALLELIZED BLOCK ########## */
2687 sfree(dondata);
2688 #endif /* HAVE_OPENMP =======================================/ */
2690 normalizeACF(ct, NULL, nn);
2692 fprintf(stderr, "\n\nACF successfully calculated.\n");
2694 /* Use this part to fit to geminate recombination - JCP 129, 84505 (2008) */
2696 snew(ctdouble, nn);
2697 snew(timedouble, nn);
2698 snew(fittedct, nn);
2700 for (j=0;j<nn;j++){
2701 timedouble[j]=(double)(hb->time[j]);
2702 ctdouble[j]=(double)(ct[j]);
2705 /* Remove ballistic term */
2706 if (bBallistic) {
2707 if (params->ballistic/params->tDelta >= params->nExpFit*2+1)
2708 takeAwayBallistic(ctdouble, timedouble, nn, params->ballistic, params->nExpFit, params->bDt);
2709 else
2710 printf("\nNumber of data points is less than the number of parameters to fit\n."
2711 "The system is underdetermined, hence no ballistic term can be found.\n\n");
2713 if (bGemFit)
2714 fitGemRecomb(ctdouble, timedouble, &fittedct, nn, params);
2717 if (bContact)
2718 fp = xvgropen(fn, "Contact Autocorrelation","Time (ps)","C(t)",oenv);
2719 else
2720 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation","Time (ps)","C(t)",oenv);
2721 xvgr_legend(fp,asize(legGem),legGem,oenv);
2723 for(j=0; (j<nn); j++)
2725 fprintf(fp, "%10g %10g", hb->time[j]-hb->time[0],ct[j]);
2726 if (bBallistic)
2727 fprintf(fp," %10g", ctdouble[j]);
2728 if (bGemFit)
2729 fprintf(fp," %10g", fittedct[j]);
2730 fprintf(fp,"\n");
2732 fclose(fp);
2734 sfree(ctdouble);
2735 sfree(timedouble);
2736 sfree(fittedct);
2737 sfree(ct);
2739 break; /* case AC_GEM */
2741 case AC_LUZAR:
2742 snew(rhbex,2*n2);
2743 snew(ct,2*n2);
2744 snew(gt,2*n2);
2745 snew(ht,2*n2);
2746 snew(ght,2*n2);
2747 snew(dght,2*n2);
2749 snew(kt,nn);
2750 snew(cct,nn);
2752 for(i=0; (i<hb->d.nrd); i++) {
2753 for(k=0; (k<hb->a.nra); k++) {
2754 nhydro = 0;
2755 hbh = hb->hbmap[i][k];
2757 if (hbh) {
2758 if (bMerge || bContact) {
2759 if (ISHB(hbh->history[0])) {
2760 h[0] = hbh->h[0];
2761 g[0] = hbh->g[0];
2762 nhydro = 1;
2765 else {
2766 for(m=0; (m<hb->maxhydro); m++)
2767 if (bContact ? ISDIST(hbh->history[m]) : ISHB(hbh->history[m])) {
2768 g[nhydro] = hbh->g[m];
2769 h[nhydro] = hbh->h[m];
2770 nhydro++;
2774 nf = hbh->nframes;
2775 for(nh=0; (nh<nhydro); nh++) {
2776 int nrint = bContact ? hb->nrdist : hb->nrhb;
2777 if ((((nhbonds+1) % 10) == 0) || (nhbonds+1 == nrint))
2778 fprintf(stderr,"\rACF %d/%d",nhbonds+1,nrint);
2779 nhbonds++;
2780 for(j=0; (j<nframes); j++) {
2781 /* Changed '<' into '<=' below, just like I did in
2782 the hbm-output-loop in the gmx_hbond() block.
2783 - Erik Marklund, May 31, 2006 */
2784 if (j <= nf) {
2785 ihb = is_hb(h[nh],j);
2786 idist = is_hb(g[nh],j);
2788 else {
2789 ihb = idist = 0;
2791 rhbex[j] = ihb-aver_nhb;
2792 /* For contacts: if a second cut-off is provided, use it,
2793 * otherwise use g(t) = 1-h(t) */
2794 if (!R2 && bContact)
2795 gt[j] = 1-ihb;
2796 else
2797 gt[j] = idist*(1-ihb);
2798 ht[j] = rhbex[j];
2799 nhb += ihb;
2803 /* The autocorrelation function is normalized after summation only */
2804 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&rhbex,hb->time[1]-hb->time[0],
2805 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2807 /* Cross correlation analysis for thermodynamics */
2808 for(j=nframes; (j<n2); j++) {
2809 ht[j] = 0;
2810 gt[j] = 0;
2813 cross_corr(n2,ht,gt,dght);
2815 for(j=0; (j<nn); j++) {
2816 ct[j] += rhbex[j];
2817 ght[j] += dght[j];
2823 fprintf(stderr,"\n");
2824 sfree(h);
2825 sfree(g);
2826 normalizeACF(ct, gt, nn);
2828 /* Determine tail value for statistics */
2829 tail = 0;
2830 tail2 = 0;
2831 for(j=nn/2; (j<nn); j++) {
2832 tail += ct[j];
2833 tail2 += ct[j]*ct[j];
2835 tail /= (nn - nn/2);
2836 tail2 /= (nn - nn/2);
2837 dtail = sqrt(tail2-tail*tail);
2839 /* Check whether the ACF is long enough */
2840 if (dtail > tol) {
2841 printf("\nWARNING: Correlation function is probably not long enough\n"
2842 "because the standard deviation in the tail of C(t) > %g\n"
2843 "Tail value (average C(t) over second half of acf): %g +/- %g\n",
2844 tol,tail,dtail);
2846 for(j=0; (j<nn); j++) {
2847 cct[j] = ct[j];
2848 ct[j] = (cct[j]-tail)/(1-tail);
2850 /* Compute negative derivative k(t) = -dc(t)/dt */
2851 compute_derivative(nn,hb->time,ct,kt);
2852 for(j=0; (j<nn); j++)
2853 kt[j] = -kt[j];
2856 if (bContact)
2857 fp = xvgropen(fn, "Contact Autocorrelation","Time (ps)","C(t)",oenv);
2858 else
2859 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation","Time (ps)","C(t)",oenv);
2860 xvgr_legend(fp,asize(legLuzar),legLuzar, oenv);
2863 for(j=0; (j<nn); j++)
2864 fprintf(fp,"%10g %10g %10g %10g %10g\n",
2865 hb->time[j]-hb->time[0],ct[j],cct[j],ght[j],kt[j]);
2866 ffclose(fp);
2868 analyse_corr(nn,hb->time,ct,ght,kt,NULL,NULL,NULL,
2869 fit_start,temp,smooth_tail_start,oenv);
2871 do_view(oenv,fn,NULL);
2872 sfree(rhbex);
2873 sfree(ct);
2874 sfree(gt);
2875 sfree(ht);
2876 sfree(ght);
2877 sfree(dght);
2878 sfree(cct);
2879 sfree(kt);
2880 /* sfree(h); */
2881 /* sfree(g); */
2883 break; /* case AC_LUZAR */
2885 default:
2886 gmx_fatal(FARGS, "Unrecognized type of ACF-calulation. acType = %i.", acType);
2887 } /* switch (acType) */
2890 static void init_hbframe(t_hbdata *hb,int nframes,real t)
2892 int i,j,m;
2894 hb->time[nframes] = t;
2895 hb->nhb[nframes] = 0;
2896 hb->ndist[nframes] = 0;
2897 for (i=0; (i<max_hx); i++)
2898 hb->nhx[nframes][i]=0;
2899 /* Loop invalidated */
2900 if (hb->bHBmap && 0)
2901 for (i=0; (i<hb->d.nrd); i++)
2902 for (j=0; (j<hb->a.nra); j++)
2903 for (m=0; (m<hb->maxhydro); m++)
2904 if (hb->hbmap[i][j] && hb->hbmap[i][j]->h[m])
2905 set_hb(hb,i,m,j,nframes,HB_NO);
2906 /*set_hb(hb->hbmap[i][j]->h[m],nframes-hb->hbmap[i][j]->n0,HB_NO);*/
2909 static void analyse_donor_props(const char *fn,t_hbdata *hb,int nframes,real t,
2910 const output_env_t oenv)
2912 static FILE *fp = NULL;
2913 char *leg[] = { "Nbound", "Nfree" };
2914 int i,j,k,nbound,nb,nhtot;
2916 if (!fn)
2917 return;
2918 if (!fp) {
2919 fp = xvgropen(fn,"Donor properties","Time (ps)","Number",oenv);
2920 xvgr_legend(fp,asize(leg),leg,oenv);
2922 nbound = 0;
2923 nhtot = 0;
2924 for(i=0; (i<hb->d.nrd); i++) {
2925 for(k=0; (k<hb->d.nhydro[i]); k++) {
2926 nb = 0;
2927 nhtot ++;
2928 for(j=0; (j<hb->a.nra) && (nb == 0); j++) {
2929 if (hb->hbmap[i][j] && hb->hbmap[i][j]->h[k] &&
2930 is_hb(hb->hbmap[i][j]->h[k],nframes))
2931 nb = 1;
2933 nbound += nb;
2936 fprintf(fp,"%10.3e %6d %6d\n",t,nbound,nhtot-nbound);
2939 static void dump_hbmap(t_hbdata *hb,
2940 int nfile,t_filenm fnm[],bool bTwo,bool bInsert,
2941 bool bContact, int isize[],int *index[],char *grpnames[],
2942 t_atoms *atoms)
2944 FILE *fp,*fplog;
2945 int ddd,hhh,aaa,i,j,k,m,grp;
2946 char ds[32],hs[32],as[32];
2947 bool first;
2949 fp = opt2FILE("-hbn",nfile,fnm,"w");
2950 if (opt2bSet("-g",nfile,fnm)) {
2951 fplog = ffopen(opt2fn("-g",nfile,fnm),"w");
2952 if (bContact)
2953 fprintf(fplog,"# %10s %12s %12s\n","Donor","Hydrogen","Acceptor");
2954 else
2955 fprintf(fplog,"# %10s %12s %12s\n","Donor","Hydrogen","Acceptor");
2957 else
2958 fplog = NULL;
2959 for (grp=gr0; grp<=(bTwo?gr1:gr0); grp++) {
2960 fprintf(fp,"[ %s ]",grpnames[grp]);
2961 for (i=0; i<isize[grp]; i++) {
2962 fprintf(fp,(i%15)?" ":"\n");
2963 fprintf(fp," %4u",index[grp][i]+1);
2965 fprintf(fp,"\n");
2967 Added -contact support below.
2968 - Erik Marklund, May 29, 2006
2970 if (!bContact) {
2971 fprintf(fp,"[ donors_hydrogens_%s ]\n",grpnames[grp]);
2972 for (i=0; (i<hb->d.nrd); i++) {
2973 if (hb->d.grp[i] == grp) {
2974 for(j=0; (j<hb->d.nhydro[i]); j++)
2975 fprintf(fp," %4u %4u",hb->d.don[i]+1,
2976 hb->d.hydro[i][j]+1);
2977 fprintf(fp,"\n");
2980 first = TRUE;
2981 fprintf(fp,"[ acceptors_%s ]",grpnames[grp]);
2982 for (i=0; (i<hb->a.nra); i++) {
2983 if (hb->a.grp[i] == grp) {
2984 fprintf(fp,(i%15 && !first)?" ":"\n");
2985 fprintf(fp," %4u",hb->a.acc[i]+1);
2986 first = FALSE;
2989 fprintf(fp,"\n");
2992 if (bTwo)
2993 fprintf(fp,bContact ? "[ contacts_%s-%s ]\n" :
2994 "[ hbonds_%s-%s ]\n",grpnames[0],grpnames[1]);
2995 else
2996 fprintf(fp,bContact ? "[ contacts_%s ]" : "[ hbonds_%s ]\n",grpnames[0]);
2998 for(i=0; (i<hb->d.nrd); i++) {
2999 ddd = hb->d.don[i];
3000 for(k=0; (k<hb->a.nra); k++) {
3001 aaa = hb->a.acc[k];
3002 for(m=0; (m<hb->d.nhydro[i]); m++) {
3003 if (hb->hbmap[i][k] && ISHB(hb->hbmap[i][k]->history[m])) {
3004 sprintf(ds,"%s",mkatomname(atoms,ddd));
3005 sprintf(as,"%s",mkatomname(atoms,aaa));
3006 if (bContact) {
3007 fprintf(fp," %6u %6u\n",ddd+1,aaa+1);
3008 if (fplog)
3009 fprintf(fplog,"%12s %12s\n",ds,as);
3010 } else {
3011 hhh = hb->d.hydro[i][m];
3012 sprintf(hs,"%s",mkatomname(atoms,hhh));
3013 fprintf(fp," %6u %6u %6u\n",ddd+1,hhh+1,aaa+1);
3014 if (fplog)
3015 fprintf(fplog,"%12s %12s %12s\n",ds,hs,as);
3021 if (bInsert) {
3022 if (bTwo)
3023 fprintf(fp,"[ insert_%s->%s-%s ]",
3024 grpnames[2],grpnames[0],grpnames[1]);
3025 else
3026 fprintf(fp,"[ insert_%s->%s ]",grpnames[2],grpnames[0]);
3027 j=0;
3029 /* for(i=0; (i<hb->nrhb); i++) {
3030 if (hb->hb[i].bInsert) {
3031 fprintf(fp,(j%15)?" ":"\n");
3032 fprintf(fp,"%4d",i+1);
3033 j++;
3036 fprintf(fp,"\n");
3038 ffclose(fp);
3039 if (fplog)
3040 ffclose(fplog);
3043 #ifdef HAVE_OPENMP
3044 /* sync_hbdata() updates the parallel t_hbdata p_hb using hb as template.
3045 * It mimics add_frames() and init_frame() to some extent. */
3046 static void sync_hbdata(t_hbdata *hb, t_hbdata *p_hb,
3047 int nframes, real t)
3049 int i;
3050 if (nframes >= p_hb->max_frames)
3052 p_hb->max_frames += 4096;
3053 srenew(p_hb->nhb, p_hb->max_frames);
3054 srenew(p_hb->ndist, p_hb->max_frames);
3055 srenew(p_hb->n_bound, p_hb->max_frames);
3056 srenew(p_hb->nhx, p_hb->max_frames);
3057 if (p_hb->bDAnr)
3058 srenew(p_hb->danr, p_hb->max_frames);
3059 memset(&(p_hb->nhb[nframes]), 0, sizeof(int) * (p_hb->max_frames-nframes));
3060 memset(&(p_hb->ndist[nframes]), 0, sizeof(int) * (p_hb->max_frames-nframes));
3061 p_hb->nhb[nframes] = 0;
3062 p_hb->ndist[nframes] = 0;
3065 p_hb->nframes = nframes;
3066 /* for (i=0;) */
3067 /* { */
3068 /* p_hb->nhx[nframes][i] */
3069 /* } */
3070 memset(&(p_hb->nhx[nframes]), 0 ,sizeof(int)*max_hx); /* zero the helix count for this frame */
3072 /* hb->per will remain constant througout the frame loop,
3073 * even though the data its members point to will change,
3074 * hence no need for re-syncing. */
3076 #endif
3078 int gmx_hbond(int argc,char *argv[])
3080 const char *desc[] = {
3081 "g_hbond computes and analyzes hydrogen bonds. Hydrogen bonds are",
3082 "determined based on cutoffs for the angle Acceptor - Donor - Hydrogen",
3083 "(zero is extended) and the distance Hydrogen - Acceptor.",
3084 "OH and NH groups are regarded as donors, O is an acceptor always,",
3085 "N is an acceptor by default, but this can be switched using",
3086 "[TT]-nitacc[tt]. Dummy hydrogen atoms are assumed to be connected",
3087 "to the first preceding non-hydrogen atom.[PAR]",
3089 "You need to specify two groups for analysis, which must be either",
3090 "identical or non-overlapping. All hydrogen bonds between the two",
3091 "groups are analyzed.[PAR]",
3093 "If you set -shell, you will be asked for an additional index group",
3094 "which should contain exactly one atom. In this case, only hydrogen",
3095 "bonds between atoms within the shell distance from the one atom are",
3096 "considered.[PAR]",
3098 /* "It is also possible to analyse specific hydrogen bonds with",
3099 "[TT]-sel[tt]. This index file must contain a group of atom triplets",
3100 "Donor Hydrogen Acceptor, in the following way:[PAR]",
3102 "[TT]",
3103 "[ selected ][BR]",
3104 " 20 21 24[BR]",
3105 " 25 26 29[BR]",
3106 " 1 3 6[BR]",
3107 "[tt][BR]",
3108 "Note that the triplets need not be on separate lines.",
3109 "Each atom triplet specifies a hydrogen bond to be analyzed,",
3110 "note also that no check is made for the types of atoms.[PAR]",
3112 "[TT]-ins[tt] turns on computing solvent insertion into hydrogen bonds.",
3113 "In this case an additional group must be selected, specifying the",
3114 "solvent molecules.[PAR]",
3116 "[BB]Output:[bb][BR]",
3117 "[TT]-num[tt]: number of hydrogen bonds as a function of time.[BR]",
3118 "[TT]-ac[tt]: average over all autocorrelations of the existence",
3119 "functions (either 0 or 1) of all hydrogen bonds.[BR]",
3120 "[TT]-dist[tt]: distance distribution of all hydrogen bonds.[BR]",
3121 "[TT]-ang[tt]: angle distribution of all hydrogen bonds.[BR]",
3122 "[TT]-hx[tt]: the number of n-n+i hydrogen bonds as a function of time",
3123 "where n and n+i stand for residue numbers and i ranges from 0 to 6.",
3124 "This includes the n-n+3, n-n+4 and n-n+5 hydrogen bonds associated",
3125 "with helices in proteins.[BR]",
3126 "[TT]-hbn[tt]: all selected groups, donors, hydrogens and acceptors",
3127 "for selected groups, all hydrogen bonded atoms from all groups and",
3128 "all solvent atoms involved in insertion.[BR]",
3129 "[TT]-hbm[tt]: existence matrix for all hydrogen bonds over all",
3130 "frames, this also contains information on solvent insertion",
3131 "into hydrogen bonds. Ordering is identical to that in [TT]-hbn[tt]",
3132 "index file.[BR]",
3133 "[TT]-dan[tt]: write out the number of donors and acceptors analyzed for",
3134 "each timeframe. This is especially useful when using [TT]-shell[tt].[BR]",
3135 "[TT]-nhbdist[tt]: compute the number of HBonds per hydrogen in order to",
3136 "compare results to Raman Spectroscopy.",
3137 "[PAR]",
3138 "Note: options [TT]-ac[tt], [TT]-life[tt], [TT]-hbn[tt] and [TT]-hbm[tt]",
3139 "require an amount of memory proportional to the total numbers of donors",
3140 "times the total number of acceptors in the selected group(s)."
3143 static real acut=30, abin=1, rcut=0.35, r2cut=0, rbin=0.005, rshell=-1;
3144 static real maxnhb=0,fit_start=1,fit_end=60,temp=298.15,smooth_tail_start=-1, D=-1;
3145 static bool bNitAcc=TRUE,bInsert=FALSE,bDA=TRUE,bMerge=TRUE;
3146 static int nDump=0, nFitPoints=100;
3147 static int nThreads = 0, nBalExp=4;
3149 static bool bContact=FALSE, bBallistic=FALSE, bBallisticDt=FALSE, bGemFit=FALSE;
3150 static real logAfterTime = 10, gemBallistic = 0.2; /* ps */
3151 static char *NNtype[] = {NULL, "none", "binary", "oneOverR3", "dipole", NULL};
3153 /* options */
3154 t_pargs pa [] = {
3155 { "-ins", FALSE, etBOOL, {&bInsert},
3156 "Analyze solvent insertion" },
3157 { "-a", FALSE, etREAL, {&acut},
3158 "Cutoff angle (degrees, Acceptor - Donor - Hydrogen)" },
3159 { "-r", FALSE, etREAL, {&rcut},
3160 "Cutoff radius (nm, X - Acceptor, see next option)" },
3161 { "-da", FALSE, etBOOL, {&bDA},
3162 "Use distance Donor-Acceptor (if TRUE) or Hydrogen-Acceptor (FALSE)" },
3163 { "-r2", FALSE, etREAL, {&r2cut},
3164 "Second cutoff radius. Mainly useful with -contact and -ac"},
3165 { "-abin", FALSE, etREAL, {&abin},
3166 "Binwidth angle distribution (degrees)" },
3167 { "-rbin", FALSE, etREAL, {&rbin},
3168 "Binwidth distance distribution (nm)" },
3169 { "-nitacc",FALSE, etBOOL, {&bNitAcc},
3170 "Regard nitrogen atoms as acceptors" },
3171 { "-contact",FALSE,etBOOL, {&bContact},
3172 "Do not look for hydrogen bonds, but merely for contacts within the cut-off distance" },
3173 { "-shell", FALSE, etREAL, {&rshell},
3174 "when > 0, only calculate hydrogen bonds within # nm shell around "
3175 "one particle" },
3176 { "-fitstart", FALSE, etREAL, {&fit_start},
3177 "Time (ps) from which to start fitting the correlation functions in order to obtain the forward and backward rate constants for HB breaking and formation. With -gemfit we suggest -fitstart 0" },
3178 { "-fitstart", FALSE, etREAL, {&fit_start},
3179 "Time (ps) to which to stop fitting the correlation functions in order to obtain the forward and backward rate constants for HB breaking and formation (only with -gemfit)" },
3180 { "-temp", FALSE, etREAL, {&temp},
3181 "Temperature (K) for computing the Gibbs energy corresponding to HB breaking and reforming" },
3182 { "-smooth",FALSE, etREAL, {&smooth_tail_start},
3183 "If >= 0, the tail of the ACF will be smoothed by fitting it to an exponential function: y = A exp(-x/tau)" },
3184 { "-dump", FALSE, etINT, {&nDump},
3185 "Dump the first N hydrogen bond ACFs in a single xvg file for debugging" },
3186 { "-max_hb",FALSE, etREAL, {&maxnhb},
3187 "Theoretical maximum number of hydrogen bonds used for normalizing HB autocorrelation function. Can be useful in case the program estimates it wrongly" },
3188 { "-merge", FALSE, etBOOL, {&bMerge},
3189 "H-bonds between the same donor and acceptor, but with different hydrogen are treated as a single H-bond. Mainly important for the ACF." },
3190 { "-geminate", FALSE, etENUM, {gemType},
3191 "Use reversible geminate recombination for the kinetics/thermodynamics calclations. See Markovitch et al., J. Chem. Phys 129, 084505 (2008) for details."},
3192 { "-diff", FALSE, etREAL, {&D},
3193 "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."},
3194 #ifdef HAVE_OPENMP
3195 { "-nthreads", FALSE, etINT, {&nThreads},
3196 "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)"},
3197 #endif
3198 { "-NN", FALSE, etENUM, {NNtype},
3199 "HIDDENDo a full all vs all loop and estimate the interaction energy instead of having a binary existence function for hydrogen bonds. NOT FULLY TESTED YET! DON'T USE IT!"},
3200 { "-gemfit", FALSE, etBOOL, {&bGemFit},
3201 "With -gemainate != none: fit ka and kd to the ACF"},
3202 /* { "-gemlogstart", FALSE, etREAL, {&logAfterTime}, */
3203 /* "HIDDENWith -gemfit: After this time (ps) the data points fitted to will be equidistant in log-time."}, */
3204 { "-gemnp", FALSE, etINT, {&nFitPoints},
3205 "HIDDENNuber of points in the ACF used to fit rev. gem. recomb. model"},
3206 { "-ballistic", FALSE, etBOOL, {&bBallistic},
3207 "Calculate and remove ultrafast \"ballistic\" component in the ACF"},
3208 { "-ballisticlen", FALSE, etREAL, {&gemBallistic},
3209 "HIDDENFitting interval for the ultrafast \"ballistic\" component in ACF"},
3210 { "-nbalexp", FALSE, etINT, {&nBalExp},
3211 "HIDDENNumber of exponentials to fit when removing the ballistic component"},
3212 { "-ballisticDt", FALSE, etBOOL, {&bBallisticDt},
3213 "HIDDENIf TRUE, finding of the fastest ballistic component will be based on the time derivative at t=0, "
3214 "while if FALSE, it will be based on the exponent alone (like in Markovitch 2008)"}
3216 const char *bugs[] = {
3217 "The option [TT]-sel[tt] that used to work on selected hbonds is out of order, and therefore not available for the time being."
3219 t_filenm fnm[] = {
3220 { efTRX, "-f", NULL, ffREAD },
3221 { efTPX, NULL, NULL, ffREAD },
3222 { efNDX, NULL, NULL, ffOPTRD },
3223 /* { efNDX, "-sel", "select", ffOPTRD },*/
3224 { efXVG, "-num", "hbnum", ffWRITE },
3225 { efLOG, "-g", "hbond", ffOPTWR },
3226 { efXVG, "-ac", "hbac", ffOPTWR },
3227 { efXVG, "-dist","hbdist", ffOPTWR },
3228 { efXVG, "-ang", "hbang", ffOPTWR },
3229 { efXVG, "-hx", "hbhelix",ffOPTWR },
3230 { efNDX, "-hbn", "hbond", ffOPTWR },
3231 { efXPM, "-hbm", "hbmap", ffOPTWR },
3232 { efXVG, "-don", "donor", ffOPTWR },
3233 { efXVG, "-dan", "danum", ffOPTWR },
3234 { efXVG, "-life","hblife", ffOPTWR },
3235 { efXVG, "-nhbdist", "nhbdist",ffOPTWR }
3238 #define NFILE asize(fnm)
3240 char hbmap [HB_NR]={ ' ', 'o', '-', '*' };
3241 char *hbdesc[HB_NR]={ "None", "Present", "Inserted", "Present & Inserted" };
3242 t_rgb hbrgb [HB_NR]={ {1,1,1},{1,0,0}, {0,0,1}, {1,0,1} };
3244 int status, trrStatus=1;
3245 t_topology top;
3246 t_inputrec ir;
3247 t_pargs *ppa;
3248 int npargs,natoms,nframes=0,shatom;
3249 int *isize;
3250 char **grpnames;
3251 atom_id **index;
3252 rvec *x,hbox;
3253 matrix box;
3254 real t,ccut,dist,ang;
3255 double max_nhb,aver_nhb,aver_dist;
3256 int h,i,j,k=0,l,start,end,id,ja,ogrp,nsel;
3257 int xi,yi,zi,ai;
3258 int xj,yj,zj,aj,xjj,yjj,zjj;
3259 int xk,yk,zk,ak,xkk,ykk,zkk;
3260 bool bSelected,bHBmap,bStop,bTwo,was,bBox,bTric;
3261 int *adist,*rdist;
3262 int grp,nabin,nrbin,bin,resdist,ihb;
3263 char **leg;
3264 t_hbdata *hb;
3265 FILE *fp,*fpins=NULL,*fpnhb=NULL;
3266 t_gridcell ***grid;
3267 t_ncell *icell,*jcell,*kcell;
3268 ivec ngrid;
3269 unsigned char *datable;
3270 output_env_t oenv;
3271 int gemmode, NN;
3272 PSTYPE peri=0;
3273 t_E E;
3274 int ii, jj, hh, actual_nThreads;
3275 int threadNr=0;
3276 bool bGem, bNN, bParallel;
3277 t_gemParams *params=NULL;
3279 CopyRight(stdout,argv[0]);
3281 npargs = asize(pa);
3282 ppa = add_acf_pargs(&npargs,pa);
3284 parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_BE_NICE,NFILE,fnm,npargs,
3285 ppa,asize(desc),desc,asize(bugs),bugs,&oenv);
3287 /* NN-loop? If so, what estimator to use ?*/
3288 NN = 1;
3289 while (NN < NN_NR && strcasecmp(NNtype[0], NNtype[NN])!=0)
3290 NN++;
3291 if (NN == NN_NR)
3292 gmx_fatal(FARGS, "Invalid NN-loop type.");
3294 bNN = FALSE;
3295 for (i=2; bNN==FALSE && i<NN_NR; i++)
3296 bNN = bNN || NN == i;
3298 if (NN > NN_NONE && bMerge)
3299 bMerge = FALSE;
3301 /* geminate recombination? If so, which flavor? */
3302 gemmode = 1;
3303 while (gemmode < gemNR && strcasecmp(gemType[0], gemType[gemmode])!=0)
3304 gemmode++;
3305 if (gemmode == gemNR)
3306 gmx_fatal(FARGS, "Invalid recombination type.");
3308 bGem = FALSE;
3309 for (i=2; bGem==FALSE && i<gemNR; i++)
3310 bGem = bGem || gemmode == i;
3312 if (bGem) {
3313 printf("Geminate recombination: %s\n" ,gemType[gemmode]);
3314 #ifndef HAVE_LIBGSL
3315 printf("Note that some aspects of reversible geminate recombination won't work without gsl.\n");
3316 #endif
3317 if (bContact) {
3318 if (gemmode != gemDD) {
3319 printf("Turning off -contact option...\n");
3320 bContact = FALSE;
3322 } else {
3323 if (gemmode == gemDD) {
3324 printf("Turning on -contact option...\n");
3325 bContact = TRUE;
3328 if (bMerge) {
3329 if (gemmode == gemAA) {
3330 printf("Turning off -merge option...\n");
3331 bMerge = FALSE;
3333 } else {
3334 if (gemmode != gemAA) {
3335 printf("Turning on -merge option...\n");
3336 bMerge = TRUE;
3339 } else
3340 printf("No geminate recombination.\n");
3342 /* process input */
3343 bSelected = opt2bSet("-sel",NFILE,fnm);
3344 ccut = cos(acut*DEG2RAD);
3346 if (bContact) {
3347 if (bSelected)
3348 gmx_fatal(FARGS,"Can not analyze selected contacts: turn off -sel");
3349 if (bInsert)
3350 gmx_fatal(FARGS,"Can not analyze inserted contacts: turn off -ins");
3351 if (!bDA) {
3352 gmx_fatal(FARGS,"Can not analyze contact between H and A: turn off -noda");
3356 #ifndef HAVE_LIBGSL
3357 printf("NO GSL! Can't find and take away ballistic term in ACF without GSL\n.");
3358 #endif
3360 /* Initiate main data structure! */
3361 bHBmap = (opt2bSet("-ac",NFILE,fnm) ||
3362 opt2bSet("-life",NFILE,fnm) ||
3363 opt2bSet("-hbn",NFILE,fnm) ||
3364 opt2bSet("-hbm",NFILE,fnm) ||
3365 bGem);
3367 #ifdef HAVE_OPENMP
3368 printf("Compiled with OpenMP (%i)\n", _OPENMP);
3369 #endif
3371 /* if (bContact && bGem) */
3372 /* gmx_fatal(FARGS, "Can't do reversible geminate recombination with -contact yet."); */
3374 if (opt2bSet("-nhbdist",NFILE,fnm)) {
3375 char *leg[MAXHH+1] = { "0 HBs", "1 HB", "2 HBs", "3 HBs", "Total" };
3376 fpnhb = xvgropen(opt2fn("-nhbdist",NFILE,fnm),
3377 "Number of donor-H with N HBs","Time (ps)","N",oenv);
3378 xvgr_legend(fpnhb,asize(leg),leg,oenv);
3381 hb = mk_hbdata(bHBmap,opt2bSet("-dan",NFILE,fnm),bMerge || bContact, bGem, gemmode);
3383 /* get topology */
3384 read_tpx_top(ftp2fn(efTPX,NFILE,fnm),&ir,box,&natoms,NULL,NULL,NULL,&top);
3386 snew(grpnames,grNR);
3387 snew(index,grNR);
3388 snew(isize,grNR);
3389 /* Make Donor-Acceptor table */
3390 snew(datable, top.atoms.nr);
3391 gen_datable(index[0],isize[0],datable,top.atoms.nr);
3393 if (bSelected) {
3394 /* analyze selected hydrogen bonds */
3395 printf("Select group with selected atoms:\n");
3396 get_index(&(top.atoms),opt2fn("-sel",NFILE,fnm),
3397 1,&nsel,index,grpnames);
3398 if (nsel % 3)
3399 gmx_fatal(FARGS,"Number of atoms in group '%s' not a multiple of 3\n"
3400 "and therefore cannot contain triplets of "
3401 "Donor-Hydrogen-Acceptor",grpnames[0]);
3402 bTwo=FALSE;
3404 for(i=0; (i<nsel); i+=3) {
3405 int dd = index[0][i];
3406 int aa = index[0][i+2];
3407 /* int */ hh = index[0][i+1];
3408 add_dh (&hb->d,dd,hh,i,datable);
3409 add_acc(&hb->a,aa,i);
3410 /* Should this be here ? */
3411 snew(hb->d.dptr,top.atoms.nr);
3412 snew(hb->a.aptr,top.atoms.nr);
3413 add_hbond(hb,dd,aa,hh,gr0,gr0,0,FALSE,bMerge,0,bContact,peri);
3415 printf("Analyzing %d selected hydrogen bonds from '%s'\n",
3416 isize[0],grpnames[0]);
3418 else {
3419 /* analyze all hydrogen bonds: get group(s) */
3420 printf("Specify 2 groups to analyze:\n");
3421 get_index(&(top.atoms),ftp2fn_null(efNDX,NFILE,fnm),
3422 2,isize,index,grpnames);
3424 /* check if we have two identical or two non-overlapping groups */
3425 bTwo = isize[0] != isize[1];
3426 for (i=0; (i<isize[0]) && !bTwo; i++)
3427 bTwo = index[0][i] != index[1][i];
3428 if (bTwo) {
3429 printf("Checking for overlap in atoms between %s and %s\n",
3430 grpnames[0],grpnames[1]);
3431 for (i=0; i<isize[1];i++)
3432 if (ISINGRP(datable[index[1][i]]))
3433 gmx_fatal(FARGS,"Partial overlap between groups '%s' and '%s'",
3434 grpnames[0],grpnames[1]);
3436 printf("Checking for overlap in atoms between %s and %s\n",
3437 grpnames[0],grpnames[1]);
3438 for (i=0; i<isize[0]; i++)
3439 for (j=0; j<isize[1]; j++)
3440 if (index[0][i] == index[1][j])
3441 gmx_fatal(FARGS,"Partial overlap between groups '%s' and '%s'",
3442 grpnames[0],grpnames[1]);
3445 if (bTwo)
3446 printf("Calculating %s "
3447 "between %s (%d atoms) and %s (%d atoms)\n",
3448 bContact ? "contacts" : "hydrogen bonds",
3449 grpnames[0],isize[0],grpnames[1],isize[1]);
3450 else
3451 fprintf(stderr,"Calculating %s in %s (%d atoms)\n",
3452 bContact?"contacts":"hydrogen bonds",grpnames[0],isize[0]);
3454 sfree(datable);
3455 if (bInsert) {
3456 printf("Specify group for insertion analysis:\n");
3457 get_index(&(top.atoms),ftp2fn_null(efNDX,NFILE,fnm),
3458 1,&(isize[grI]),&(index[grI]),&(grpnames[grI]));
3459 printf("Checking for overlap...\n");
3460 for (i=0; i<isize[grI]; i++)
3461 for (grp=0; grp<(bTwo?2:1); grp++)
3462 for (j=0; j<isize[grp]; j++)
3463 if (index[grI][i] == index[grp][j])
3464 gmx_fatal(FARGS,"Partial overlap between groups '%s' and '%s'",
3465 grpnames[grp],grpnames[grI]);
3466 fpins=ffopen("insert.dat","w");
3467 fprintf(fpins,"%4s: %15s -> %15s (%7s) - %15s (%7s)\n",
3468 "time","insert","donor","distang","acceptor","distang");
3471 /* search donors and acceptors in groups */
3472 snew(datable, top.atoms.nr);
3473 for (i=0; (i<grNR); i++)
3474 if ( ((i==gr0) && !bSelected ) ||
3475 ((i==gr1) && bTwo ) ||
3476 ((i==grI) && bInsert ) ) {
3477 gen_datable(index[i],isize[i],datable,top.atoms.nr);
3478 if (bContact) {
3479 search_acceptors(&top,isize[i],index[i],&hb->a,i,
3480 bNitAcc,TRUE,(bTwo && (i==gr0)) || !bTwo, datable);
3481 search_donors (&top,isize[i],index[i],&hb->d,i,
3482 TRUE,(bTwo && (i==gr1)) || !bTwo, datable);
3484 else {
3485 search_acceptors(&top,isize[i],index[i],&hb->a,i,bNitAcc,FALSE,TRUE, datable);
3486 search_donors (&top,isize[i],index[i],&hb->d,i,FALSE,TRUE, datable);
3488 if (bTwo)
3489 clear_datable_grp(datable,top.atoms.nr);
3491 sfree(datable);
3492 printf("Found %d donors and %d acceptors\n",hb->d.nrd,hb->a.nra);
3493 /*if (bSelected)
3494 snew(donors[gr0D], dons[gr0D].nrd);*/
3496 if (bHBmap) {
3497 printf("Making hbmap structure...");
3498 /* Generate hbond data structure */
3499 mk_hbmap(hb,bTwo,bInsert);
3500 printf("done.\n");
3503 #ifdef HAVE_NN_LOOPS
3504 if (bNN)
3505 mk_hbEmap(hb, 0);
3506 #endif
3508 if (bGem) {
3509 printf("Making per structure...");
3510 /* Generate hbond data structure */
3511 mk_per(hb);
3512 printf("done.\n");
3515 /* check input */
3516 bStop=FALSE;
3517 if (hb->d.nrd + hb->a.nra == 0) {
3518 printf("No Donors or Acceptors found\n");
3519 bStop=TRUE;
3521 if (!bStop) {
3522 if (hb->d.nrd == 0) {
3523 printf("No Donors found\n");
3524 bStop=TRUE;
3526 if (hb->a.nra == 0) {
3527 printf("No Acceptors found\n");
3528 bStop=TRUE;
3531 if (bStop)
3532 gmx_fatal(FARGS,"Nothing to be done");
3534 shatom=0;
3535 if (rshell > 0) {
3536 int shisz;
3537 atom_id *shidx;
3538 char *shgrpnm;
3539 /* get index group with atom for shell */
3540 do {
3541 printf("Select atom for shell (1 atom):\n");
3542 get_index(&(top.atoms),ftp2fn_null(efNDX,NFILE,fnm),
3543 1,&shisz,&shidx,&shgrpnm);
3544 if (shisz != 1)
3545 printf("group contains %d atoms, should be 1 (one)\n",shisz);
3546 } while(shisz != 1);
3547 shatom = shidx[0];
3548 printf("Will calculate hydrogen bonds within a shell "
3549 "of %g nm around atom %i\n",rshell,shatom+1);
3552 /* Analyze trajectory */
3553 natoms=read_first_x(oenv,&status,ftp2fn(efTRX,NFILE,fnm),&t,&x,box);
3554 if ( natoms > top.atoms.nr )
3555 gmx_fatal(FARGS,"Topology (%d atoms) does not match trajectory (%d atoms)",
3556 top.atoms.nr,natoms);
3558 bBox = ir.ePBC!=epbcNONE;
3559 grid = init_grid(bBox, box, (rcut>r2cut)?rcut:r2cut, ngrid);
3560 nabin = acut/abin;
3561 nrbin = rcut/rbin;
3562 snew(adist,nabin+1);
3563 snew(rdist,nrbin+1);
3565 if (bGem && !bBox)
3566 gmx_fatal(FARGS, "Can't do geminate recombination without periodic box.");
3568 bParallel = FALSE;
3570 #ifndef HAVE_OPENMP
3571 #define __ADIST adist
3572 #define __RDIST rdist
3573 #define __HBDATA hb
3574 #else /* HAVE_OPENMP ================================================== \
3575 * Set up the OpenMP stuff, |
3576 * like the number of threads and such |
3577 * Also start the parallel loop. |
3579 #define __ADIST p_adist[threadNr]
3580 #define __RDIST p_rdist[threadNr]
3581 #define __HBDATA p_hb[threadNr]
3583 bParallel = !bSelected && !bInsert;
3585 if (bParallel)
3587 #if (_OPENMP > 200805)
3588 actual_nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_thread_limit());
3589 #else
3590 actual_nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_num_procs());
3591 #endif
3592 omp_set_num_threads(actual_nThreads);
3593 printf("Frame loop parallelized with OpenMP using %i threads.\n", actual_nThreads);
3594 fflush(stdout);
3596 else
3598 actual_nThreads = 1;
3601 t_hbdata **p_hb; /* one per thread, then merge after the frame loop */
3602 int **p_adist, **p_rdist; /* a histogram for each thread. */
3603 snew(p_hb, actual_nThreads);
3604 snew(p_adist, actual_nThreads);
3605 snew(p_rdist, actual_nThreads);
3606 for (i=0; i<actual_nThreads; i++)
3608 snew(p_hb[i], 1);
3609 snew(p_adist[i], nabin+1);
3610 snew(p_rdist[i], nrbin+1);
3612 p_hb[i]->max_frames = 0;
3613 p_hb[i]->nhb = NULL;
3614 p_hb[i]->ndist = NULL;
3615 p_hb[i]->n_bound = NULL;
3616 p_hb[i]->time = NULL;
3617 p_hb[i]->nhx = NULL;
3619 p_hb[i]->bHBmap = hb->bHBmap;
3620 p_hb[i]->bDAnr = hb->bDAnr;
3621 p_hb[i]->bGem = hb->bGem;
3622 p_hb[i]->wordlen = hb->wordlen;
3623 p_hb[i]->nframes = hb->nframes;
3624 p_hb[i]->maxhydro = hb->maxhydro;
3625 p_hb[i]->danr = hb->danr;
3626 p_hb[i]->d = hb->d;
3627 p_hb[i]->a = hb->a;
3628 p_hb[i]->hbmap = hb->hbmap;
3629 p_hb[i]->time = hb->time; /* This may need re-syncing at every frame. */
3630 p_hb[i]->per = hb->per;
3632 #ifdef HAVE_NN_LOOPS
3633 p_hb[i]->hbE = hb->hbE;
3634 #endif
3636 p_hb[i]->nrhb = 0;
3637 p_hb[i]->nrdist = 0;
3640 /* Make a thread pool here,
3641 * instead of forking anew at every frame. */
3643 #pragma omp parallel \
3644 private(i, j, h, ii, jj, hh, E, \
3645 xi, yi, zi, xj, yj, zj, threadNr, \
3646 dist, ang, peri, icell, jcell, \
3647 grp, ogrp, ai, aj, xjj, yjj, zjj, \
3648 xk, yk, zk, ihb, id, resdist, \
3649 xkk, ykk, zkk, kcell, ak, k, bTric) \
3650 default(none) \
3651 shared(hb, p_hb, p_adist, p_rdist, actual_nThreads, \
3652 x, bBox, box, hbox, rcut, r2cut, rshell, \
3653 shatom, ngrid, grid, nframes, t, \
3654 bParallel, bNN, index, bMerge, bContact, \
3655 bTwo, bDA,ccut, abin, rbin, top, \
3656 bInsert, bSelected, bDebug, stderr, nsel, \
3657 bGem, oenv, fnm, fpnhb, trrStatus, natoms, \
3658 status, nabin, nrbin, adist, rdist, debug)
3659 { /* Start of parallel region */
3660 threadNr = omp_get_thread_num();
3661 #endif /* HAVE_OPENMP ================================================= */
3664 bTric = bBox && TRICLINIC(box);
3666 #ifdef HAVE_OPENMP
3667 sync_hbdata(hb, p_hb[threadNr], nframes, t);
3668 #pragma omp single
3669 #endif
3671 build_grid(hb,x,x[shatom], bBox,box,hbox, (rcut>r2cut)?rcut:r2cut,
3672 rshell, ngrid,grid);
3673 reset_nhbonds(&(hb->d));
3675 if (debug && bDebug)
3676 dump_grid(debug, ngrid, grid);
3678 add_frames(hb,nframes);
3679 init_hbframe(hb,nframes,t);
3681 if (hb->bDAnr)
3682 count_da_grid(ngrid, grid, hb->danr[nframes]);
3683 } /* omp single */
3685 #ifdef HAVE_OPENMP
3686 p_hb[threadNr]->time = hb->time; /* This pointer may have changed. */
3687 #endif
3688 if (bNN)
3690 #ifdef HAVE_NN_LOOPS /* Unlock this feature when testing */
3691 /* Loop over all atom pairs and estimate interaction energy */
3692 #ifdef HAVE_OPENMP /* ------- */
3693 #pragma omp single
3694 #endif /* HAVE_OPENMP ------- */
3696 addFramesNN(hb, nframes);
3698 #ifdef HAVE_OPENMP /* ---------------- */
3699 #pragma omp barrier
3700 #pragma omp for schedule(dynamic)
3701 #endif /* HAVE_OPENMP ---------------- */
3702 for (i=0; i<hb->d.nrd; i++)
3704 for(j=0;j<hb->a.nra; j++)
3706 for (h=0;
3707 h < (bContact ? 1 : hb->d.nhydro[i]);
3708 h++)
3710 if (i==hb->d.nrd || j==hb->a.nra)
3711 gmx_fatal(FARGS, "out of bounds");
3713 /* Get the real atom ids */
3714 ii = hb->d.don[i];
3715 jj = hb->a.acc[j];
3716 hh = hb->d.hydro[i][h];
3718 /* Estimate the energy from the geometry */
3719 E = calcHbEnergy(ii, jj, hh, x, NN, box, hbox, &(hb->d));
3720 /* Store the energy */
3721 storeHbEnergy(hb, i, j, h, E, nframes);
3725 #endif /* HAVE_NN_LOOPS */
3726 } /* if (bNN)*/
3727 else
3729 if (bSelected)
3731 #ifdef HAVE_OPENMP
3732 #pragma omp single
3733 #endif
3735 /* Do not parallelize this just yet. */
3736 /* int ii; */
3737 for(ii=0; (ii<nsel); ii++) {
3738 int dd = index[0][i];
3739 int aa = index[0][i+2];
3740 /* int */ hh = index[0][i+1];
3741 ihb = is_hbond(hb,ii,ii,dd,aa,rcut,r2cut,ccut,x,bBox,box,
3742 hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
3744 if (ihb) {
3745 /* add to index if not already there */
3746 /* Add a hbond */
3747 add_hbond(hb,dd,aa,hh,ii,ii,nframes,FALSE,bMerge,ihb,bContact,peri);
3750 } /* omp single */
3751 } /* if (bSelected) */
3752 else
3754 #ifdef HAVE_OPENMP
3755 #pragma omp single
3757 #endif
3758 if (bGem)
3759 calcBoxProjection(box, hb->per->P);
3761 /* loop over all gridcells (xi,yi,zi) */
3762 /* Removed confusing macro, DvdS 27/12/98 */
3763 #ifdef HAVE_OPENMP
3765 /* The outer grid loop will have to do for now. */
3766 #pragma omp for schedule(dynamic)
3767 #endif
3768 for(xi=0; (xi<ngrid[XX]); xi++)
3769 for(yi=0; (yi<ngrid[YY]); yi++)
3770 for(zi=0; (zi<ngrid[ZZ]); zi++) {
3772 /* loop over donor groups gr0 (always) and gr1 (if necessary) */
3773 for (grp=gr0; (grp <= (bTwo?gr1:gr0)); grp++) {
3774 icell=&(grid[zi][yi][xi].d[grp]);
3776 if (bTwo)
3777 ogrp = 1-grp;
3778 else
3779 ogrp = grp;
3781 /* loop over all hydrogen atoms from group (grp)
3782 * in this gridcell (icell)
3784 for (ai=0; (ai<icell->nr); ai++) {
3785 i = icell->atoms[ai];
3787 /* loop over all adjacent gridcells (xj,yj,zj) */
3788 /* This is a macro!!! */
3789 LOOPGRIDINNER(xj,yj,zj,xjj,yjj,zjj,xi,yi,zi,ngrid,bTric) {
3790 jcell=&(grid[zj][yj][xj].a[ogrp]);
3791 /* loop over acceptor atoms from other group (ogrp)
3792 * in this adjacent gridcell (jcell)
3794 for (aj=0; (aj<jcell->nr); aj++) {
3795 j = jcell->atoms[aj];
3797 /* check if this once was a h-bond */
3798 peri = -1;
3799 ihb = is_hbond(__HBDATA,grp,ogrp,i,j,rcut,r2cut,ccut,x,bBox,box,
3800 hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
3802 if (ihb) {
3803 /* add to index if not already there */
3804 /* Add a hbond */
3805 add_hbond(__HBDATA,i,j,h,grp,ogrp,nframes,FALSE,bMerge,ihb,bContact,peri);
3807 /* make angle and distance distributions */
3808 if (ihb == hbHB && !bContact) {
3809 if (dist>rcut)
3810 gmx_fatal(FARGS,"distance is higher than what is allowed for an hbond: %f",dist);
3811 ang*=RAD2DEG;
3812 __ADIST[(int)( ang/abin)]++;
3813 __RDIST[(int)(dist/rbin)]++;
3814 if (!bTwo) {
3815 int id,ia;
3816 if ((id = donor_index(&hb->d,grp,i)) == NOTSET)
3817 gmx_fatal(FARGS,"Invalid donor %d",i);
3818 if ((ia = acceptor_index(&hb->a,ogrp,j)) == NOTSET)
3819 gmx_fatal(FARGS,"Invalid acceptor %d",j);
3820 resdist=abs(top.atoms.atom[i].resind-
3821 top.atoms.atom[j].resind);
3822 if (resdist >= max_hx)
3823 resdist = max_hx-1;
3824 __HBDATA->nhx[nframes][resdist]++;
3828 if (bInsert && bSelected && MASTER_THREAD_ONLY(threadNr)) {
3829 /* this has been a h-bond, or we are analyzing
3830 selected bonds: check for inserted */
3831 bool ins_d, ins_a;
3832 real ins_d_dist, ins_d_ang, ins_a_dist, ins_a_ang;
3833 int ins_d_k=0,ins_a_k=0;
3835 ins_d=ins_a=FALSE;
3836 ins_d_dist=ins_d_ang=ins_a_dist=ins_a_ang=1e6;
3838 /* loop over gridcells adjacent to i (xk,yk,zk) */
3839 LOOPGRIDINNER(xk,yk,zk,xkk,ykk,zkk,xi,yi,zi,ngrid,bTric){
3840 kcell=&(grid[zk][yk][xk].a[grI]);
3841 /* loop over acceptor atoms from ins group
3842 in this adjacent gridcell (kcell) */
3843 for (ak=0; (ak<kcell->nr); ak++) {
3844 k=kcell->atoms[ak];
3845 ihb = is_hbond(hb,grp,grI,i,k,rcut,r2cut,ccut,x,
3846 bBox,box,hbox,&dist,&ang,bDA,&h,
3847 bContact,bMerge,&peri);
3848 if (ihb == hbHB) {
3849 if (dist < ins_d_dist) {
3850 ins_d=TRUE;
3851 ins_d_dist=dist;
3852 ins_d_ang =ang ;
3853 ins_d_k =k ;
3858 ENDLOOPGRIDINNER;
3859 /* loop over gridcells adjacent to j (xk,yk,zk) */
3860 LOOPGRIDINNER(xk,yk,zk,xkk,ykk,zkk,xj,yj,zj,ngrid,bTric){
3861 kcell=&grid[zk][yk][xk].d[grI];
3862 /* loop over hydrogen atoms from ins group
3863 in this adjacent gridcell (kcell) */
3864 for (ak=0; ak<kcell->nr; ak++) {
3865 k = kcell->atoms[ak];
3866 ihb = is_hbond(hb,grI,ogrp,k,j,rcut,r2cut,ccut,x,
3867 bBox,box,hbox,&dist,&ang,bDA,&h,
3868 bContact,bMerge,&peri);
3869 if (ihb == hbHB) {
3870 if (dist<ins_a_dist) {
3871 ins_a=TRUE;
3872 ins_a_dist=dist;
3873 ins_a_ang =ang ;
3874 ins_a_k =k ;
3879 ENDLOOPGRIDINNER;
3882 ihb = is_hbond(hb,grI,grI,ins_d_k,ins_a_k,rcut,r2cut,ccut,x,
3883 bBox,box,hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
3884 if (ins_d && ins_a && ihb) {
3885 /* add to hbond index if not already there */
3886 add_hbond(hb,ins_d_k,ins_a_k,h,grI,ogrp,
3887 nframes,TRUE,bMerge,ihb,bContact,peri);
3889 /* print insertion info to file */
3890 /*fprintf(fpins,
3891 "%4g: %4u:%3.3s%4d%3.3s -> "
3892 "%4u:%3.3s%4d%3.3s (%4.2f,%2.0f) - "
3893 "%4u:%3.3s%4d%3.3s (%4.2f,%2.0f)\n",t,
3894 a[grIA][ins_d_k]+1,
3895 *top.atoms.resname[top.atoms.atom[a[grIA][ins_d_k]].resnr],
3896 top.atoms.atom[a[grIA][ins_d_k]].resnr+1,
3897 *top.atoms.atomname[a[grIA][ins_d_k]],
3898 a[grp+grD][i]+1,
3899 *top.atoms.resname[top.atoms.atom[a[grp+grD][i]].resnr],
3900 top.atoms.atom[a[grp+grD][i]].resnr+1,
3901 *top.atoms.atomname[a[grp+grD][i]],
3902 ins_d_dist,ins_d_ang*RAD2DEG,
3903 a[ogrp+grA][j]+1,
3904 *top.atoms.resname[top.atoms.atom[a[ogrp+grA][j]].resnr],
3905 top.atoms.atom[a[ogrp+grA][j]].resnr+1,
3906 *top.atoms.atomname[a[ogrp+grA][j]],
3907 ins_a_dist,ins_a_ang*RAD2DEG);*/
3910 } /* if (bInsert && bSelected), omp single */
3912 } /* for aj */
3914 ENDLOOPGRIDINNER;
3915 } /* for ai */
3916 } /* for grp */
3917 } /* for xi,yi,zi */
3918 } /* if (bSelected) {...} else */
3920 #ifdef HAVE_OPENMP /* ---------------------------- */
3921 /* Better wait for all threads to finnish using x[] before updating it. */
3922 k = nframes; /* */
3923 #pragma omp barrier /* */
3924 #pragma omp critical /* */
3925 { /* */
3926 /* Sum up histograms and counts from p_hb[] into hb */
3927 { /* */
3928 hb->nhb[k] += p_hb[threadNr]->nhb[k];
3929 hb->ndist[k] += p_hb[threadNr]->ndist[k];
3930 for (j=0; j<max_hx; j++) /**/
3931 hb->nhx[k][j] += p_hb[threadNr]->nhx[k][j];
3932 } /* */
3933 } /* */
3934 /* */
3935 /* Here are a handful of single constructs
3936 * to share the workload a bit. The most
3937 * important one is of course the last one,
3938 * where there's a potential bottleneck in form
3939 * of slow I/O. */
3940 #pragma omp single /* ++++++++++++++++, */
3941 #endif /* HAVE_OPENMP ----------------+------------*/
3942 { /* + */
3943 if (hb != NULL) /* */
3944 { /* + */
3945 analyse_donor_props(opt2fn_null("-don",NFILE,fnm),hb,k,t,oenv);
3946 } /* + */
3947 } /* + */
3948 #ifdef HAVE_OPENMP /* + */
3949 #pragma omp single /* +++ +++ */
3950 #endif /* + */
3951 { /* + */
3952 if (fpnhb) /* + */
3953 do_nhb_dist(fpnhb,hb,t);
3954 } /* + */
3955 } /* if (bNN) {...} else + */
3956 #ifdef HAVE_OPENMP /* + */
3957 #pragma omp single /* +++ +++ */
3958 #endif /* + */
3959 { /* + */
3960 trrStatus = (read_next_x(oenv,status,&t,natoms,x,box));
3961 nframes++; /* + */
3962 } /* + */
3963 #ifdef HAVE_OPENMP /* ++++++++++++++++´ */
3964 #pragma omp barrier
3965 #endif
3966 } while (trrStatus);
3968 #ifdef HAVE_OPENMP
3969 #pragma omp critical
3971 hb->nrhb += p_hb[threadNr]->nrhb;
3972 hb->nrdist += p_hb[threadNr]->nrdist;
3974 /* Free parallel datastructures */
3975 sfree(p_hb[threadNr]->nhb);
3976 sfree(p_hb[threadNr]->ndist);
3977 sfree(p_hb[threadNr]->nhx);
3979 #pragma omp for
3980 for (i=0; i<nabin; i++)
3981 for (j=0; j<actual_nThreads; j++)
3983 adist[i] += p_adist[j][i];
3984 #pragma omp for
3985 for (i=0; i<=nrbin; i++)
3986 for (j=0; j<actual_nThreads; j++)
3987 rdist[i] += p_rdist[j][i];
3989 sfree(p_adist[threadNr]);
3990 sfree(p_rdist[threadNr]);
3991 } /* End of parallel region */
3992 sfree(p_adist);
3993 sfree(p_rdist);
3994 #endif
3997 free_grid(ngrid,&grid);
3999 close_trj(status);
4000 if (bInsert)
4001 ffclose(fpins);
4002 if (fpnhb)
4003 ffclose(fpnhb);
4005 /* Compute maximum possible number of different hbonds */
4006 if (maxnhb > 0)
4007 max_nhb = maxnhb;
4008 else {
4009 max_nhb = 0.5*(hb->d.nrd*hb->a.nra);
4011 /* Added support for -contact below.
4012 * - Erik Marklund, May 29-31, 2006 */
4013 /* Changed contact code.
4014 * - Erik Marklund, June 29, 2006 */
4015 if (bHBmap && !bNN) {
4016 if (hb->nrhb==0) {
4017 printf("No %s found!!\n", bContact ? "contacts" : "hydrogen bonds");
4018 } else {
4019 printf("Found %d different %s in trajectory\n"
4020 "Found %d different atom-pairs within %s distance\n",
4021 hb->nrhb, bContact?"contacts":"hydrogen bonds",
4022 hb->nrdist,(r2cut>0)?"second cut-off":"hydrogen bonding");
4024 /*Control the pHist.*/
4026 if (bMerge)
4027 merge_hb(hb,bTwo,bContact);
4029 if (opt2bSet("-hbn",NFILE,fnm))
4030 dump_hbmap(hb,NFILE,fnm,bTwo,bInsert,bContact,isize,index,grpnames,&top.atoms);
4032 /* Moved the call to merge_hb() to a line BEFORE dump_hbmap
4033 * to make the -hbn and -hmb output match eachother.
4034 * - Erik Marklund, May 30, 2006 */
4037 /* Print out number of hbonds and distances */
4038 aver_nhb = 0;
4039 aver_dist = 0;
4040 fp = xvgropen(opt2fn("-num",NFILE,fnm),bContact ? "Contacts" :
4041 "Hydrogen Bonds","Time","Number",oenv);
4042 snew(leg,2);
4043 snew(leg[0],STRLEN);
4044 snew(leg[1],STRLEN);
4045 sprintf(leg[0],"%s",bContact?"Contacts":"Hydrogen bonds");
4046 sprintf(leg[1],"Pairs within %g nm",(r2cut>0)?r2cut:rcut);
4047 xvgr_legend(fp,2,leg,oenv);
4048 sfree(leg[1]);
4049 sfree(leg[0]);
4050 sfree(leg);
4051 for(i=0; (i<nframes); i++) {
4052 fprintf(fp,"%10g %10d %10d\n",hb->time[i],hb->nhb[i],hb->ndist[i]);
4053 aver_nhb += hb->nhb[i];
4054 aver_dist += hb->ndist[i];
4056 ffclose(fp);
4057 aver_nhb /= nframes;
4058 aver_dist /= nframes;
4059 /* Print HB distance distribution */
4060 if (opt2bSet("-dist",NFILE,fnm)) {
4061 long sum;
4063 sum=0;
4064 for(i=0; i<nrbin; i++)
4065 sum+=rdist[i];
4067 fp = xvgropen(opt2fn("-dist",NFILE,fnm),
4068 "Hydrogen Bond Distribution",
4069 "Hydrogen - Acceptor Distance (nm)","",oenv);
4070 for(i=0; i<nrbin; i++)
4071 fprintf(fp,"%10g %10g\n",(i+0.5)*rbin,rdist[i]/(rbin*(real)sum));
4072 ffclose(fp);
4075 /* Print HB angle distribution */
4076 if (opt2bSet("-ang",NFILE,fnm)) {
4077 long sum;
4079 sum=0;
4080 for(i=0; i<nabin; i++)
4081 sum+=adist[i];
4083 fp = xvgropen(opt2fn("-ang",NFILE,fnm),
4084 "Hydrogen Bond Distribution",
4085 "Donor - Hydrogen - Acceptor Angle (\\SO\\N)","",oenv);
4086 for(i=0; i<nabin; i++)
4087 fprintf(fp,"%10g %10g\n",(i+0.5)*abin,adist[i]/(abin*(real)sum));
4088 ffclose(fp);
4091 /* Print HB in alpha-helix */
4092 if (opt2bSet("-hx",NFILE,fnm)) {
4093 fp = xvgropen(opt2fn("-hx",NFILE,fnm),
4094 "Hydrogen Bonds","Time(ps)","Count",oenv);
4095 xvgr_legend(fp,NRHXTYPES,hxtypenames,oenv);
4096 for(i=0; i<nframes; i++) {
4097 fprintf(fp,"%10g",hb->time[i]);
4098 for (j=0; j<max_hx; j++)
4099 fprintf(fp," %6d",hb->nhx[i][j]);
4100 fprintf(fp,"\n");
4102 ffclose(fp);
4104 if (!bNN)
4105 printf("Average number of %s per timeframe %.3f out of %g possible\n",
4106 bContact ? "contacts" : "hbonds",
4107 bContact ? aver_dist : aver_nhb, max_nhb);
4109 /* Do Autocorrelation etc. */
4110 if (hb->bHBmap) {
4112 Added support for -contact in ac and hbm calculations below.
4113 - Erik Marklund, May 29, 2006
4115 ivec itmp;
4116 rvec rtmp;
4117 if (opt2bSet("-ac",NFILE,fnm) || opt2bSet("-life",NFILE,fnm))
4118 please_cite(stdout,"Spoel2006b");
4119 if (opt2bSet("-ac",NFILE,fnm)) {
4120 char *gemstring=NULL;
4122 if (bGem || bNN) {
4123 params = init_gemParams(rcut, D, hb->time, hb->nframes/2, nFitPoints, fit_start, fit_end,
4124 gemBallistic, nBalExp, bBallisticDt);
4125 if (params == NULL)
4126 gmx_fatal(FARGS, "Could not initiate t_gemParams params.");
4128 gemstring = strdup(gemType[hb->per->gemtype]);
4129 do_hbac(opt2fn("-ac",NFILE,fnm),hb,aver_nhb/max_nhb,aver_dist,nDump,
4130 bMerge,bContact,fit_start,temp,r2cut>0,smooth_tail_start,oenv,
4131 params, gemstring, nThreads, NN, bBallistic, bGemFit);
4133 if (opt2bSet("-life",NFILE,fnm))
4134 do_hblife(opt2fn("-life",NFILE,fnm),hb,bMerge,bContact,oenv);
4135 if (opt2bSet("-hbm",NFILE,fnm)) {
4136 t_matrix mat;
4137 int id,ia,hh,x,y;
4139 mat.nx=nframes;
4140 mat.ny=(bContact ? hb->nrdist : hb->nrhb);
4142 snew(mat.matrix,mat.nx);
4143 for(x=0; (x<mat.nx); x++)
4144 snew(mat.matrix[x],mat.ny);
4145 y=0;
4146 for(id=0; (id<hb->d.nrd); id++)
4147 for(ia=0; (ia<hb->a.nra); ia++) {
4148 for(hh=0; (hh<hb->maxhydro); hh++) {
4149 if (hb->hbmap[id][ia]) {
4150 if (ISHB(hb->hbmap[id][ia]->history[hh])) {
4151 /* Changed '<' into '<=' in the for-statement below.
4152 * It fixed the previously undiscovered bug that caused
4153 * the last occurance of an hbond/contact to not be
4154 * set in mat.matrix. Have a look at any old -hbm-output
4155 * and you will notice that the last column is allways empty.
4156 * - Erik Marklund May 30, 2006
4158 for(x=0; (x<=hb->hbmap[id][ia]->nframes); x++) {
4159 int nn0 = hb->hbmap[id][ia]->n0;
4160 range_check(y,0,mat.ny);
4161 mat.matrix[x+nn0][y] = is_hb(hb->hbmap[id][ia]->h[hh],x);
4163 y++;
4168 mat.axis_x=hb->time;
4169 snew(mat.axis_y,mat.ny);
4170 for(j=0; j<mat.ny; j++)
4171 mat.axis_y[j]=j;
4172 sprintf(mat.title,bContact ? "Contact Existence Map":
4173 "Hydrogen Bond Existence Map");
4174 sprintf(mat.legend,bContact ? "Contacts" : "Hydrogen Bonds");
4175 sprintf(mat.label_x,"Time (ps)");
4176 sprintf(mat.label_y, bContact ? "Contact Index" : "Hydrogen Bond Index");
4177 mat.bDiscrete=TRUE;
4178 if (bInsert)
4179 mat.nmap=HB_NR;
4180 else
4181 mat.nmap=2;
4182 snew(mat.map,mat.nmap);
4183 for(i=0; i<mat.nmap; i++) {
4184 mat.map[i].code.c1=hbmap[i];
4185 mat.map[i].desc=hbdesc[i];
4186 mat.map[i].rgb=hbrgb[i];
4188 fp = opt2FILE("-hbm",NFILE,fnm,"w");
4189 write_xpm_m(fp, mat);
4190 ffclose(fp);
4191 for(x=0; x<mat.nx; x++)
4192 sfree(mat.matrix[x]);
4193 sfree(mat.axis_y);
4194 sfree(mat.matrix);
4195 sfree(mat.map);
4199 if (bGem) {
4200 fprintf(stderr, "There were %i periodic shifts\n", hb->per->nper);
4201 fprintf(stderr, "Freeing pHist for all donors...\n");
4202 for (i=0; i<hb->d.nrd; i++) {
4203 fprintf(stderr, "\r%i",i);
4204 if (hb->per->pHist[i] != NULL) {
4205 for (j=0; j<hb->a.nra; j++)
4206 clearPshift(&(hb->per->pHist[i][j]));
4207 sfree(hb->per->pHist[i]);
4210 sfree(hb->per->pHist);
4211 sfree(hb->per->p2i);
4212 sfree(hb->per);
4213 fprintf(stderr, "...done.\n");
4216 #ifdef HAVE_NN_LOOPS
4217 if (bNN)
4218 free_hbEmap(hb);
4219 #endif
4221 if (hb->bDAnr) {
4222 int i,j,nleg;
4223 char **legnames;
4224 char buf[STRLEN];
4226 #define USE_THIS_GROUP(j) ( (j == gr0) || (bTwo && (j == gr1)) || (bInsert && (j == grI)) )
4228 fp = xvgropen(opt2fn("-dan",NFILE,fnm),
4229 "Donors and Acceptors","Time(ps)","Count",oenv);
4230 nleg = (bTwo?2:1)*2 + (bInsert?2:0);
4231 snew(legnames,nleg);
4232 i=0;
4233 for(j=0; j<grNR; j++)
4234 if ( USE_THIS_GROUP(j) ) {
4235 sprintf(buf,"Donors %s",grpnames[j]);
4236 legnames[i++] = strdup(buf);
4237 sprintf(buf,"Acceptors %s",grpnames[j]);
4238 legnames[i++] = strdup(buf);
4240 if (i != nleg)
4241 gmx_incons("number of legend entries");
4242 xvgr_legend(fp,nleg,legnames,oenv);
4243 for(i=0; i<nframes; i++) {
4244 fprintf(fp,"%10g",hb->time[i]);
4245 for (j=0; (j<grNR); j++)
4246 if ( USE_THIS_GROUP(j) )
4247 fprintf(fp," %6d",hb->danr[i][j]);
4248 fprintf(fp,"\n");
4250 ffclose(fp);
4253 thanx(stdout);
4255 return 0;