1 /* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
4 * This source code is part of
8 * GROningen MAchine for Chemical Simulations
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
34 * Groningen Machine for Chemical Simulation
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.
47 /*#define DOUSEOPENMP*/
62 #include "gmx_fatal.h"
75 typedef short int t_E;
78 typedef int t_hx[max_hx];
79 #define NRHXTYPES max_hx
80 const char *hxtypenames[NRHXTYPES]=
81 {"n-n","n-n+1","n-n+2","n-n+3","n-n+4","n-n+5","n-n>6"};
85 #define MASTER_THREAD_ONLY(threadNr) ((threadNr)==0)
87 #define MASTER_THREAD_ONLY(threadNr) ((threadNr)==(threadNr))
90 /* -----------------------------------------*/
92 enum { gr0, gr1, grI, grNR };
93 enum { hbNo, hbDist, hbHB, hbNR, hbR2};
94 enum { noDA, ACC, DON, DA, INGROUP};
95 enum {NN_NULL, NN_NONE, NN_BINARY, NN_1_over_r3, NN_dipole, NN_NR};
97 static const char *grpnames[grNR] = {"0","1","I" };
99 static gmx_bool bDebug = FALSE;
104 #define HB_YESINS HB_YES|HB_INS
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)
126 typedef int t_icell[grNR];
127 typedef atom_id h_id[MAXHYDRO];
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 */
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
150 atom_id *acc; /* Atom numbers of the acceptors */
151 int *grp; /* Group index */
152 int *aptr; /* Map atom number to acceptor index */
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 */
165 /* Tune this to match memory requirements. It should be a signed integer type, e.g. signed char.*/
169 int len; /* The length of frame and p. */
170 int *frame; /* The frames at which transitio*/
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 */
192 int *Etot; /* Total energy for each frame */
193 t_E ****E; /* Energy estimate for [d][a][h][frame-n0] */
197 gmx_bool bHBmap,bDAnr,bGem;
199 /* The following arrays are nframes long */
200 int nframes,max_frames,maxhydro;
206 /* These structures are initialized from the topology at start up */
209 /* This holds a matrix with all possible hydrogen bonds */
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. */
221 static void clearPshift(t_pShift *pShift)
223 if (pShift->len > 0) {
225 sfree(pShift->frame);
230 static void calcBoxProjection(matrix B, matrix P)
232 const int vp[] = {XX,YY,ZZ};
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;
243 for (i=0; i<3; i++){ m = vp[i];
244 mvmul(M, U[m], P[m]);
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(). */
256 /* extend it by 0.5 in all directions since (int) rounds toward 0.*/
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,
267 * - Erik Marklund May 29, 2006
270 static PSTYPE periodicIndex(ivec r, t_gemPeriod *per, gmx_bool daSwap) {
271 /* Try to merge hbonds on the fly. That means that if the
272 * acceptor and donor are mergable, then:
273 * 1) store the hb-info so that acceptor id > donor id,
274 * 2) add the periodic shift in pairs, so that [-x,-y,-z] is
275 * stored in per.p2i[] whenever acceptor id < donor id.
276 * Note that [0,0,0] should already be the first element of per.p2i
277 * by the time this function is called. */
279 /* daSwap is TRUE if the donor and acceptor were swapped.
280 * If so, then the negative vector should be used. */
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])
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. */
300 fprintf(stderr, "p2i not initialized. This shouldn't happen!\n");
304 srenew(per->p2i, per->nper+2);
305 copy_ivec(r, per->p2i[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];
314 return per->nper - 1 - (daSwap ? 0:1);
317 static t_hbdata *mk_hbdata(gmx_bool bHBmap,gmx_bool bDAnr,gmx_bool oneHB, gmx_bool bGem, int gemmode)
322 hb->wordlen = 8*sizeof(unsigned int);
329 hb->maxhydro = MAXHYDRO;
331 hb->per->gemtype = bGem ? gemmode : 0;
336 static void mk_hbmap(t_hbdata *hb,gmx_bool bTwo)
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)
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]);
373 static void mk_hbEmap (t_hbdata *hb, int n0)
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;
392 static void free_hbEmap (t_hbdata *hb)
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]);
409 static void addFramesNN(t_hbdata *hb, int frame)
412 #define DELTAFRAMES_HBE 10
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){
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
443 rvec dipole[2], xmol[3], xmean[2];
448 /* Self-interaction */
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)
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]);
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]);
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);
495 realE = pow(r, -3.0);
496 E = (t_E)(SCALEFACTOR_E * realE);
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]);
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]);
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]);
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]);
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);
543 double cosalpha = cos_angle(dipole[0],dipole[1]);
544 realE = cosalpha * pow(r, -3.0);
545 E = (t_E)(SCALEFACTOR_E * realE);
549 printf("Can't do that type of energy estimate: %i\n.", EEst);
556 static void storeHbEnergy(t_hbdata *hb, int d, int a, int h, t_E E, int frame){
557 /* hb - hbond data structure
561 E - estimate of the energy
562 frame - the current frame.
565 /* Store the estimated energy */
569 hb->hbE.E[d][a][h][frame] = E;
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)
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]))
591 printf("Couldn't find mirror of [%i, %i, %i], index \n",
599 static void add_frames(t_hbdata *hb,int nframes)
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);
611 srenew(hb->danr,hb->max_frames);
616 #define OFFSET(frame) (frame / 32)
617 #define MASK(frame) (1 << (frame % 32))
619 static void _set_hb(unsigned int hbexist[],unsigned int frame,gmx_bool bValue)
622 hbexist[OFFSET(frame)] |= MASK(frame);
624 hbexist[OFFSET(frame)] &= ~MASK(frame);
627 static gmx_bool is_hb(unsigned int hbexist[],int frame)
629 return ((hbexist[OFFSET(frame)] & MASK(frame)) != 0) ? 1 : 0;
632 static void set_hb(t_hbdata *hb,int id,int ih, int ia,int frame,int ihb)
634 unsigned int *ghptr=NULL;
637 ghptr = hb->hbmap[id][ia]->h[ih];
638 else if (ihb == hbDist)
639 ghptr = hb->hbmap[id][ia]->g[ih];
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);
652 pHist->frame[0] = frame;
656 if (pHist->p[pHist->len-1] != p) {
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. */
666 static PSTYPE getPshift(t_pShift pHist, int frame)
671 || (pHist.len > 0 && pHist.frame[0]>frame))
674 for (i=0; i<pHist.len; i++)
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)
690 t_hbond *hb = hbd->hbmap[id][ia];
691 int maxhydro = min(hbd->maxhydro,hbd->d.nhydro[id]);
692 int wlen = hbd->wordlen;
694 gmx_bool bGem = hbd->bGem;
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);
704 hb->nframes = frame-hb->n0;
705 /* We need a while loop here because hbonds may be returning
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++) {
723 set_hb(hbd,id,h,ia,frame,ihb);
725 if (p>=hbd->per->nper)
726 gmx_fatal(FARGS, "invalid shift: p=%u, nper=%u", p, hbd->per->nper);
728 addPshift(&(hbd->per->pHist[id][ia]), p, frame);
734 static void inc_nhbonds(t_donors *ddd,int d, int h)
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]++;
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)
753 if (a->grp[ai] != grp) {
755 fprintf(debug,"Acc. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
756 ai,a->grp[ai],grp,file,line);
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)
771 if (d->grp[di] != grp) {
773 fprintf(debug,"Don. group inconsist.. grp[%d] = %d, grp = %d (%s, %d)\n",
774 di,d->grp[di],grp,file,line);
780 #define donor_index(d,grp,i) _donor_index(d,grp,i,__FILE__,__LINE__)
782 static gmx_bool isInterchangable(t_hbdata *hb, int d, int a, int grpa, int grpd)
784 /* g_hbond doesn't allow overlapping groups */
788 donor_index(&hb->d,grpd,a) != NOTSET
789 && acceptor_index(&hb->a,grpa,d) != NOTSET;
793 static void add_hbond(t_hbdata *hb,int d,int a,int h,int grpd,int grpa,
794 int frame,gmx_bool bMerge,int ihb,gmx_bool bContact, PSTYPE p)
797 gmx_bool daSwap = FALSE;
799 if ((id = hb->d.dptr[d]) == NOTSET)
800 gmx_fatal(FARGS,"No donor atom %d",d+1);
801 else if (grpd != hb->d.grp[id])
802 gmx_fatal(FARGS,"Inconsistent donor groups, %d iso %d, atom %d",
803 grpd,hb->d.grp[id],d+1);
804 if ((ia = hb->a.aptr[a]) == NOTSET)
805 gmx_fatal(FARGS,"No acceptor atom %d",a+1);
806 else if (grpa != hb->a.grp[ia])
807 gmx_fatal(FARGS,"Inconsistent acceptor groups, %d iso %d, atom %d",
808 grpa,hb->a.grp[ia],a+1);
813 if (isInterchangable(hb, d, a, grpd, grpa) && d>a)
814 /* Then swap identity so that the id of d is lower then that of a.
816 * This should really be redundant by now, as is_hbond() now ought to return
817 * hbNo in the cases where this conditional is TRUE. */
824 /* Now repeat donor/acc check. */
825 if ((id = hb->d.dptr[d]) == NOTSET)
826 gmx_fatal(FARGS,"No donor atom %d",d+1);
827 else if (grpd != hb->d.grp[id])
828 gmx_fatal(FARGS,"Inconsistent donor groups, %d iso %d, atom %d",
829 grpd,hb->d.grp[id],d+1);
830 if ((ia = hb->a.aptr[a]) == NOTSET)
831 gmx_fatal(FARGS,"No acceptor atom %d",a+1);
832 else if (grpa != hb->a.grp[ia])
833 gmx_fatal(FARGS,"Inconsistent acceptor groups, %d iso %d, atom %d",
834 grpa,hb->a.grp[ia],a+1);
839 /* Loop over hydrogens to find which hydrogen is in this particular HB */
840 if ((ihb == hbHB) && !bMerge && !bContact) {
841 for(k=0; (k<hb->d.nhydro[id]); k++)
842 if (hb->d.hydro[id][k] == h)
844 if (k == hb->d.nhydro[id])
845 gmx_fatal(FARGS,"Donor %d does not have hydrogen %d (a = %d)",
852 if (hb->hbmap[id][ia] == NULL) {
853 snew(hb->hbmap[id][ia],1);
854 snew(hb->hbmap[id][ia]->h,hb->maxhydro);
855 snew(hb->hbmap[id][ia]->g,hb->maxhydro);
857 add_ff(hb,id,k,ia,frame,ihb,p);
860 /* Strange construction with frame >=0 is a relic from old code
861 * for selected hbond analysis. It may be necessary again if that
862 * is made to work again.
865 hh = hb->hbmap[id][ia]->history[k];
869 hb->hbmap[id][ia]->history[k] = hh | 2;
878 hb->hbmap[id][ia]->history[k] = hh | 1;
895 if (bMerge && daSwap)
896 h = hb->d.hydro[id][0];
897 /* Increment number if HBonds per H */
898 if (ihb == hbHB && !bContact)
899 inc_nhbonds(&(hb->d),d,h);
902 /* Now a redundant function. It might find use at some point though. */
903 static gmx_bool in_list(atom_id selection,int isize,atom_id *index)
909 for(i=0; (i<isize) && !bFound; i++)
910 if(selection == index[i])
916 static char *mkatomname(t_atoms *atoms,int i)
921 rnr = atoms->atom[i].resind;
922 sprintf(buf,"%4s%d%-4s",
923 *atoms->resinfo[rnr].name,atoms->resinfo[rnr].nr,*atoms->atomname[i]);
928 static void gen_datable(atom_id *index, int isize, unsigned char *datable, int natoms){
929 /* Generates table of all atoms and sets the ingroup bit for atoms in index[] */
932 for (i=0; i<isize; i++){
933 if (index[i] >= natoms)
934 gmx_fatal(FARGS,"Atom has index %d larger than number of atoms %d.",index[i],natoms);
935 datable[index[i]] |= INGROUP;
939 static void clear_datable_grp(unsigned char *datable, int size){
940 /* Clears group information from the table */
942 const char mask = !(char)INGROUP;
948 static void add_acc(t_acceptors *a,int ia,int grp)
950 if (a->nra >= a->max_nra) {
952 srenew(a->acc,a->max_nra);
953 srenew(a->grp,a->max_nra);
955 a->grp[a->nra] = grp;
956 a->acc[a->nra++] = ia;
959 static void search_acceptors(t_topology *top,int isize,
960 atom_id *index,t_acceptors *a,int grp,
962 gmx_bool bContact,gmx_bool bDoIt, unsigned char *datable)
967 for (i=0; (i<isize); i++) {
970 (((*top->atoms.atomname[n])[0] == 'O') ||
971 (bNitAcc && ((*top->atoms.atomname[n])[0] == 'N')))) &&
972 ISINGRP(datable[n])) {
973 datable[n] |= ACC; /* set the atom's acceptor flag in datable. */
978 snew(a->aptr,top->atoms.nr);
979 for(i=0; (i<top->atoms.nr); i++)
981 for(i=0; (i<a->nra); i++)
982 a->aptr[a->acc[i]] = i;
985 static void add_h2d(int id,int ih,t_donors *ddd)
989 for(i=0; (i<ddd->nhydro[id]); i++)
990 if (ddd->hydro[id][i] == ih) {
991 printf("Hm. This isn't the first time I found this donor (%d,%d)\n",
995 if (i == ddd->nhydro[id]) {
996 if (ddd->nhydro[id] >= MAXHYDRO)
997 gmx_fatal(FARGS,"Donor %d has more than %d hydrogens!",
998 ddd->don[id],MAXHYDRO);
999 ddd->hydro[id][i] = ih;
1004 static void add_dh(t_donors *ddd,int id,int ih,int grp, unsigned char *datable)
1008 if (ISDON(datable[id]) || !datable) {
1009 if (ddd->dptr[id] == NOTSET) { /* New donor */
1015 if (i == ddd->nrd) {
1016 if (ddd->nrd >= ddd->max_nrd) {
1017 ddd->max_nrd += 128;
1018 srenew(ddd->don,ddd->max_nrd);
1019 srenew(ddd->nhydro,ddd->max_nrd);
1020 srenew(ddd->hydro,ddd->max_nrd);
1021 srenew(ddd->nhbonds,ddd->max_nrd);
1022 srenew(ddd->grp,ddd->max_nrd);
1024 ddd->don[ddd->nrd] = id;
1025 ddd->nhydro[ddd->nrd] = 0;
1026 ddd->grp[ddd->nrd] = grp;
1033 printf("Warning: Atom %d is not in the d/a-table!\n", id);
1036 static void search_donors(t_topology *top, int isize, atom_id *index,
1037 t_donors *ddd,int grp,gmx_bool bContact,gmx_bool bDoIt,
1038 unsigned char *datable)
1041 t_functype func_type;
1042 t_ilist *interaction;
1047 snew(ddd->dptr,top->atoms.nr);
1048 for(i=0; (i<top->atoms.nr); i++)
1049 ddd->dptr[i] = NOTSET;
1054 for(i=0; (i<isize); i++) {
1055 datable[index[i]] |= DON;
1056 add_dh(ddd,index[i],-1,grp,datable);
1060 for(func_type=0; (func_type < F_NRE); func_type++) {
1061 interaction=&(top->idef.il[func_type]);
1062 if (func_type == F_POSRES)
1063 { /* The ilist looks strange for posre. Bug in grompp?
1064 * We don't need posre interactions for hbonds anyway.*/
1067 for(i=0; i < interaction->nr;
1068 i+=interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms+1) {
1070 if (func_type != top->idef.functype[interaction->iatoms[i]]) {
1071 fprintf(stderr,"Error in func_type %s",
1072 interaction_function[func_type].longname);
1076 /* check out this functype */
1077 if (func_type == F_SETTLE) {
1078 nr1=interaction->iatoms[i+1];
1080 if (ISINGRP(datable[nr1])) {
1081 if (ISINGRP(datable[nr1+1])) {
1082 datable[nr1] |= DON;
1083 add_dh(ddd,nr1,nr1+1,grp,datable);
1085 if (ISINGRP(datable[nr1+2])) {
1086 datable[nr1] |= DON;
1087 add_dh(ddd,nr1,nr1+2,grp,datable);
1091 else if (IS_CHEMBOND(func_type)) {
1092 for (j=0; j<2; j++) {
1093 nr1=interaction->iatoms[i+1+j];
1094 nr2=interaction->iatoms[i+2-j];
1095 if ((*top->atoms.atomname[nr1][0] == 'H') &&
1096 ((*top->atoms.atomname[nr2][0] == 'O') ||
1097 (*top->atoms.atomname[nr2][0] == 'N')) &&
1098 ISINGRP(datable[nr1]) && ISINGRP(datable[nr2])) {
1099 datable[nr2] |= DON;
1100 add_dh(ddd,nr2,nr1,grp,datable);
1107 for(func_type=0; func_type < F_NRE; func_type++) {
1108 interaction=&top->idef.il[func_type];
1109 for(i=0; i < interaction->nr;
1110 i+=interaction_function[top->idef.functype[interaction->iatoms[i]]].nratoms+1) {
1112 if (func_type != top->idef.functype[interaction->iatoms[i]])
1113 gmx_incons("function type in search_donors");
1115 if ( interaction_function[func_type].flags & IF_VSITE ) {
1116 nr1=interaction->iatoms[i+1];
1117 if ( *top->atoms.atomname[nr1][0] == 'H') {
1120 while (!stop && ( *top->atoms.atomname[nr2][0] == 'H'))
1125 if ( !stop && ( ( *top->atoms.atomname[nr2][0] == 'O') ||
1126 ( *top->atoms.atomname[nr2][0] == 'N') ) &&
1127 ISINGRP(datable[nr1]) && ISINGRP(datable[nr2])) {
1128 datable[nr2] |= DON;
1129 add_dh(ddd,nr2,nr1,grp,datable);
1139 static t_gridcell ***init_grid(gmx_bool bBox,rvec box[],real rcut,ivec ngrid)
1145 for(i=0; i<DIM; i++)
1146 ngrid[i]=(box[i][i]/(1.2*rcut));
1148 if ( !bBox || (ngrid[XX]<3) || (ngrid[YY]<3) || (ngrid[ZZ]<3) )
1149 for(i=0; i<DIM; i++)
1152 printf("\nWill do grid-seach on %dx%dx%d grid, rcut=%g\n",
1153 ngrid[XX],ngrid[YY],ngrid[ZZ],rcut);
1154 snew(grid,ngrid[ZZ]);
1155 for (z=0; z<ngrid[ZZ]; z++) {
1156 snew((grid)[z],ngrid[YY]);
1157 for (y=0; y<ngrid[YY]; y++)
1158 snew((grid)[z][y],ngrid[XX]);
1163 static void control_pHist(t_hbdata *hb, int nframes)
1167 for (i=0;i<hb->d.nrd;i++)
1168 for (j=0;j<hb->a.nra;j++)
1169 if (hb->per->pHist[i][j].len != 0)
1170 for (k=hb->hbmap[i][j][0].n0; k<nframes; k++) {
1171 p = getPshift(hb->per->pHist[i][j], k);
1172 if (p>hb->per->nper)
1173 fprintf(stderr, "Weird stuff in pHist[%i][%i].p at frame %i: p=%i\n",
1178 static void reset_nhbonds(t_donors *ddd)
1182 for(i=0; (i<ddd->nrd); i++)
1183 for(j=0; (j<MAXHH); j++)
1184 ddd->nhbonds[i][j] = 0;
1187 void pbc_correct_gem(rvec dx,matrix box,rvec hbox);
1189 static void build_grid(t_hbdata *hb,rvec x[], rvec xshell,
1190 gmx_bool bBox, matrix box, rvec hbox,
1191 real rcut, real rshell,
1192 ivec ngrid, t_gridcell ***grid)
1194 int i,m,gr,xi,yi,zi,nr;
1197 rvec invdelta,dshell,xtemp={0,0,0};
1199 gmx_bool bDoRshell,bInShell,bAcc;
1204 bDoRshell = (rshell > 0);
1205 rshell2 = sqr(rshell);
1208 #define DBB(x) if (debug && bDebug) fprintf(debug,"build_grid, line %d, %s = %d\n",__LINE__,#x,x)
1210 for(m=0; m<DIM; m++) {
1211 hbox[m]=box[m][m]*0.5;
1213 invdelta[m]=ngrid[m]/box[m][m];
1214 if (1/invdelta[m] < rcut)
1215 gmx_fatal(FARGS,"Your computational box has shrunk too much.\n"
1216 "%s can not handle this situation, sorry.\n",
1225 /* resetting atom counts */
1226 for(gr=0; (gr<grNR); gr++) {
1227 for (zi=0; zi<ngrid[ZZ]; zi++)
1228 for (yi=0; yi<ngrid[YY]; yi++)
1229 for (xi=0; xi<ngrid[XX]; xi++) {
1230 grid[zi][yi][xi].d[gr].nr=0;
1231 grid[zi][yi][xi].a[gr].nr=0;
1235 /* put atoms in grid cells */
1236 for(bAcc=FALSE; (bAcc<=TRUE); bAcc++) {
1246 for(i=0; (i<nr); i++) {
1247 /* check if we are inside the shell */
1248 /* if bDoRshell=FALSE then bInShell=TRUE always */
1252 rvec_sub(x[ad[i]],xshell,dshell);
1254 if (FALSE && !hb->bGem) {
1255 for(m=DIM-1; m>=0 && bInShell; m--) {
1256 if ( dshell[m] < -hbox[m] )
1257 rvec_inc(dshell,box[m]);
1258 else if ( dshell[m] >= hbox[m] )
1259 dshell[m] -= 2*hbox[m];
1260 /* if we're outside the cube, we're outside the sphere also! */
1261 if ( (dshell[m]>rshell) || (-dshell[m]>rshell) )
1265 gmx_bool bDone = FALSE;
1269 for(m=DIM-1; m>=0 && bInShell; m--) {
1270 if ( dshell[m] < -hbox[m] ) {
1272 rvec_inc(dshell,box[m]);
1274 if ( dshell[m] >= hbox[m] ) {
1276 dshell[m] -= 2*hbox[m];
1280 for(m=DIM-1; m>=0 && bInShell; m--) {
1281 /* if we're outside the cube, we're outside the sphere also! */
1282 if ( (dshell[m]>rshell) || (-dshell[m]>rshell) )
1287 /* if we're inside the cube, check if we're inside the sphere */
1289 bInShell = norm2(dshell) < rshell2;
1295 copy_rvec(x[ad[i]], xtemp);
1296 pbc_correct_gem(x[ad[i]], box, hbox);
1298 for(m=DIM-1; m>=0; m--) {
1299 if (TRUE || !hb->bGem){
1300 /* put atom in the box */
1301 while( x[ad[i]][m] < 0 )
1302 rvec_inc(x[ad[i]],box[m]);
1303 while( x[ad[i]][m] >= box[m][m] )
1304 rvec_dec(x[ad[i]],box[m]);
1306 /* determine grid index of atom */
1307 grididx[m]=x[ad[i]][m]*invdelta[m];
1308 grididx[m] = (grididx[m]+ngrid[m]) % ngrid[m];
1311 copy_rvec(xtemp, x[ad[i]]); /* copy back */
1315 range_check(gx,0,ngrid[XX]);
1316 range_check(gy,0,ngrid[YY]);
1317 range_check(gz,0,ngrid[ZZ]);
1321 /* add atom to grid cell */
1323 newgrid=&(grid[gz][gy][gx].a[gr]);
1325 newgrid=&(grid[gz][gy][gx].d[gr]);
1326 if (newgrid->nr >= newgrid->maxnr) {
1328 DBB(newgrid->maxnr);
1329 srenew(newgrid->atoms, newgrid->maxnr);
1332 newgrid->atoms[newgrid->nr]=ad[i];
1340 static void count_da_grid(ivec ngrid, t_gridcell ***grid, t_icell danr)
1344 for(gr=0; (gr<grNR); gr++) {
1346 for (zi=0; zi<ngrid[ZZ]; zi++)
1347 for (yi=0; yi<ngrid[YY]; yi++)
1348 for (xi=0; xi<ngrid[XX]; xi++) {
1349 danr[gr] += grid[zi][yi][xi].d[gr].nr;
1355 * Without a box, the grid is 1x1x1, so all loops are 1 long.
1356 * With a rectangular box (bTric==FALSE) all loops are 3 long.
1357 * With a triclinic box all loops are 3 long, except when a cell is
1358 * located next to one of the box edges which is not parallel to the
1359 * x/y-plane, in that case all cells in a line or layer are searched.
1360 * This could be implemented slightly more efficient, but the code
1361 * would get much more complicated.
1363 #define B(n,x,bTric,bEdge) ((n==1) ? x : bTric&&(bEdge) ? 0 : (x-1))
1364 #define E(n,x,bTric,bEdge) ((n==1) ? x : bTric&&(bEdge) ? n-1 : (x+1))
1365 #define GRIDMOD(j,n) (j+n)%(n)
1366 #define LOOPGRIDINNER(x,y,z,xx,yy,zz,xo,yo,zo,n,bTric) \
1367 for(zz=B(n[ZZ],zo,bTric,FALSE); zz<=E(n[ZZ],zo,bTric,FALSE); zz++) { \
1368 z=GRIDMOD(zz,n[ZZ]); \
1369 for(yy=B(n[YY],yo,bTric,z==0||z==n[ZZ]-1); \
1370 yy<=E(n[YY],yo,bTric,z==0||z==n[ZZ]-1); yy++) { \
1371 y=GRIDMOD(yy,n[YY]); \
1372 for(xx=B(n[XX],xo,bTric,y==0||y==n[YY]-1||z==0||z==n[ZZ]-1); \
1373 xx<=E(n[XX],xo,bTric,y==0||y==n[YY]-1||z==0||z==n[ZZ]-1); xx++) { \
1374 x=GRIDMOD(xx,n[XX]);
1375 #define ENDLOOPGRIDINNER \
1381 static void dump_grid(FILE *fp, ivec ngrid, t_gridcell ***grid)
1383 int gr,x,y,z,sum[grNR];
1385 fprintf(fp,"grid %dx%dx%d\n",ngrid[XX],ngrid[YY],ngrid[ZZ]);
1386 for (gr=0; gr<grNR; gr++) {
1388 fprintf(fp,"GROUP %d (%s)\n",gr,grpnames[gr]);
1389 for (z=0; z<ngrid[ZZ]; z+=2) {
1390 fprintf(fp,"Z=%d,%d\n",z,z+1);
1391 for (y=0; y<ngrid[YY]; y++) {
1392 for (x=0; x<ngrid[XX]; x++) {
1393 fprintf(fp,"%3d",grid[x][y][z].d[gr].nr);
1394 sum[gr]+=grid[z][y][x].d[gr].nr;
1395 fprintf(fp,"%3d",grid[x][y][z].a[gr].nr);
1396 sum[gr]+=grid[z][y][x].a[gr].nr;
1400 if ( (z+1) < ngrid[ZZ] )
1401 for (x=0; x<ngrid[XX]; x++) {
1402 fprintf(fp,"%3d",grid[z+1][y][x].d[gr].nr);
1403 sum[gr]+=grid[z+1][y][x].d[gr].nr;
1404 fprintf(fp,"%3d",grid[z+1][y][x].a[gr].nr);
1405 sum[gr]+=grid[z+1][y][x].a[gr].nr;
1411 fprintf(fp,"TOTALS:");
1412 for (gr=0; gr<grNR; gr++)
1413 fprintf(fp," %d=%d",gr,sum[gr]);
1417 /* New GMX record! 5 * in a row. Congratulations!
1418 * Sorry, only four left.
1420 static void free_grid(ivec ngrid, t_gridcell ****grid)
1423 t_gridcell ***g = *grid;
1425 for (z=0; z<ngrid[ZZ]; z++) {
1426 for (y=0; y<ngrid[YY]; y++) {
1435 static void pbc_correct(rvec dx,matrix box,rvec hbox)
1438 for(m=DIM-1; m>=0; m--) {
1439 if ( dx[m] < -hbox[m] )
1440 rvec_inc(dx,box[m]);
1441 else if ( dx[m] >= hbox[m] )
1442 rvec_dec(dx,box[m]);
1446 void pbc_correct_gem(rvec dx,matrix box,rvec hbox)
1449 gmx_bool bDone = FALSE;
1452 for(m=DIM-1; m>=0; m--) {
1453 if ( dx[m] < -hbox[m] ) {
1455 rvec_inc(dx,box[m]);
1457 if ( dx[m] >= hbox[m] ) {
1459 rvec_dec(dx,box[m]);
1465 /* Added argument r2cut, changed contact and implemented
1466 * use of second cut-off.
1467 * - Erik Marklund, June 29, 2006
1469 static int is_hbond(t_hbdata *hb,int grpd,int grpa,int d,int a,
1470 real rcut, real r2cut, real ccut,
1471 rvec x[], gmx_bool bBox, matrix box,rvec hbox,
1472 real *d_ha, real *ang,gmx_bool bDA,int *hhh,
1473 gmx_bool bContact, gmx_bool bMerge, PSTYPE *p)
1476 rvec r_da,r_ha,r_dh, r={0, 0, 0};
1478 real rc2,r2c2,rda2,rha2,ca;
1479 gmx_bool HAinrange = FALSE; /* If !bDA. Needed for returning hbDist in a correct way. */
1480 gmx_bool daSwap = FALSE;
1485 if (((id = donor_index(&hb->d,grpd,d)) == NOTSET) ||
1486 ((ja = acceptor_index(&hb->a,grpa,a)) == NOTSET))
1492 rvec_sub(x[d],x[a],r_da);
1493 /* Insert projection code here */
1495 if (bMerge && d>a && isInterchangable(hb, d, a, grpd, grpa))
1497 /* Then this hbond/contact will be found again, or it has already been found. */
1501 if (d>a && bMerge && isInterchangable(hb, d, a, grpd, grpa)) { /* acceptor is also a donor and vice versa? */
1503 daSwap = TRUE; /* If so, then their history should be filed with donor and acceptor swapped. */
1506 copy_rvec(r_da, r); /* Save this for later */
1507 pbc_correct_gem(r_da,box,hbox);
1509 pbc_correct_gem(r_da,box,hbox);
1512 rda2 = iprod(r_da,r_da);
1515 if (daSwap && grpa == grpd)
1519 calcBoxDistance(hb->per->P, r, ri);
1520 *p = periodicIndex(ri, hb->per, daSwap); /* find (or add) periodicity index. */
1524 else if (rda2 < r2c2)
1531 if (bDA && (rda2 > rc2))
1534 for(h=0; (h < hb->d.nhydro[id]); h++) {
1535 hh = hb->d.hydro[id][h];
1538 rvec_sub(x[hh],x[a],r_ha);
1540 pbc_correct_gem(r_ha,box,hbox);
1542 rha2 = iprod(r_ha,r_ha);
1546 calcBoxDistance(hb->per->P, r, ri);
1547 *p = periodicIndex(ri, hb->per, daSwap); /* find periodicity index. */
1550 if (bDA || (!bDA && (rha2 <= rc2))) {
1551 rvec_sub(x[d],x[hh],r_dh);
1554 pbc_correct_gem(r_dh,box,hbox);
1556 pbc_correct_gem(r_dh,box,hbox);
1561 ca = cos_angle(r_dh,r_da);
1562 /* if angle is smaller, cos is larger */
1565 *d_ha = sqrt(bDA?rda2:rha2);
1571 if (bDA || (!bDA && HAinrange))
1577 /* Fixed previously undiscovered bug in the merge
1578 code, where the last frame of each hbond disappears.
1579 - Erik Marklund, June 1, 2006 */
1580 /* Added the following arguments:
1581 * ptmp[] - temporary periodicity hisory
1582 * a1 - identity of first acceptor/donor
1583 * a2 - identity of second acceptor/donor
1584 * - Erik Marklund, FEB 20 2010 */
1586 /* Merging is now done on the fly, so do_merge is most likely obsolete now.
1587 * Will do some more testing before removing the function entirely.
1588 * - Erik Marklund, MAY 10 2010 */
1589 static void do_merge(t_hbdata *hb,int ntmp,
1590 unsigned int htmp[],unsigned int gtmp[],PSTYPE ptmp[],
1591 t_hbond *hb0,t_hbond *hb1, int a1, int a2)
1593 /* Here we need to make sure we're treating periodicity in
1594 * the right way for the geminate recombination kinetics. */
1596 int m,mm,n00,n01,nn0,nnframes;
1600 /* Decide where to start from when merging */
1604 nnframes = max(n00 + hb0->nframes,n01 + hb1->nframes) - nn0;
1605 /* Initiate tmp arrays */
1606 for(m=0; (m<ntmp); m++) {
1611 /* Fill tmp arrays with values due to first HB */
1612 /* Once again '<' had to be replaced with '<='
1613 to catch the last frame in which the hbond
1615 - Erik Marklund, June 1, 2006 */
1616 for(m=0; (m<=hb0->nframes); m++) {
1618 htmp[mm] = is_hb(hb0->h[0],m);
1620 pm = getPshift(hb->per->pHist[a1][a2], m+hb0->n0);
1621 if (pm > hb->per->nper)
1622 gmx_fatal(FARGS, "Illegal shift!");
1624 ptmp[mm] = pm; /*hb->per->pHist[a1][a2][m];*/
1627 /* If we're doing geminate recompbination we usually don't need the distances.
1628 * Let's save some memory and time. */
1629 if (TRUE || !hb->bGem || hb->per->gemtype == gemAD){
1630 for(m=0; (m<=hb0->nframes); m++) {
1632 gtmp[mm] = is_hb(hb0->g[0],m);
1636 for(m=0; (m<=hb1->nframes); m++) {
1638 htmp[mm] = htmp[mm] || is_hb(hb1->h[0],m);
1639 gtmp[mm] = gtmp[mm] || is_hb(hb1->g[0],m);
1640 if (hb->bGem /* && ptmp[mm] != 0 */) {
1642 /* If this hbond has been seen before with donor and acceptor swapped,
1643 * then we need to find the mirrored (*-1) periodicity vector to truely
1644 * merge the hbond history. */
1645 pm = findMirror(getPshift(hb->per->pHist[a2][a1],m+hb1->n0), hb->per->p2i, hb->per->nper);
1646 /* Store index of mirror */
1647 if (pm > hb->per->nper)
1648 gmx_fatal(FARGS, "Illegal shift!");
1652 /* Reallocate target array */
1653 if (nnframes > hb0->maxframes) {
1654 srenew(hb0->h[0],4+nnframes/hb->wordlen);
1655 srenew(hb0->g[0],4+nnframes/hb->wordlen);
1657 if (NULL != hb->per->pHist)
1659 clearPshift(&(hb->per->pHist[a1][a2]));
1662 /* Copy temp array to target array */
1663 for(m=0; (m<=nnframes); m++) {
1664 _set_hb(hb0->h[0],m,htmp[m]);
1665 _set_hb(hb0->g[0],m,gtmp[m]);
1667 addPshift(&(hb->per->pHist[a1][a2]), ptmp[m], m+nn0);
1670 /* Set scalar variables */
1672 hb0->maxframes = nnframes;
1675 /* Added argument bContact for nicer output.
1676 * Erik Marklund, June 29, 2006
1678 static void merge_hb(t_hbdata *hb,gmx_bool bTwo, gmx_bool bContact){
1679 int i,inrnew,indnew,j,ii,jj,m,id,ia,grp,ogrp,ntmp;
1680 unsigned int *htmp,*gtmp;
1685 indnew = hb->nrdist;
1687 /* Check whether donors are also acceptors */
1688 printf("Merging hbonds with Acceptor and Donor swapped\n");
1690 ntmp = 2*hb->max_frames;
1694 for(i=0; (i<hb->d.nrd); i++) {
1695 fprintf(stderr,"\r%d/%d",i+1,hb->d.nrd);
1697 ii = hb->a.aptr[id];
1698 for(j=0; (j<hb->a.nra); j++) {
1700 jj = hb->d.dptr[ia];
1701 if ((id != ia) && (ii != NOTSET) && (jj != NOTSET) &&
1702 (!bTwo || (bTwo && (hb->d.grp[i] != hb->a.grp[j])))) {
1703 hb0 = hb->hbmap[i][j];
1704 hb1 = hb->hbmap[jj][ii];
1705 if (hb0 && hb1 && ISHB(hb0->history[0]) && ISHB(hb1->history[0])) {
1706 do_merge(hb,ntmp,htmp,gtmp,ptmp,hb0,hb1,i,j);
1707 if (ISHB(hb1->history[0]))
1709 else if (ISDIST(hb1->history[0]))
1713 gmx_incons("No contact history");
1715 gmx_incons("Neither hydrogen bond nor distance");
1719 clearPshift(&(hb->per->pHist[jj][ii]));
1723 hb1->history[0] = hbNo;
1728 fprintf(stderr,"\n");
1729 printf("- Reduced number of hbonds from %d to %d\n",hb->nrhb,inrnew);
1730 printf("- Reduced number of distances from %d to %d\n",hb->nrdist,indnew);
1732 hb->nrdist = indnew;
1738 static void do_nhb_dist(FILE *fp,t_hbdata *hb,real t)
1740 int i,j,k,n_bound[MAXHH],nbtot;
1744 /* Set array to 0 */
1745 for(k=0; (k<MAXHH); k++)
1747 /* Loop over possible donors */
1748 for(i=0; (i<hb->d.nrd); i++) {
1749 for(j=0; (j<hb->d.nhydro[i]); j++)
1750 n_bound[hb->d.nhbonds[i][j]]++;
1752 fprintf(fp,"%12.5e",t);
1754 for(k=0; (k<MAXHH); k++) {
1755 fprintf(fp," %8d",n_bound[k]);
1756 nbtot += n_bound[k]*k;
1758 fprintf(fp," %8d\n",nbtot);
1761 /* Added argument bContact in do_hblife(...). Also
1762 * added support for -contact in function body.
1763 * - Erik Marklund, May 31, 2006 */
1764 /* Changed the contact code slightly.
1765 * - Erik Marklund, June 29, 2006
1767 static void do_hblife(const char *fn,t_hbdata *hb,gmx_bool bMerge,gmx_bool bContact,
1768 const output_env_t oenv)
1771 const char *leg[] = { "p(t)", "t p(t)" };
1773 int i,j,j0,k,m,nh,ihb,ohb,nhydro,ndump=0;
1774 int nframes = hb->nframes;
1777 double sum,integral;
1780 snew(h,hb->maxhydro);
1781 snew(histo,nframes+1);
1782 /* Total number of hbonds analyzed here */
1783 for(i=0; (i<hb->d.nrd); i++) {
1784 for(k=0; (k<hb->a.nra); k++) {
1785 hbh = hb->hbmap[i][k];
1797 for(m=0; (m<hb->maxhydro); m++)
1799 h[nhydro++] = bContact ? hbh->g[m] : hbh->h[m];
1802 for(nh=0; (nh<nhydro); nh++) {
1806 /* Changed '<' into '<=' below, just like I
1807 did in the hbm-output-loop in the main code.
1808 - Erik Marklund, May 31, 2006
1810 for(j=0; (j<=hbh->nframes); j++) {
1811 ihb = is_hb(h[nh],j);
1812 if (debug && (ndump < 10))
1813 fprintf(debug,"%5d %5d\n",j,ihb);
1829 fprintf(stderr,"\n");
1831 fp = xvgropen(fn,"Uninterrupted contact lifetime",output_env_get_xvgr_tlabel(oenv),"()",oenv);
1833 fp = xvgropen(fn,"Uninterrupted hydrogen bond lifetime",output_env_get_xvgr_tlabel(oenv),"()",
1836 xvgr_legend(fp,asize(leg),leg,oenv);
1838 while ((j0 > 0) && (histo[j0] == 0))
1841 for(i=0; (i<=j0); i++)
1843 dt = hb->time[1]-hb->time[0];
1846 for(i=1; (i<=j0); i++) {
1847 t = hb->time[i] - hb->time[0] - 0.5*dt;
1848 x1 = t*histo[i]/sum;
1849 fprintf(fp,"%8.3f %10.3e %10.3e\n",t,histo[i]/sum,x1);
1854 printf("%s lifetime = %.2f ps\n", bContact?"Contact":"HB", integral);
1855 printf("Note that the lifetime obtained in this manner is close to useless\n");
1856 printf("Use the -ac option instead and check the Forward lifetime\n");
1857 please_cite(stdout,"Spoel2006b");
1862 /* Changed argument bMerge into oneHB to handle contacts properly.
1863 * - Erik Marklund, June 29, 2006
1865 static void dump_ac(t_hbdata *hb,gmx_bool oneHB,int nDump)
1868 int i,j,k,m,nd,ihb,idist;
1869 int nframes = hb->nframes;
1875 fp = ffopen("debug-ac.xvg","w");
1876 for(j=0; (j<nframes); j++) {
1877 fprintf(fp,"%10.3f",hb->time[j]);
1878 for(i=nd=0; (i<hb->d.nrd) && (nd < nDump); i++) {
1879 for(k=0; (k<hb->a.nra) && (nd < nDump); k++) {
1882 hbh = hb->hbmap[i][k];
1885 ihb = is_hb(hbh->h[0],j);
1886 idist = is_hb(hbh->g[0],j);
1891 for(m=0; (m<hb->maxhydro) && !ihb ; m++) {
1892 ihb = ihb || ((hbh->h[m]) && is_hb(hbh->h[m],j));
1893 idist = idist || ((hbh->g[m]) && is_hb(hbh->g[m],j));
1895 /* This is not correct! */
1896 /* What isn't correct? -Erik M */
1900 fprintf(fp," %1d-%1d",ihb,idist);
1910 static real calc_dg(real tau,real temp)
1918 return kbt*log(kbt*tau/PLANCK);
1922 int n0,n1,nparams,ndelta;
1924 real *t,*ct,*nt,*kt,*sigma_ct,*sigma_nt,*sigma_kt;
1928 #include <gsl/gsl_multimin.h>
1929 #include <gsl/gsl_sf.h>
1930 #include <gsl/gsl_version.h>
1932 static double my_f(const gsl_vector *v,void *params)
1934 t_luzar *tl = (t_luzar *)params;
1936 double tol=1e-16,chi2=0;
1940 for(i=0; (i<tl->nparams); i++) {
1941 tl->kkk[i] = gsl_vector_get(v, i);
1946 for(i=tl->n0; (i<tl->n1); i+=tl->ndelta) {
1947 di=sqr(k*tl->sigma_ct[i]) + sqr(kp*tl->sigma_nt[i]) + sqr(tl->sigma_kt[i]);
1950 chi2 += sqr(k*tl->ct[i]-kp*tl->nt[i]-tl->kt[i])/di;
1953 fprintf(stderr,"WARNING: sigma_ct = %g, sigma_nt = %g, sigma_kt = %g\n"
1954 "di = %g k = %g kp = %g\n",tl->sigma_ct[i],
1955 tl->sigma_nt[i],tl->sigma_kt[i],di,k,kp);
1958 chi2 = 0.3*sqr(k-0.6)+0.7*sqr(kp-1.3);
1963 static real optimize_luzar_parameters(FILE *fp,t_luzar *tl,int maxiter,
1971 const gsl_multimin_fminimizer_type *T;
1972 gsl_multimin_fminimizer *s;
1975 gsl_multimin_function my_func;
1978 my_func.n = tl->nparams;
1979 my_func.params = (void *) tl;
1981 /* Starting point */
1982 x = gsl_vector_alloc (my_func.n);
1983 for(i=0; (i<my_func.n); i++)
1984 gsl_vector_set (x, i, tl->kkk[i]);
1986 /* Step size, different for each of the parameters */
1987 dx = gsl_vector_alloc (my_func.n);
1988 for(i=0; (i<my_func.n); i++)
1989 gsl_vector_set (dx, i, 0.01*tl->kkk[i]);
1991 T = gsl_multimin_fminimizer_nmsimplex;
1992 s = gsl_multimin_fminimizer_alloc (T, my_func.n);
1994 gsl_multimin_fminimizer_set (s, &my_func, x, dx);
1995 gsl_vector_free (x);
1996 gsl_vector_free (dx);
1999 fprintf(fp,"%5s %12s %12s %12s %12s\n","Iter","k","kp","NM Size","Chi2");
2003 status = gsl_multimin_fminimizer_iterate (s);
2006 gmx_fatal(FARGS,"Something went wrong in the iteration in minimizer %s",
2007 gsl_multimin_fminimizer_name(s));
2009 d2 = gsl_multimin_fminimizer_minimum(s);
2010 size = gsl_multimin_fminimizer_size(s);
2011 status = gsl_multimin_test_size(size,tol);
2013 if (status == GSL_SUCCESS)
2015 fprintf(fp,"Minimum found using %s at:\n",
2016 gsl_multimin_fminimizer_name(s));
2019 fprintf(fp,"%5d", iter);
2020 for(i=0; (i<my_func.n); i++)
2021 fprintf(fp," %12.4e",gsl_vector_get (s->x,i));
2022 fprintf (fp," %12.4e %12.4e\n",size,d2);
2025 while ((status == GSL_CONTINUE) && (iter < maxiter));
2027 gsl_multimin_fminimizer_free (s);
2032 static real quality_of_fit(real chi2,int N)
2034 return gsl_sf_gamma_inc_Q((N-2)/2.0,chi2/2.0);
2038 static real optimize_luzar_parameters(FILE *fp,t_luzar *tl,int maxiter,
2041 fprintf(stderr,"This program needs the GNU scientific library to work.\n");
2045 static real quality_of_fit(real chi2,int N)
2047 fprintf(stderr,"This program needs the GNU scientific library to work.\n");
2054 static real compute_weighted_rates(int n,real t[],real ct[],real nt[],
2055 real kt[],real sigma_ct[],real sigma_nt[],
2056 real sigma_kt[],real *k,real *kp,
2057 real *sigma_k,real *sigma_kp,
2063 real kkk=0,kkp=0,kk2=0,kp2=0,chi2;
2068 for(i=0; (i<n); i++) {
2069 if (t[i] >= fit_start)
2080 tl.sigma_ct = sigma_ct;
2081 tl.sigma_nt = sigma_nt;
2082 tl.sigma_kt = sigma_kt;
2086 chi2 = optimize_luzar_parameters(debug,&tl,1000,1e-3);
2088 *kp = tl.kkk[1] = *kp;
2090 for(j=0; (j<NK); j++) {
2091 (void) optimize_luzar_parameters(debug,&tl,1000,1e-3);
2094 kk2 += sqr(tl.kkk[0]);
2095 kp2 += sqr(tl.kkk[1]);
2098 *sigma_k = sqrt(kk2/NK - sqr(kkk/NK));
2099 *sigma_kp = sqrt(kp2/NK - sqr(kkp/NK));
2104 static void smooth_tail(int n,real t[],real c[],real sigma_c[],real start,
2105 const output_env_t oenv)
2108 real e_1,fitparm[4];
2112 for(i=0; (i<n); i++)
2120 do_lmfit(n,c,sigma_c,0,t,start,t[n-1],oenv,bDebugMode(),effnEXP2,fitparm,0);
2123 void analyse_corr(int n,real t[],real ct[],real nt[],real kt[],
2124 real sigma_ct[],real sigma_nt[],real sigma_kt[],
2125 real fit_start,real temp,real smooth_tail_start,
2126 const output_env_t oenv)
2129 real k=1,kp=1,kow=1;
2130 real Q=0,chi22,chi2,dg,dgp,tau_hb,dtau,tau_rlx,e_1,dt,sigma_k,sigma_kp,ddg;
2131 double tmp,sn2=0,sc2=0,sk2=0,scn=0,sck=0,snk=0;
2132 gmx_bool bError = (sigma_ct != NULL) && (sigma_nt != NULL) && (sigma_kt != NULL);
2134 if (smooth_tail_start >= 0) {
2135 smooth_tail(n,t,ct,sigma_ct,smooth_tail_start,oenv);
2136 smooth_tail(n,t,nt,sigma_nt,smooth_tail_start,oenv);
2137 smooth_tail(n,t,kt,sigma_kt,smooth_tail_start,oenv);
2139 for(i0=0; (i0<n-2) && ((t[i0]-t[0]) < fit_start); i0++)
2142 for(i=i0; (i<n); i++) {
2150 printf("Hydrogen bond thermodynamics at T = %g K\n",temp);
2151 tmp = (sn2*sc2-sqr(scn));
2152 if ((tmp > 0) && (sn2 > 0)) {
2153 k = (sn2*sck-scn*snk)/tmp;
2154 kp = (k*scn-snk)/sn2;
2157 for(i=i0; (i<n); i++) {
2158 chi2 += sqr(k*ct[i]-kp*nt[i]-kt[i]);
2160 chi22 = compute_weighted_rates(n,t,ct,nt,kt,sigma_ct,sigma_nt,
2162 &sigma_k,&sigma_kp,fit_start);
2163 Q = quality_of_fit(chi2,2);
2164 ddg = BOLTZ*temp*sigma_k/k;
2165 printf("Fitting paramaters chi^2 = %10g, Quality of fit = %10g\n",
2167 printf("The Rate and Delta G are followed by an error estimate\n");
2168 printf("----------------------------------------------------------\n"
2169 "Type Rate (1/ps) Sigma Time (ps) DG (kJ/mol) Sigma\n");
2170 printf("Forward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
2171 k,sigma_k,1/k,calc_dg(1/k,temp),ddg);
2172 ddg = BOLTZ*temp*sigma_kp/kp;
2173 printf("Backward %10.3f %6.2f %8.3f %10.3f %6.2f\n",
2174 kp,sigma_kp,1/kp,calc_dg(1/kp,temp),ddg);
2178 for(i=i0; (i<n); i++) {
2179 chi2 += sqr(k*ct[i]-kp*nt[i]-kt[i]);
2181 printf("Fitting parameters chi^2 = %10g\nQ = %10g\n",
2183 printf("--------------------------------------------------\n"
2184 "Type Rate (1/ps) Time (ps) DG (kJ/mol) Chi^2\n");
2185 printf("Forward %10.3f %8.3f %10.3f %10g\n",
2186 k,1/k,calc_dg(1/k,temp),chi2);
2187 printf("Backward %10.3f %8.3f %10.3f\n",
2188 kp,1/kp,calc_dg(1/kp,temp));
2193 printf("One-way %10.3f %s%8.3f %10.3f\n",
2194 kow,bError ? " " : "",1/kow,calc_dg(1/kow,temp));
2197 printf(" - Numerical problems computing HB thermodynamics:\n"
2198 "sc2 = %g sn2 = %g sk2 = %g sck = %g snk = %g scn = %g\n",
2199 sc2,sn2,sk2,sck,snk,scn);
2200 /* Determine integral of the correlation function */
2201 tau_hb = evaluate_integral(n,t,ct,NULL,(t[n-1]-t[0])/2,&dtau);
2202 printf("Integral %10.3f %s%8.3f %10.3f\n",1/tau_hb,
2203 bError ? " " : "",tau_hb,calc_dg(tau_hb,temp));
2205 for(i=0; (i<n-2); i++) {
2206 if ((ct[i] > e_1) && (ct[i+1] <= e_1)) {
2211 /* Determine tau_relax from linear interpolation */
2212 tau_rlx = t[i]-t[0] + (e_1-ct[i])*(t[i+1]-t[i])/(ct[i+1]-ct[i]);
2213 printf("Relaxation %10.3f %8.3f %s%10.3f\n",1/tau_rlx,
2214 tau_rlx,bError ? " " : "",
2215 calc_dg(tau_rlx,temp));
2219 printf("Correlation functions too short to compute thermodynamics\n");
2222 void compute_derivative(int nn,real x[],real y[],real dydx[])
2226 /* Compute k(t) = dc(t)/dt */
2227 for(j=1; (j<nn-1); j++)
2228 dydx[j] = (y[j+1]-y[j-1])/(x[j+1]-x[j-1]);
2229 /* Extrapolate endpoints */
2230 dydx[0] = 2*dydx[1] - dydx[2];
2231 dydx[nn-1] = 2*dydx[nn-2] - dydx[nn-3];
2234 static void parallel_print(int *data, int nThreads)
2236 /* This prints the donors on which each tread is currently working. */
2239 fprintf(stderr, "\r");
2240 for (i=0; i<nThreads; i++)
2241 fprintf(stderr, "%-7i",data[i]);
2244 static void normalizeACF(real *ct, real *gt, int nhb, int len)
2246 real ct_fac, gt_fac;
2249 /* Xu and Berne use the same normalization constant */
2252 gt_fac = (nhb == 0) ? 0 : 1.0/(real)nhb;
2254 printf("Normalization for c(t) = %g for gh(t) = %g\n",ct_fac,gt_fac);
2255 for (i=0; i<len; i++)
2263 /* Added argument bContact in do_hbac(...). Also
2264 * added support for -contact in the actual code.
2265 * - Erik Marklund, May 31, 2006 */
2266 /* Changed contact code and added argument R2
2267 * - Erik Marklund, June 29, 2006
2269 static void do_hbac(const char *fn,t_hbdata *hb,
2270 int nDump,gmx_bool bMerge,gmx_bool bContact, real fit_start,
2271 real temp,gmx_bool R2,real smooth_tail_start, const output_env_t oenv,
2272 t_gemParams *params, const char *gemType, int nThreads,
2273 const int NN, const gmx_bool bBallistic, const gmx_bool bGemFit)
2276 int i,j,k,m,n,o,nd,ihb,idist,n2,nn,iter,nSets;
2277 const char *legNN[] = { "Ac(t)",
2279 static char **legGem;
2281 const char *legLuzar[] = { "Ac\\sfin sys\\v{}\\z{}(t)",
2283 "Cc\\scontact,hb\\v{}\\z{}(t)",
2284 "-dAc\\sfs\\v{}\\z{}/dt" };
2285 gmx_bool bNorm=FALSE;
2288 real *rhbex=NULL,*ht,*gt,*ght,*dght,*kt;
2289 real *ct,*p_ct,tail,tail2,dtail,ct_fac,ght_fac,*cct;
2290 const real tol = 1e-3;
2291 int nframes = hb->nframes,nf;
2292 unsigned int **h,**g;
2293 int nh,nhbonds,nhydro,ngh;
2295 PSTYPE p, *pfound = NULL, np;
2297 int *ptimes=NULL, *poff=NULL, anhb, n0, mMax=INT_MIN;
2298 real **rHbExGem = NULL;
2302 double *ctdouble, *timedouble, *fittedct;
2303 double fittolerance=0.1;
2305 enum {AC_NONE, AC_NN, AC_GEM, AC_LUZAR};
2309 int *dondata=NULL, thisThread;
2313 printf("Doing autocorrelation ");
2315 /* Decide what kind of ACF calculations to do. */
2316 if (NN > NN_NONE && NN < NN_NR) {
2317 #ifdef HAVE_NN_LOOPS
2319 printf("using the energy estimate.\n");
2322 printf("Can't do the NN-loop. Yet.\n");
2324 } else if (hb->bGem) {
2326 printf("according to the reversible geminate recombination model by Omer Markowitch.\n");
2328 nSets = 1 + (bBallistic ? 1:0) + (bGemFit ? 1:0);
2329 snew(legGem, nSets);
2330 for (i=0;i<nSets;i++)
2331 snew(legGem[i], 128);
2332 sprintf(legGem[0], "Ac\\s%s\\v{}\\z{}(t)", gemType);
2334 sprintf(legGem[1], "Ac'(t)");
2336 sprintf(legGem[(bBallistic ? 3:2)], "Ac\\s%s,fit\\v{}\\z{}(t)", gemType);
2340 printf("according to the theory of Luzar and Chandler.\n");
2344 /* build hbexist matrix in reals for autocorr */
2345 /* Allocate memory for computing ACF (rhbex) and aggregating the ACF (ct) */
2347 while (n2 < nframes)
2352 if (acType != AC_NN ||
2359 snew(h,hb->maxhydro);
2360 snew(g,hb->maxhydro);
2363 /* Dump hbonds for debugging */
2364 dump_ac(hb,bMerge||bContact,nDump);
2366 /* Total number of hbonds analyzed here */
2371 /* ------------------------------------------------
2372 * I got tired of waiting for the acf calculations
2373 * and parallelized it with openMP
2374 * set environment variable CFLAGS = "-fopenmp" when running
2375 * configure and define DOUSEOPENMP to make use of it.
2378 #ifdef HAVE_OPENMP /* ================================================= \
2379 * Set up the OpenMP stuff, |
2380 * like the number of threads and such |
2382 if (acType != AC_LUZAR)
2384 /* #if (_OPENMP >= 200805) /\* =====================\ *\/ */
2385 /* nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_thread_limit()); */
2387 nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_num_procs());
2388 /* #endif /\* _OPENMP >= 200805 ====================/ *\/ */
2390 omp_set_num_threads(nThreads);
2391 snew(dondata, nThreads);
2392 for (i=0; i<nThreads; i++)
2394 printf("ACF calculations parallelized with OpenMP using %i threads.\n"
2395 "Expect close to linear scaling over this donor-loop.\n", nThreads);
2397 fprintf(stderr, "Donors: [thread no]\n");
2400 for (i=0;i<nThreads;i++) {
2401 snprintf(tmpstr, 7, "[%i]", i);
2402 fprintf(stderr, "%-7s", tmpstr);
2405 fprintf(stderr, "\n"); /* | */
2407 #endif /* HAVE_OPENMP ===================================================/ */
2410 /* Build the ACF according to acType */
2415 #ifdef HAVE_NN_LOOPS
2416 /* Here we're using the estimated energy for the hydrogen bonds. */
2418 #ifdef HAVE_OPENMP /* ==================================\ */
2419 #pragma omp parallel \
2420 private(i, j, k, nh, E, rhbex, thisThread), \
2424 thisThread = omp_get_thread_num();
2426 #endif /* ==============================================/ */
2429 memset(rhbex, 0, n2*sizeof(real)); /* Trust no-one, not even malloc()! */
2431 #ifdef HAVE_OPENMP /* ################################################## \
2436 #pragma omp for schedule (dynamic)
2438 for (i=0; i<hb->d.nrd; i++) /* loop over donors */
2440 #ifdef HAVE_OPENMP /* ====== Write some output ======\ */
2441 #pragma omp critical
2443 dondata[thisThread] = i;
2444 parallel_print(dondata, nThreads);
2447 fprintf(stderr, "\r %i", i);
2448 #endif /* ===========================================/ */
2450 for (j=0; j<hb->a.nra; j++) /* loop over acceptors */
2452 for (nh=0; nh<hb->d.nhydro[i]; nh++) /* loop over donors' hydrogens */
2454 E = hb->hbE.E[i][j][nh];
2457 for (k=0; k<nframes; k++)
2459 if (E[k] != NONSENSE_E)
2460 rhbex[k] = (real)E[k];
2463 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&(rhbex),hb->time[1]-hb->time[0],
2464 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2466 #pragma omp critical
2469 for(k=0; (k<nn); k++)
2480 } /* End of parallel block # */
2481 /* ##############################################################/ */
2484 normalizeACF(ct, NULL, 0, nn);
2486 snew(timedouble, nn);
2487 for (j=0; j<nn; j++)
2489 timedouble[j]=(double)(hb->time[j]);
2490 ctdouble[j]=(double)(ct[j]);
2493 /* Remove ballistic term */
2494 /* Ballistic component removal and fitting to the reversible geminate recombination model
2495 * will be taken out for the time being. First of all, one can remove the ballistic
2496 * component with g_analyze afterwards. Secondly, and more importantly, there are still
2497 * problems with the robustness of the fitting to the model. More work is needed.
2498 * A third reason is that we're currently using gsl for this and wish to reduce dependence
2499 * on external libraries. There are Levenberg-Marquardt and nsimplex solvers that come with
2500 * a BSD-licence that can do the job.
2502 * - Erik Marklund, June 18 2010.
2504 /* if (params->ballistic/params->tDelta >= params->nExpFit*2+1) */
2505 /* takeAwayBallistic(ctdouble, timedouble, nn, params->ballistic, params->nExpFit, params->bDt); */
2507 /* printf("\nNumber of data points is less than the number of parameters to fit\n." */
2508 /* "The system is underdetermined, hence no ballistic term can be found.\n\n"); */
2510 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation",output_env_get_xvgr_tlabel(oenv),"C(t)");
2511 xvgr_legend(fp,asize(legNN),legNN);
2513 for(j=0; (j<nn); j++)
2514 fprintf(fp,"%10g %10g %10g\n",
2515 hb->time[j]-hb->time[0],
2522 #endif /* HAVE_NN_LOOPS */
2523 break; /* case AC_NN */
2527 memset(ct,0,2*n2*sizeof(real));
2529 fprintf(stderr, "Donor:\n");
2532 #define __ACDATA p_ct
2535 #ifdef HAVE_OPENMP /* =========================================\
2537 #pragma omp parallel default(none) \
2538 private(i, k, nh, hbh, pHist, h, g, n0, nf, np, j, m, \
2539 pfound, poff, rHbExGem, p, ihb, mMax, \
2541 shared(hb, dondata, ct, nn, nThreads, n2, stderr, bNorm, \
2542 nframes, bMerge, bContact)
2543 { /* ########## THE START OF THE ENORMOUS PARALLELIZED BLOCK! ########## */
2546 thisThread = omp_get_thread_num();
2547 snew(h,hb->maxhydro);
2548 snew(g,hb->maxhydro);
2555 memset(p_ct,0,2*n2*sizeof(real));
2557 /* I'm using a chunk size of 1, since I expect \
2558 * the overhead to be really small compared \
2559 * to the actual calculations \ */
2560 #pragma omp for schedule(dynamic,1) nowait /* \ */
2561 #endif /* HAVE_OPENMP =========================================/ */
2563 for (i=0; i<hb->d.nrd; i++) {
2565 #pragma omp critical
2567 dondata[thisThread] = i;
2568 parallel_print(dondata, nThreads);
2571 fprintf(stderr, "\r %i", i);
2574 for (k=0; k<hb->a.nra; k++) {
2575 for (nh=0; nh < ((bMerge || bContact) ? 1 : hb->d.nhydro[i]); nh++) {
2576 hbh = hb->hbmap[i][k];
2578 /* Note that if hb->per->gemtype==gemDD, then distances will be stored in
2579 * hb->hbmap[d][a].h array anyway, because the contact flag will be set.
2580 * hence, it's only with the gemAD mode that hb->hbmap[d][a].g will be used. */
2581 pHist = &(hb->per->pHist[i][k]);
2582 if (ISHB(hbh->history[nh]) && pHist->len != 0) {
2584 /* No need for a critical section */
2585 /* #ifdef HAVE_OPENMP */
2586 /* #pragma omp critical */
2590 g[nh] = hb->per->gemtype==gemAD ? hbh->g[nh] : NULL;
2594 /* count the number of periodic shifts encountered and store
2595 * them in separate arrays. */
2597 for (j=0; j<pHist->len; j++)
2600 for (m=0; m<=np; m++) {
2601 if (m == np) { /* p not recognized in list. Add it and set up new array. */
2603 if (np>hb->per->nper)
2604 gmx_fatal(FARGS, "Too many pshifts. Something's utterly wrong here.");
2605 if (m>=mMax) { /* Extend the arrays.
2606 * Doing it like this, using mMax to keep track of the sizes,
2607 * eleviates the need for freeing and re-allocating the arrays
2608 * when taking on the next donor-acceptor pair */
2610 srenew(pfound,np); /* The list of found periodic shifts. */
2611 srenew(rHbExGem,np); /* The hb existence functions (-aver_hb). */
2612 snew(rHbExGem[m],2*n2);
2616 /* This shouldn't have to be critical, right? */
2617 /* #ifdef HAVE_OPENMP */
2618 /* #pragma omp critical */
2621 if (rHbExGem != NULL && rHbExGem[m] != NULL) {
2622 /* This must be done, as this array was most likey
2623 * used to store stuff in some previous iteration. */
2624 memset(rHbExGem[m], 0, (sizeof(real)) * (2*n2));
2627 fprintf(stderr, "rHbExGem not initialized! m = %i\n", m);
2636 } /* m: Loop over found shifts */
2637 } /* j: Loop over shifts */
2639 /* Now unpack and disentangle the existence funtions. */
2640 for (j=0; j<nf; j++) {
2646 * pfound: list of periodic shifts found for this pair.
2647 * poff: list of frame offsets; that is, the first
2648 * frame a hbond has a particular periodic shift. */
2649 p = getPshift(*pHist, j+n0);
2652 for (m=0; m<np; m++)
2657 gmx_fatal(FARGS,"Shift not found, but must be there.");
2660 ihb = is_hb(h[nh],j) || ((hb->per->gemtype!=gemAD || j==0) ? FALSE : is_hb(g[nh],j));
2664 poff[m] = j; /* Here's where the first hbond with shift p is,
2665 * relative to the start of h[0].*/
2667 gmx_fatal(FARGS, "j<poff[m]");
2668 rHbExGem[m][j-poff[m]] += 1;
2673 /* Now, build ac. */
2674 for (m=0; m<np; m++) {
2675 if (rHbExGem[m][0]>0 && n0+poff[m]<nn/* && m==0 */) {
2676 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&(rHbExGem[m]),hb->time[1]-hb->time[0],
2677 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2678 for(j=0; (j<nn); j++)
2679 __ACDATA[j] += rHbExGem[m][j];
2681 } /* Building of ac. */
2684 } /* hydrogen loop */
2685 } /* acceptor loop */
2688 for (m=0; m<=mMax; m++) {
2697 #ifdef HAVE_OPENMP /* =======================================\ */
2698 #pragma omp critical
2700 for (i=0; i<nn; i++)
2705 } /* ########## THE END OF THE ENORMOUS PARALLELIZED BLOCK ########## */
2707 #endif /* HAVE_OPENMP =======================================/ */
2709 normalizeACF(ct, NULL, 0, nn);
2711 fprintf(stderr, "\n\nACF successfully calculated.\n");
2713 /* Use this part to fit to geminate recombination - JCP 129, 84505 (2008) */
2716 snew(timedouble, nn);
2720 timedouble[j]=(double)(hb->time[j]);
2721 ctdouble[j]=(double)(ct[j]);
2724 /* Remove ballistic term */
2725 /* Ballistic component removal and fitting to the reversible geminate recombination model
2726 * will be taken out for the time being. First of all, one can remove the ballistic
2727 * component with g_analyze afterwards. Secondly, and more importantly, there are still
2728 * problems with the robustness of the fitting to the model. More work is needed.
2729 * A third reason is that we're currently using gsl for this and wish to reduce dependence
2730 * on external libraries. There are Levenberg-Marquardt and nsimplex solvers that come with
2731 * a BSD-licence that can do the job.
2733 * - Erik Marklund, June 18 2010.
2735 /* if (bBallistic) { */
2736 /* if (params->ballistic/params->tDelta >= params->nExpFit*2+1) */
2737 /* takeAwayBallistic(ctdouble, timedouble, nn, params->ballistic, params->nExpFit, params->bDt); */
2739 /* printf("\nNumber of data points is less than the number of parameters to fit\n." */
2740 /* "The system is underdetermined, hence no ballistic term can be found.\n\n"); */
2743 /* fitGemRecomb(ctdouble, timedouble, &fittedct, nn, params); */
2747 fp = xvgropen(fn, "Contact Autocorrelation",output_env_get_xvgr_tlabel(oenv),"C(t)",oenv);
2749 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation",output_env_get_xvgr_tlabel(oenv),"C(t)",oenv);
2750 xvgr_legend(fp,asize(legGem),(const char**)legGem,oenv);
2752 for(j=0; (j<nn); j++)
2754 fprintf(fp, "%10g %10g", hb->time[j]-hb->time[0],ct[j]);
2756 fprintf(fp," %10g", ctdouble[j]);
2758 fprintf(fp," %10g", fittedct[j]);
2768 break; /* case AC_GEM */
2781 for(i=0; (i<hb->d.nrd); i++) {
2782 for(k=0; (k<hb->a.nra); k++) {
2784 hbh = hb->hbmap[i][k];
2787 if (bMerge || bContact) {
2788 if (ISHB(hbh->history[0])) {
2795 for(m=0; (m<hb->maxhydro); m++)
2796 if (bContact ? ISDIST(hbh->history[m]) : ISHB(hbh->history[m])) {
2797 g[nhydro] = hbh->g[m];
2798 h[nhydro] = hbh->h[m];
2804 for(nh=0; (nh<nhydro); nh++) {
2805 int nrint = bContact ? hb->nrdist : hb->nrhb;
2806 if ((((nhbonds+1) % 10) == 0) || (nhbonds+1 == nrint))
2807 fprintf(stderr,"\rACF %d/%d",nhbonds+1,nrint);
2809 for(j=0; (j<nframes); j++) {
2810 /* Changed '<' into '<=' below, just like I did in
2811 the hbm-output-loop in the gmx_hbond() block.
2812 - Erik Marklund, May 31, 2006 */
2814 ihb = is_hb(h[nh],j);
2815 idist = is_hb(g[nh],j);
2821 /* For contacts: if a second cut-off is provided, use it,
2822 * otherwise use g(t) = 1-h(t) */
2823 if (!R2 && bContact)
2826 gt[j] = idist*(1-ihb);
2832 /* The autocorrelation function is normalized after summation only */
2833 low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&rhbex,hb->time[1]-hb->time[0],
2834 eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
2836 /* Cross correlation analysis for thermodynamics */
2837 for(j=nframes; (j<n2); j++) {
2842 cross_corr(n2,ht,gt,dght);
2844 for(j=0; (j<nn); j++) {
2852 fprintf(stderr,"\n");
2855 normalizeACF(ct, ght, nhb, nn);
2857 /* Determine tail value for statistics */
2860 for(j=nn/2; (j<nn); j++) {
2862 tail2 += ct[j]*ct[j];
2864 tail /= (nn - nn/2);
2865 tail2 /= (nn - nn/2);
2866 dtail = sqrt(tail2-tail*tail);
2868 /* Check whether the ACF is long enough */
2870 printf("\nWARNING: Correlation function is probably not long enough\n"
2871 "because the standard deviation in the tail of C(t) > %g\n"
2872 "Tail value (average C(t) over second half of acf): %g +/- %g\n",
2875 for(j=0; (j<nn); j++) {
2877 ct[j] = (cct[j]-tail)/(1-tail);
2879 /* Compute negative derivative k(t) = -dc(t)/dt */
2880 compute_derivative(nn,hb->time,ct,kt);
2881 for(j=0; (j<nn); j++)
2886 fp = xvgropen(fn, "Contact Autocorrelation",output_env_get_xvgr_tlabel(oenv),"C(t)",oenv);
2888 fp = xvgropen(fn, "Hydrogen Bond Autocorrelation",output_env_get_xvgr_tlabel(oenv),"C(t)",oenv);
2889 xvgr_legend(fp,asize(legLuzar),legLuzar, oenv);
2892 for(j=0; (j<nn); j++)
2893 fprintf(fp,"%10g %10g %10g %10g %10g\n",
2894 hb->time[j]-hb->time[0],ct[j],cct[j],ght[j],kt[j]);
2897 analyse_corr(nn,hb->time,ct,ght,kt,NULL,NULL,NULL,
2898 fit_start,temp,smooth_tail_start,oenv);
2900 do_view(oenv,fn,NULL);
2912 break; /* case AC_LUZAR */
2915 gmx_fatal(FARGS, "Unrecognized type of ACF-calulation. acType = %i.", acType);
2916 } /* switch (acType) */
2919 static void init_hbframe(t_hbdata *hb,int nframes,real t)
2923 hb->time[nframes] = t;
2924 hb->nhb[nframes] = 0;
2925 hb->ndist[nframes] = 0;
2926 for (i=0; (i<max_hx); i++)
2927 hb->nhx[nframes][i]=0;
2928 /* Loop invalidated */
2929 if (hb->bHBmap && 0)
2930 for (i=0; (i<hb->d.nrd); i++)
2931 for (j=0; (j<hb->a.nra); j++)
2932 for (m=0; (m<hb->maxhydro); m++)
2933 if (hb->hbmap[i][j] && hb->hbmap[i][j]->h[m])
2934 set_hb(hb,i,m,j,nframes,HB_NO);
2935 /*set_hb(hb->hbmap[i][j]->h[m],nframes-hb->hbmap[i][j]->n0,HB_NO);*/
2938 static void analyse_donor_props(const char *fn,t_hbdata *hb,int nframes,real t,
2939 const output_env_t oenv)
2941 static FILE *fp = NULL;
2942 const char *leg[] = { "Nbound", "Nfree" };
2943 int i,j,k,nbound,nb,nhtot;
2948 fp = xvgropen(fn,"Donor properties",output_env_get_xvgr_tlabel(oenv),"Number",oenv);
2949 xvgr_legend(fp,asize(leg),leg,oenv);
2953 for(i=0; (i<hb->d.nrd); i++) {
2954 for(k=0; (k<hb->d.nhydro[i]); k++) {
2957 for(j=0; (j<hb->a.nra) && (nb == 0); j++) {
2958 if (hb->hbmap[i][j] && hb->hbmap[i][j]->h[k] &&
2959 is_hb(hb->hbmap[i][j]->h[k],nframes))
2965 fprintf(fp,"%10.3e %6d %6d\n",t,nbound,nhtot-nbound);
2968 static void dump_hbmap(t_hbdata *hb,
2969 int nfile,t_filenm fnm[],gmx_bool bTwo,
2970 gmx_bool bContact, int isize[],int *index[],char *grpnames[],
2974 int ddd,hhh,aaa,i,j,k,m,grp;
2975 char ds[32],hs[32],as[32];
2978 fp = opt2FILE("-hbn",nfile,fnm,"w");
2979 if (opt2bSet("-g",nfile,fnm)) {
2980 fplog = ffopen(opt2fn("-g",nfile,fnm),"w");
2982 fprintf(fplog,"# %10s %12s %12s\n","Donor","Hydrogen","Acceptor");
2984 fprintf(fplog,"# %10s %12s %12s\n","Donor","Hydrogen","Acceptor");
2988 for (grp=gr0; grp<=(bTwo?gr1:gr0); grp++) {
2989 fprintf(fp,"[ %s ]",grpnames[grp]);
2990 for (i=0; i<isize[grp]; i++) {
2991 fprintf(fp,(i%15)?" ":"\n");
2992 fprintf(fp," %4u",index[grp][i]+1);
2996 Added -contact support below.
2997 - Erik Marklund, May 29, 2006
3000 fprintf(fp,"[ donors_hydrogens_%s ]\n",grpnames[grp]);
3001 for (i=0; (i<hb->d.nrd); i++) {
3002 if (hb->d.grp[i] == grp) {
3003 for(j=0; (j<hb->d.nhydro[i]); j++)
3004 fprintf(fp," %4u %4u",hb->d.don[i]+1,
3005 hb->d.hydro[i][j]+1);
3010 fprintf(fp,"[ acceptors_%s ]",grpnames[grp]);
3011 for (i=0; (i<hb->a.nra); i++) {
3012 if (hb->a.grp[i] == grp) {
3013 fprintf(fp,(i%15 && !first)?" ":"\n");
3014 fprintf(fp," %4u",hb->a.acc[i]+1);
3022 fprintf(fp,bContact ? "[ contacts_%s-%s ]\n" :
3023 "[ hbonds_%s-%s ]\n",grpnames[0],grpnames[1]);
3025 fprintf(fp,bContact ? "[ contacts_%s ]" : "[ hbonds_%s ]\n",grpnames[0]);
3027 for(i=0; (i<hb->d.nrd); i++) {
3029 for(k=0; (k<hb->a.nra); k++) {
3031 for(m=0; (m<hb->d.nhydro[i]); m++) {
3032 if (hb->hbmap[i][k] && ISHB(hb->hbmap[i][k]->history[m])) {
3033 sprintf(ds,"%s",mkatomname(atoms,ddd));
3034 sprintf(as,"%s",mkatomname(atoms,aaa));
3036 fprintf(fp," %6u %6u\n",ddd+1,aaa+1);
3038 fprintf(fplog,"%12s %12s\n",ds,as);
3040 hhh = hb->d.hydro[i][m];
3041 sprintf(hs,"%s",mkatomname(atoms,hhh));
3042 fprintf(fp," %6u %6u %6u\n",ddd+1,hhh+1,aaa+1);
3044 fprintf(fplog,"%12s %12s %12s\n",ds,hs,as);
3056 /* sync_hbdata() updates the parallel t_hbdata p_hb using hb as template.
3057 * It mimics add_frames() and init_frame() to some extent. */
3058 static void sync_hbdata(t_hbdata *hb, t_hbdata *p_hb,
3059 int nframes, real t)
3062 if (nframes >= p_hb->max_frames)
3064 p_hb->max_frames += 4096;
3065 srenew(p_hb->nhb, p_hb->max_frames);
3066 srenew(p_hb->ndist, p_hb->max_frames);
3067 srenew(p_hb->n_bound, p_hb->max_frames);
3068 srenew(p_hb->nhx, p_hb->max_frames);
3070 srenew(p_hb->danr, p_hb->max_frames);
3071 memset(&(p_hb->nhb[nframes]), 0, sizeof(int) * (p_hb->max_frames-nframes));
3072 memset(&(p_hb->ndist[nframes]), 0, sizeof(int) * (p_hb->max_frames-nframes));
3073 p_hb->nhb[nframes] = 0;
3074 p_hb->ndist[nframes] = 0;
3077 p_hb->nframes = nframes;
3080 /* p_hb->nhx[nframes][i] */
3082 memset(&(p_hb->nhx[nframes]), 0 ,sizeof(int)*max_hx); /* zero the helix count for this frame */
3084 /* hb->per will remain constant througout the frame loop,
3085 * even though the data its members point to will change,
3086 * hence no need for re-syncing. */
3090 int gmx_hbond(int argc,char *argv[])
3092 const char *desc[] = {
3093 "[TT]g_hbond[tt] computes and analyzes hydrogen bonds. Hydrogen bonds are",
3094 "determined based on cutoffs for the angle Acceptor - Donor - Hydrogen",
3095 "(zero is extended) and the distance Hydrogen - Acceptor.",
3096 "OH and NH groups are regarded as donors, O is an acceptor always,",
3097 "N is an acceptor by default, but this can be switched using",
3098 "[TT]-nitacc[tt]. Dummy hydrogen atoms are assumed to be connected",
3099 "to the first preceding non-hydrogen atom.[PAR]",
3101 "You need to specify two groups for analysis, which must be either",
3102 "identical or non-overlapping. All hydrogen bonds between the two",
3103 "groups are analyzed.[PAR]",
3105 "If you set [TT]-shell[tt], you will be asked for an additional index group",
3106 "which should contain exactly one atom. In this case, only hydrogen",
3107 "bonds between atoms within the shell distance from the one atom are",
3110 /* "It is also possible to analyse specific hydrogen bonds with",
3111 "[TT]-sel[tt]. This index file must contain a group of atom triplets",
3112 "Donor Hydrogen Acceptor, in the following way:[PAR]",
3120 "Note that the triplets need not be on separate lines.",
3121 "Each atom triplet specifies a hydrogen bond to be analyzed,",
3122 "note also that no check is made for the types of atoms.[PAR]",
3124 "[BB]Output:[bb][BR]",
3125 "[TT]-num[tt]: number of hydrogen bonds as a function of time.[BR]",
3126 "[TT]-ac[tt]: average over all autocorrelations of the existence",
3127 "functions (either 0 or 1) of all hydrogen bonds.[BR]",
3128 "[TT]-dist[tt]: distance distribution of all hydrogen bonds.[BR]",
3129 "[TT]-ang[tt]: angle distribution of all hydrogen bonds.[BR]",
3130 "[TT]-hx[tt]: the number of n-n+i hydrogen bonds as a function of time",
3131 "where n and n+i stand for residue numbers and i ranges from 0 to 6.",
3132 "This includes the n-n+3, n-n+4 and n-n+5 hydrogen bonds associated",
3133 "with helices in proteins.[BR]",
3134 "[TT]-hbn[tt]: all selected groups, donors, hydrogens and acceptors",
3135 "for selected groups, all hydrogen bonded atoms from all groups and",
3136 "all solvent atoms involved in insertion.[BR]",
3137 "[TT]-hbm[tt]: existence matrix for all hydrogen bonds over all",
3138 "frames, this also contains information on solvent insertion",
3139 "into hydrogen bonds. Ordering is identical to that in [TT]-hbn[tt]",
3141 "[TT]-dan[tt]: write out the number of donors and acceptors analyzed for",
3142 "each timeframe. This is especially useful when using [TT]-shell[tt].[BR]",
3143 "[TT]-nhbdist[tt]: compute the number of HBonds per hydrogen in order to",
3144 "compare results to Raman Spectroscopy.",
3146 "Note: options [TT]-ac[tt], [TT]-life[tt], [TT]-hbn[tt] and [TT]-hbm[tt]",
3147 "require an amount of memory proportional to the total numbers of donors",
3148 "times the total number of acceptors in the selected group(s)."
3151 static real acut=30, abin=1, rcut=0.35, r2cut=0, rbin=0.005, rshell=-1;
3152 static real maxnhb=0,fit_start=1,fit_end=60,temp=298.15,smooth_tail_start=-1, D=-1;
3153 static gmx_bool bNitAcc=TRUE,bDA=TRUE,bMerge=TRUE;
3154 static int nDump=0, nFitPoints=100;
3155 static int nThreads = 0, nBalExp=4;
3157 static gmx_bool bContact=FALSE, bBallistic=FALSE, bBallisticDt=FALSE, bGemFit=FALSE;
3158 static real logAfterTime = 10, gemBallistic = 0.2; /* ps */
3159 static const char *NNtype[] = {NULL, "none", "binary", "oneOverR3", "dipole", NULL};
3163 { "-a", FALSE, etREAL, {&acut},
3164 "Cutoff angle (degrees, Acceptor - Donor - Hydrogen)" },
3165 { "-r", FALSE, etREAL, {&rcut},
3166 "Cutoff radius (nm, X - Acceptor, see next option)" },
3167 { "-da", FALSE, etBOOL, {&bDA},
3168 "Use distance Donor-Acceptor (if TRUE) or Hydrogen-Acceptor (FALSE)" },
3169 { "-r2", FALSE, etREAL, {&r2cut},
3170 "Second cutoff radius. Mainly useful with [TT]-contact[tt] and [TT]-ac[tt]"},
3171 { "-abin", FALSE, etREAL, {&abin},
3172 "Binwidth angle distribution (degrees)" },
3173 { "-rbin", FALSE, etREAL, {&rbin},
3174 "Binwidth distance distribution (nm)" },
3175 { "-nitacc",FALSE, etBOOL, {&bNitAcc},
3176 "Regard nitrogen atoms as acceptors" },
3177 { "-contact",FALSE,etBOOL, {&bContact},
3178 "Do not look for hydrogen bonds, but merely for contacts within the cut-off distance" },
3179 { "-shell", FALSE, etREAL, {&rshell},
3180 "when > 0, only calculate hydrogen bonds within # nm shell around "
3182 { "-fitstart", FALSE, etREAL, {&fit_start},
3183 "Time (ps) from which to start fitting the correlation functions in order to obtain the forward and backward rate constants for HB breaking and formation. With [TT]-gemfit[tt] we suggest [TT]-fitstart 0[tt]" },
3184 { "-fitstart", FALSE, etREAL, {&fit_start},
3185 "Time (ps) to which to stop fitting the correlation functions in order to obtain the forward and backward rate constants for HB breaking and formation (only with [TT]-gemfit[tt])" },
3186 { "-temp", FALSE, etREAL, {&temp},
3187 "Temperature (K) for computing the Gibbs energy corresponding to HB breaking and reforming" },
3188 { "-smooth",FALSE, etREAL, {&smooth_tail_start},
3189 "If >= 0, the tail of the ACF will be smoothed by fitting it to an exponential function: y = A exp(-x/[GRK]tau[grk])" },
3190 { "-dump", FALSE, etINT, {&nDump},
3191 "Dump the first N hydrogen bond ACFs in a single [TT].xvg[tt] file for debugging" },
3192 { "-max_hb",FALSE, etREAL, {&maxnhb},
3193 "Theoretical maximum number of hydrogen bonds used for normalizing HB autocorrelation function. Can be useful in case the program estimates it wrongly" },
3194 { "-merge", FALSE, etBOOL, {&bMerge},
3195 "H-bonds between the same donor and acceptor, but with different hydrogen are treated as a single H-bond. Mainly important for the ACF." },
3196 { "-geminate", FALSE, etENUM, {gemType},
3197 "Use reversible geminate recombination for the kinetics/thermodynamics calclations. See Markovitch et al., J. Chem. Phys 129, 084505 (2008) for details."},
3198 { "-diff", FALSE, etREAL, {&D},
3199 "Dffusion coefficient to use in the reversible geminate recombination kinetic model. If negative, then it will be fitted to the ACF along with ka and kd."},
3201 { "-nthreads", FALSE, etINT, {&nThreads},
3202 "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)"},
3205 const char *bugs[] = {
3206 "The option [TT]-sel[tt] that used to work on selected hbonds is out of order, and therefore not available for the time being."
3209 { efTRX, "-f", NULL, ffREAD },
3210 { efTPX, NULL, NULL, ffREAD },
3211 { efNDX, NULL, NULL, ffOPTRD },
3212 /* { efNDX, "-sel", "select", ffOPTRD },*/
3213 { efXVG, "-num", "hbnum", ffWRITE },
3214 { efLOG, "-g", "hbond", ffOPTWR },
3215 { efXVG, "-ac", "hbac", ffOPTWR },
3216 { efXVG, "-dist","hbdist", ffOPTWR },
3217 { efXVG, "-ang", "hbang", ffOPTWR },
3218 { efXVG, "-hx", "hbhelix",ffOPTWR },
3219 { efNDX, "-hbn", "hbond", ffOPTWR },
3220 { efXPM, "-hbm", "hbmap", ffOPTWR },
3221 { efXVG, "-don", "donor", ffOPTWR },
3222 { efXVG, "-dan", "danum", ffOPTWR },
3223 { efXVG, "-life","hblife", ffOPTWR },
3224 { efXVG, "-nhbdist", "nhbdist",ffOPTWR }
3227 #define NFILE asize(fnm)
3229 char hbmap [HB_NR]={ ' ', 'o', '-', '*' };
3230 const char *hbdesc[HB_NR]={ "None", "Present", "Inserted", "Present & Inserted" };
3231 t_rgb hbrgb [HB_NR]={ {1,1,1},{1,0,0}, {0,0,1}, {1,0,1} };
3233 t_trxstatus *status;
3238 int npargs,natoms,nframes=0,shatom;
3244 real t,ccut,dist=0.0,ang=0.0;
3245 double max_nhb,aver_nhb,aver_dist;
3246 int h=0,i,j,k=0,l,start,end,id,ja,ogrp,nsel;
3248 int xj,yj,zj,aj,xjj,yjj,zjj;
3249 int xk,yk,zk,ak,xkk,ykk,zkk;
3250 gmx_bool bSelected,bHBmap,bStop,bTwo,was,bBox,bTric;
3252 int grp,nabin,nrbin,bin,resdist,ihb;
3255 FILE *fp,*fpins=NULL,*fpnhb=NULL;
3257 t_ncell *icell,*jcell,*kcell;
3259 unsigned char *datable;
3264 int ii, jj, hh, actual_nThreads;
3266 gmx_bool bGem, bNN, bParallel;
3267 t_gemParams *params=NULL;
3269 CopyRight(stdout,argv[0]);
3272 ppa = add_acf_pargs(&npargs,pa);
3274 parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,NFILE,fnm,npargs,
3275 ppa,asize(desc),desc,asize(bugs),bugs,&oenv);
3277 /* NN-loop? If so, what estimator to use ?*/
3279 /* Outcommented for now DvdS 2010-07-13
3280 while (NN < NN_NR && gmx_strcasecmp(NNtype[0], NNtype[NN])!=0)
3283 gmx_fatal(FARGS, "Invalid NN-loop type.");
3286 for (i=2; bNN==FALSE && i<NN_NR; i++)
3287 bNN = bNN || NN == i;
3289 if (NN > NN_NONE && bMerge)
3292 /* geminate recombination? If so, which flavor? */
3294 while (gemmode < gemNR && gmx_strcasecmp(gemType[0], gemType[gemmode])!=0)
3296 if (gemmode == gemNR)
3297 gmx_fatal(FARGS, "Invalid recombination type.");
3300 for (i=2; bGem==FALSE && i<gemNR; i++)
3301 bGem = bGem || gemmode == i;
3304 printf("Geminate recombination: %s\n" ,gemType[gemmode]);
3306 printf("Note that some aspects of reversible geminate recombination won't work without gsl.\n");
3309 if (gemmode != gemDD) {
3310 printf("Turning off -contact option...\n");
3314 if (gemmode == gemDD) {
3315 printf("Turning on -contact option...\n");
3320 if (gemmode == gemAA) {
3321 printf("Turning off -merge option...\n");
3325 if (gemmode != gemAA) {
3326 printf("Turning on -merge option...\n");
3334 ccut = cos(acut*DEG2RAD);
3338 gmx_fatal(FARGS,"Can not analyze selected contacts.");
3340 gmx_fatal(FARGS,"Can not analyze contact between H and A: turn off -noda");
3345 /* Don't pollute stdout with information about external libraries.
3347 * printf("NO GSL! Can't find and take away ballistic term in ACF without GSL\n.");
3351 /* Initiate main data structure! */
3352 bHBmap = (opt2bSet("-ac",NFILE,fnm) ||
3353 opt2bSet("-life",NFILE,fnm) ||
3354 opt2bSet("-hbn",NFILE,fnm) ||
3355 opt2bSet("-hbm",NFILE,fnm) ||
3359 /* Same thing here. There is no reason whatsoever to write the specific version of
3360 * OpenMP used for compilation to stdout for normal usage.
3362 * printf("Compiled with OpenMP (%i)\n", _OPENMP);
3366 /* if (bContact && bGem) */
3367 /* gmx_fatal(FARGS, "Can't do reversible geminate recombination with -contact yet."); */
3369 if (opt2bSet("-nhbdist",NFILE,fnm)) {
3370 const char *leg[MAXHH+1] = { "0 HBs", "1 HB", "2 HBs", "3 HBs", "Total" };
3371 fpnhb = xvgropen(opt2fn("-nhbdist",NFILE,fnm),
3372 "Number of donor-H with N HBs",output_env_get_xvgr_tlabel(oenv),"N",oenv);
3373 xvgr_legend(fpnhb,asize(leg),leg,oenv);
3376 hb = mk_hbdata(bHBmap,opt2bSet("-dan",NFILE,fnm),bMerge || bContact, bGem, gemmode);
3379 read_tpx_top(ftp2fn(efTPX,NFILE,fnm),&ir,box,&natoms,NULL,NULL,NULL,&top);
3381 snew(grpnames,grNR);
3384 /* Make Donor-Acceptor table */
3385 snew(datable, top.atoms.nr);
3386 gen_datable(index[0],isize[0],datable,top.atoms.nr);
3389 /* analyze selected hydrogen bonds */
3390 printf("Select group with selected atoms:\n");
3391 get_index(&(top.atoms),opt2fn("-sel",NFILE,fnm),
3392 1,&nsel,index,grpnames);
3394 gmx_fatal(FARGS,"Number of atoms in group '%s' not a multiple of 3\n"
3395 "and therefore cannot contain triplets of "
3396 "Donor-Hydrogen-Acceptor",grpnames[0]);
3399 for(i=0; (i<nsel); i+=3) {
3400 int dd = index[0][i];
3401 int aa = index[0][i+2];
3402 /* int */ hh = index[0][i+1];
3403 add_dh (&hb->d,dd,hh,i,datable);
3404 add_acc(&hb->a,aa,i);
3405 /* Should this be here ? */
3406 snew(hb->d.dptr,top.atoms.nr);
3407 snew(hb->a.aptr,top.atoms.nr);
3408 add_hbond(hb,dd,aa,hh,gr0,gr0,0,bMerge,0,bContact,peri);
3410 printf("Analyzing %d selected hydrogen bonds from '%s'\n",
3411 isize[0],grpnames[0]);
3414 /* analyze all hydrogen bonds: get group(s) */
3415 printf("Specify 2 groups to analyze:\n");
3416 get_index(&(top.atoms),ftp2fn_null(efNDX,NFILE,fnm),
3417 2,isize,index,grpnames);
3419 /* check if we have two identical or two non-overlapping groups */
3420 bTwo = isize[0] != isize[1];
3421 for (i=0; (i<isize[0]) && !bTwo; i++)
3422 bTwo = index[0][i] != index[1][i];
3424 printf("Checking for overlap in atoms between %s and %s\n",
3425 grpnames[0],grpnames[1]);
3426 for (i=0; i<isize[1];i++)
3427 if (ISINGRP(datable[index[1][i]]))
3428 gmx_fatal(FARGS,"Partial overlap between groups '%s' and '%s'",
3429 grpnames[0],grpnames[1]);
3431 printf("Checking for overlap in atoms between %s and %s\n",
3432 grpnames[0],grpnames[1]);
3433 for (i=0; i<isize[0]; i++)
3434 for (j=0; j<isize[1]; j++)
3435 if (index[0][i] == index[1][j])
3436 gmx_fatal(FARGS,"Partial overlap between groups '%s' and '%s'",
3437 grpnames[0],grpnames[1]);
3441 printf("Calculating %s "
3442 "between %s (%d atoms) and %s (%d atoms)\n",
3443 bContact ? "contacts" : "hydrogen bonds",
3444 grpnames[0],isize[0],grpnames[1],isize[1]);
3446 fprintf(stderr,"Calculating %s in %s (%d atoms)\n",
3447 bContact?"contacts":"hydrogen bonds",grpnames[0],isize[0]);
3451 /* search donors and acceptors in groups */
3452 snew(datable, top.atoms.nr);
3453 for (i=0; (i<grNR); i++)
3454 if ( ((i==gr0) && !bSelected ) ||
3455 ((i==gr1) && bTwo )) {
3456 gen_datable(index[i],isize[i],datable,top.atoms.nr);
3458 search_acceptors(&top,isize[i],index[i],&hb->a,i,
3459 bNitAcc,TRUE,(bTwo && (i==gr0)) || !bTwo, datable);
3460 search_donors (&top,isize[i],index[i],&hb->d,i,
3461 TRUE,(bTwo && (i==gr1)) || !bTwo, datable);
3464 search_acceptors(&top,isize[i],index[i],&hb->a,i,bNitAcc,FALSE,TRUE, datable);
3465 search_donors (&top,isize[i],index[i],&hb->d,i,FALSE,TRUE, datable);
3468 clear_datable_grp(datable,top.atoms.nr);
3471 printf("Found %d donors and %d acceptors\n",hb->d.nrd,hb->a.nra);
3473 snew(donors[gr0D], dons[gr0D].nrd);*/
3476 printf("Making hbmap structure...");
3477 /* Generate hbond data structure */
3482 #ifdef HAVE_NN_LOOPS
3488 printf("Making per structure...");
3489 /* Generate hbond data structure */
3496 if (hb->d.nrd + hb->a.nra == 0) {
3497 printf("No Donors or Acceptors found\n");
3501 if (hb->d.nrd == 0) {
3502 printf("No Donors found\n");
3505 if (hb->a.nra == 0) {
3506 printf("No Acceptors found\n");
3511 gmx_fatal(FARGS,"Nothing to be done");
3518 /* get index group with atom for shell */
3520 printf("Select atom for shell (1 atom):\n");
3521 get_index(&(top.atoms),ftp2fn_null(efNDX,NFILE,fnm),
3522 1,&shisz,&shidx,&shgrpnm);
3524 printf("group contains %d atoms, should be 1 (one)\n",shisz);
3525 } while(shisz != 1);
3527 printf("Will calculate hydrogen bonds within a shell "
3528 "of %g nm around atom %i\n",rshell,shatom+1);
3531 /* Analyze trajectory */
3532 natoms=read_first_x(oenv,&status,ftp2fn(efTRX,NFILE,fnm),&t,&x,box);
3533 if ( natoms > top.atoms.nr )
3534 gmx_fatal(FARGS,"Topology (%d atoms) does not match trajectory (%d atoms)",
3535 top.atoms.nr,natoms);
3537 bBox = ir.ePBC!=epbcNONE;
3538 grid = init_grid(bBox, box, (rcut>r2cut)?rcut:r2cut, ngrid);
3541 snew(adist,nabin+1);
3542 snew(rdist,nrbin+1);
3545 gmx_fatal(FARGS, "Can't do geminate recombination without periodic box.");
3550 #define __ADIST adist
3551 #define __RDIST rdist
3553 #else /* HAVE_OPENMP ================================================== \
3554 * Set up the OpenMP stuff, |
3555 * like the number of threads and such |
3556 * Also start the parallel loop. |
3558 #define __ADIST p_adist[threadNr]
3559 #define __RDIST p_rdist[threadNr]
3560 #define __HBDATA p_hb[threadNr]
3562 bParallel = !bSelected;
3566 /* #if (_OPENMP > 200805) */
3567 /* actual_nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_thread_limit()); */
3569 actual_nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_num_procs());
3571 omp_set_num_threads(actual_nThreads);
3572 printf("Frame loop parallelized with OpenMP using %i threads.\n", actual_nThreads);
3577 actual_nThreads = 1;
3580 t_hbdata **p_hb; /* one per thread, then merge after the frame loop */
3581 int **p_adist, **p_rdist; /* a histogram for each thread. */
3582 snew(p_hb, actual_nThreads);
3583 snew(p_adist, actual_nThreads);
3584 snew(p_rdist, actual_nThreads);
3585 for (i=0; i<actual_nThreads; i++)
3588 snew(p_adist[i], nabin+1);
3589 snew(p_rdist[i], nrbin+1);
3591 p_hb[i]->max_frames = 0;
3592 p_hb[i]->nhb = NULL;
3593 p_hb[i]->ndist = NULL;
3594 p_hb[i]->n_bound = NULL;
3595 p_hb[i]->time = NULL;
3596 p_hb[i]->nhx = NULL;
3598 p_hb[i]->bHBmap = hb->bHBmap;
3599 p_hb[i]->bDAnr = hb->bDAnr;
3600 p_hb[i]->bGem = hb->bGem;
3601 p_hb[i]->wordlen = hb->wordlen;
3602 p_hb[i]->nframes = hb->nframes;
3603 p_hb[i]->maxhydro = hb->maxhydro;
3604 p_hb[i]->danr = hb->danr;
3607 p_hb[i]->hbmap = hb->hbmap;
3608 p_hb[i]->time = hb->time; /* This may need re-syncing at every frame. */
3609 p_hb[i]->per = hb->per;
3611 #ifdef HAVE_NN_LOOPS
3612 p_hb[i]->hbE = hb->hbE;
3616 p_hb[i]->nrdist = 0;
3619 /* Make a thread pool here,
3620 * instead of forking anew at every frame. */
3622 #pragma omp parallel \
3623 private(i, j, h, ii, jj, hh, E, \
3624 xi, yi, zi, xj, yj, zj, threadNr, \
3625 dist, ang, peri, icell, jcell, \
3626 grp, ogrp, ai, aj, xjj, yjj, zjj, \
3627 xk, yk, zk, ihb, id, resdist, \
3628 xkk, ykk, zkk, kcell, ak, k, bTric) \
3630 shared(hb, p_hb, p_adist, p_rdist, actual_nThreads, \
3631 x, bBox, box, hbox, rcut, r2cut, rshell, \
3632 shatom, ngrid, grid, nframes, t, \
3633 bParallel, bNN, index, bMerge, bContact, \
3634 bTwo, bDA,ccut, abin, rbin, top, \
3635 bSelected, bDebug, stderr, nsel, \
3636 bGem, oenv, fnm, fpnhb, trrStatus, natoms, \
3637 status, nabin, nrbin, adist, rdist, debug)
3638 { /* Start of parallel region */
3639 threadNr = omp_get_thread_num();
3640 #endif /* HAVE_OPENMP ================================================= */
3643 bTric = bBox && TRICLINIC(box);
3646 sync_hbdata(hb, p_hb[threadNr], nframes, t);
3650 build_grid(hb,x,x[shatom], bBox,box,hbox, (rcut>r2cut)?rcut:r2cut,
3651 rshell, ngrid,grid);
3652 reset_nhbonds(&(hb->d));
3654 if (debug && bDebug)
3655 dump_grid(debug, ngrid, grid);
3657 add_frames(hb,nframes);
3658 init_hbframe(hb,nframes,output_env_conv_time(oenv,t));
3661 count_da_grid(ngrid, grid, hb->danr[nframes]);
3664 p_hb[threadNr]->time = hb->time; /* This pointer may have changed. */
3668 #ifdef HAVE_NN_LOOPS /* Unlock this feature when testing */
3669 /* Loop over all atom pairs and estimate interaction energy */
3670 #ifdef HAVE_OPENMP /* ------- */
3672 #endif /* HAVE_OPENMP ------- */
3674 addFramesNN(hb, nframes);
3676 #ifdef HAVE_OPENMP /* ---------------- */
3678 #pragma omp for schedule(dynamic)
3679 #endif /* HAVE_OPENMP ---------------- */
3680 for (i=0; i<hb->d.nrd; i++)
3682 for(j=0;j<hb->a.nra; j++)
3685 h < (bContact ? 1 : hb->d.nhydro[i]);
3688 if (i==hb->d.nrd || j==hb->a.nra)
3689 gmx_fatal(FARGS, "out of bounds");
3691 /* Get the real atom ids */
3694 hh = hb->d.hydro[i][h];
3696 /* Estimate the energy from the geometry */
3697 E = calcHbEnergy(ii, jj, hh, x, NN, box, hbox, &(hb->d));
3698 /* Store the energy */
3699 storeHbEnergy(hb, i, j, h, E, nframes);
3703 #endif /* HAVE_NN_LOOPS */
3713 /* Do not parallelize this just yet. */
3715 for(ii=0; (ii<nsel); ii++) {
3716 int dd = index[0][i];
3717 int aa = index[0][i+2];
3718 /* int */ hh = index[0][i+1];
3719 ihb = is_hbond(hb,ii,ii,dd,aa,rcut,r2cut,ccut,x,bBox,box,
3720 hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
3723 /* add to index if not already there */
3725 add_hbond(hb,dd,aa,hh,ii,ii,nframes,bMerge,ihb,bContact,peri);
3729 } /* if (bSelected) */
3737 calcBoxProjection(box, hb->per->P);
3739 /* loop over all gridcells (xi,yi,zi) */
3740 /* Removed confusing macro, DvdS 27/12/98 */
3743 /* The outer grid loop will have to do for now. */
3744 #pragma omp for schedule(dynamic)
3746 for(xi=0; xi<ngrid[XX]; xi++)
3747 for(yi=0; (yi<ngrid[YY]); yi++)
3748 for(zi=0; (zi<ngrid[ZZ]); zi++) {
3750 /* loop over donor groups gr0 (always) and gr1 (if necessary) */
3751 for (grp=gr0; (grp <= (bTwo?gr1:gr0)); grp++) {
3752 icell=&(grid[zi][yi][xi].d[grp]);
3759 /* loop over all hydrogen atoms from group (grp)
3760 * in this gridcell (icell)
3762 for (ai=0; (ai<icell->nr); ai++) {
3763 i = icell->atoms[ai];
3765 /* loop over all adjacent gridcells (xj,yj,zj) */
3766 /* This is a macro!!! */
3767 LOOPGRIDINNER(xj,yj,zj,xjj,yjj,zjj,xi,yi,zi,ngrid,bTric) {
3768 jcell=&(grid[zj][yj][xj].a[ogrp]);
3769 /* loop over acceptor atoms from other group (ogrp)
3770 * in this adjacent gridcell (jcell)
3772 for (aj=0; (aj<jcell->nr); aj++) {
3773 j = jcell->atoms[aj];
3775 /* check if this once was a h-bond */
3777 ihb = is_hbond(__HBDATA,grp,ogrp,i,j,rcut,r2cut,ccut,x,bBox,box,
3778 hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
3781 /* add to index if not already there */
3783 add_hbond(__HBDATA,i,j,h,grp,ogrp,nframes,bMerge,ihb,bContact,peri);
3785 /* make angle and distance distributions */
3786 if (ihb == hbHB && !bContact) {
3788 gmx_fatal(FARGS,"distance is higher than what is allowed for an hbond: %f",dist);
3790 __ADIST[(int)( ang/abin)]++;
3791 __RDIST[(int)(dist/rbin)]++;
3794 if ((id = donor_index(&hb->d,grp,i)) == NOTSET)
3795 gmx_fatal(FARGS,"Invalid donor %d",i);
3796 if ((ia = acceptor_index(&hb->a,ogrp,j)) == NOTSET)
3797 gmx_fatal(FARGS,"Invalid acceptor %d",j);
3798 resdist=abs(top.atoms.atom[i].resind-
3799 top.atoms.atom[j].resind);
3800 if (resdist >= max_hx)
3802 __HBDATA->nhx[nframes][resdist]++;
3812 } /* for xi,yi,zi */
3813 } /* if (bSelected) {...} else */
3815 #ifdef HAVE_OPENMP /* ---------------------------- */
3816 /* Better wait for all threads to finnish using x[] before updating it. */
3818 #pragma omp barrier /* */
3819 #pragma omp critical /* */
3821 /* Sum up histograms and counts from p_hb[] into hb */
3823 hb->nhb[k] += p_hb[threadNr]->nhb[k];
3824 hb->ndist[k] += p_hb[threadNr]->ndist[k];
3825 for (j=0; j<max_hx; j++) /**/
3826 hb->nhx[k][j] += p_hb[threadNr]->nhx[k][j];
3830 /* Here are a handful of single constructs
3831 * to share the workload a bit. The most
3832 * important one is of course the last one,
3833 * where there's a potential bottleneck in form
3835 #pragma omp single /* ++++++++++++++++, */
3836 #endif /* HAVE_OPENMP ----------------+------------*/
3838 if (hb != NULL) /* */
3840 analyse_donor_props(opt2fn_null("-don",NFILE,fnm),hb,k,t,oenv);
3843 #ifdef HAVE_OPENMP /* + */
3844 #pragma omp single /* +++ +++ */
3848 do_nhb_dist(fpnhb,hb,t);
3850 } /* if (bNN) {...} else + */
3851 #ifdef HAVE_OPENMP /* + */
3852 #pragma omp single /* +++ +++ */
3855 trrStatus = (read_next_x(oenv,status,&t,natoms,x,box));
3858 #ifdef HAVE_OPENMP /* +++++++++++++++++ */
3861 } while (trrStatus);
3864 #pragma omp critical
3866 hb->nrhb += p_hb[threadNr]->nrhb;
3867 hb->nrdist += p_hb[threadNr]->nrdist;
3869 /* Free parallel datastructures */
3870 sfree(p_hb[threadNr]->nhb);
3871 sfree(p_hb[threadNr]->ndist);
3872 sfree(p_hb[threadNr]->nhx);
3875 for (i=0; i<nabin; i++)
3876 for (j=0; j<actual_nThreads; j++)
3878 adist[i] += p_adist[j][i];
3880 for (i=0; i<=nrbin; i++)
3881 for (j=0; j<actual_nThreads; j++)
3882 rdist[i] += p_rdist[j][i];
3884 sfree(p_adist[threadNr]);
3885 sfree(p_rdist[threadNr]);
3886 } /* End of parallel region */
3891 if(nframes <2 && (opt2bSet("-ac",NFILE,fnm) || opt2bSet("-life",NFILE,fnm)))
3893 gmx_fatal(FARGS,"Cannot calculate autocorrelation of life times with less than two frames");
3896 free_grid(ngrid,&grid);
3902 /* Compute maximum possible number of different hbonds */
3906 max_nhb = 0.5*(hb->d.nrd*hb->a.nra);
3908 /* Added support for -contact below.
3909 * - Erik Marklund, May 29-31, 2006 */
3910 /* Changed contact code.
3911 * - Erik Marklund, June 29, 2006 */
3912 if (bHBmap && !bNN) {
3914 printf("No %s found!!\n", bContact ? "contacts" : "hydrogen bonds");
3916 printf("Found %d different %s in trajectory\n"
3917 "Found %d different atom-pairs within %s distance\n",
3918 hb->nrhb, bContact?"contacts":"hydrogen bonds",
3919 hb->nrdist,(r2cut>0)?"second cut-off":"hydrogen bonding");
3921 /*Control the pHist.*/
3924 merge_hb(hb,bTwo,bContact);
3926 if (opt2bSet("-hbn",NFILE,fnm))
3927 dump_hbmap(hb,NFILE,fnm,bTwo,bContact,isize,index,grpnames,&top.atoms);
3929 /* Moved the call to merge_hb() to a line BEFORE dump_hbmap
3930 * to make the -hbn and -hmb output match eachother.
3931 * - Erik Marklund, May 30, 2006 */
3934 /* Print out number of hbonds and distances */
3937 fp = xvgropen(opt2fn("-num",NFILE,fnm),bContact ? "Contacts" :
3938 "Hydrogen Bonds",output_env_get_xvgr_tlabel(oenv),"Number",oenv);
3940 snew(leg[0],STRLEN);
3941 snew(leg[1],STRLEN);
3942 sprintf(leg[0],"%s",bContact?"Contacts":"Hydrogen bonds");
3943 sprintf(leg[1],"Pairs within %g nm",(r2cut>0)?r2cut:rcut);
3944 xvgr_legend(fp,2,(const char**)leg,oenv);
3948 for(i=0; (i<nframes); i++) {
3949 fprintf(fp,"%10g %10d %10d\n",hb->time[i],hb->nhb[i],hb->ndist[i]);
3950 aver_nhb += hb->nhb[i];
3951 aver_dist += hb->ndist[i];
3954 aver_nhb /= nframes;
3955 aver_dist /= nframes;
3956 /* Print HB distance distribution */
3957 if (opt2bSet("-dist",NFILE,fnm)) {
3961 for(i=0; i<nrbin; i++)
3964 fp = xvgropen(opt2fn("-dist",NFILE,fnm),
3965 "Hydrogen Bond Distribution",
3966 "Hydrogen - Acceptor Distance (nm)","",oenv);
3967 for(i=0; i<nrbin; i++)
3968 fprintf(fp,"%10g %10g\n",(i+0.5)*rbin,rdist[i]/(rbin*(real)sum));
3972 /* Print HB angle distribution */
3973 if (opt2bSet("-ang",NFILE,fnm)) {
3977 for(i=0; i<nabin; i++)
3980 fp = xvgropen(opt2fn("-ang",NFILE,fnm),
3981 "Hydrogen Bond Distribution",
3982 "Donor - Hydrogen - Acceptor Angle (\\SO\\N)","",oenv);
3983 for(i=0; i<nabin; i++)
3984 fprintf(fp,"%10g %10g\n",(i+0.5)*abin,adist[i]/(abin*(real)sum));
3988 /* Print HB in alpha-helix */
3989 if (opt2bSet("-hx",NFILE,fnm)) {
3990 fp = xvgropen(opt2fn("-hx",NFILE,fnm),
3991 "Hydrogen Bonds",output_env_get_xvgr_tlabel(oenv),"Count",oenv);
3992 xvgr_legend(fp,NRHXTYPES,hxtypenames,oenv);
3993 for(i=0; i<nframes; i++) {
3994 fprintf(fp,"%10g",hb->time[i]);
3995 for (j=0; j<max_hx; j++)
3996 fprintf(fp," %6d",hb->nhx[i][j]);
4002 printf("Average number of %s per timeframe %.3f out of %g possible\n",
4003 bContact ? "contacts" : "hbonds",
4004 bContact ? aver_dist : aver_nhb, max_nhb);
4006 /* Do Autocorrelation etc. */
4009 Added support for -contact in ac and hbm calculations below.
4010 - Erik Marklund, May 29, 2006
4014 if (opt2bSet("-ac",NFILE,fnm) || opt2bSet("-life",NFILE,fnm))
4015 please_cite(stdout,"Spoel2006b");
4016 if (opt2bSet("-ac",NFILE,fnm)) {
4017 char *gemstring=NULL;
4020 params = init_gemParams(rcut, D, hb->time, hb->nframes/2, nFitPoints, fit_start, fit_end,
4021 gemBallistic, nBalExp, bBallisticDt);
4023 gmx_fatal(FARGS, "Could not initiate t_gemParams params.");
4025 gemstring = strdup(gemType[hb->per->gemtype]);
4026 do_hbac(opt2fn("-ac",NFILE,fnm),hb,nDump,
4027 bMerge,bContact,fit_start,temp,r2cut>0,smooth_tail_start,oenv,
4028 params, gemstring, nThreads, NN, bBallistic, bGemFit);
4030 if (opt2bSet("-life",NFILE,fnm))
4031 do_hblife(opt2fn("-life",NFILE,fnm),hb,bMerge,bContact,oenv);
4032 if (opt2bSet("-hbm",NFILE,fnm)) {
4036 if ((nframes > 0) && (hb->nrhb > 0))
4041 snew(mat.matrix,mat.nx);
4042 for(x=0; (x<mat.nx); x++)
4043 snew(mat.matrix[x],mat.ny);
4045 for(id=0; (id<hb->d.nrd); id++)
4046 for(ia=0; (ia<hb->a.nra); ia++) {
4047 for(hh=0; (hh<hb->maxhydro); hh++) {
4048 if (hb->hbmap[id][ia]) {
4049 if (ISHB(hb->hbmap[id][ia]->history[hh])) {
4050 /* Changed '<' into '<=' in the for-statement below.
4051 * It fixed the previously undiscovered bug that caused
4052 * the last occurance of an hbond/contact to not be
4053 * set in mat.matrix. Have a look at any old -hbm-output
4054 * and you will notice that the last column is allways empty.
4055 * - Erik Marklund May 30, 2006
4057 for(x=0; (x<=hb->hbmap[id][ia]->nframes); x++) {
4058 int nn0 = hb->hbmap[id][ia]->n0;
4059 range_check(y,0,mat.ny);
4060 mat.matrix[x+nn0][y] = is_hb(hb->hbmap[id][ia]->h[hh],x);
4067 mat.axis_x=hb->time;
4068 snew(mat.axis_y,mat.ny);
4069 for(j=0; j<mat.ny; j++)
4071 sprintf(mat.title,bContact ? "Contact Existence Map":
4072 "Hydrogen Bond Existence Map");
4073 sprintf(mat.legend,bContact ? "Contacts" : "Hydrogen Bonds");
4074 sprintf(mat.label_x,"%s",output_env_get_xvgr_tlabel(oenv));
4075 sprintf(mat.label_y, bContact ? "Contact Index" : "Hydrogen Bond Index");
4078 snew(mat.map,mat.nmap);
4079 for(i=0; i<mat.nmap; i++) {
4080 mat.map[i].code.c1=hbmap[i];
4081 mat.map[i].desc=hbdesc[i];
4082 mat.map[i].rgb=hbrgb[i];
4084 fp = opt2FILE("-hbm",NFILE,fnm,"w");
4085 write_xpm_m(fp, mat);
4087 for(x=0; x<mat.nx; x++)
4088 sfree(mat.matrix[x]);
4095 fprintf(stderr,"No hydrogen bonds/contacts found. No hydrogen bond map will be printed.\n");
4101 fprintf(stderr, "There were %i periodic shifts\n", hb->per->nper);
4102 fprintf(stderr, "Freeing pHist for all donors...\n");
4103 for (i=0; i<hb->d.nrd; i++) {
4104 fprintf(stderr, "\r%i",i);
4105 if (hb->per->pHist[i] != NULL) {
4106 for (j=0; j<hb->a.nra; j++)
4107 clearPshift(&(hb->per->pHist[i][j]));
4108 sfree(hb->per->pHist[i]);
4111 sfree(hb->per->pHist);
4112 sfree(hb->per->p2i);
4114 fprintf(stderr, "...done.\n");
4117 #ifdef HAVE_NN_LOOPS
4127 #define USE_THIS_GROUP(j) ( (j == gr0) || (bTwo && (j == gr1)) )
4129 fp = xvgropen(opt2fn("-dan",NFILE,fnm),
4130 "Donors and Acceptors",output_env_get_xvgr_tlabel(oenv),"Count",oenv);
4131 nleg = (bTwo?2:1)*2;
4132 snew(legnames,nleg);
4134 for(j=0; j<grNR; j++)
4135 if ( USE_THIS_GROUP(j) ) {
4136 sprintf(buf,"Donors %s",grpnames[j]);
4137 legnames[i++] = strdup(buf);
4138 sprintf(buf,"Acceptors %s",grpnames[j]);
4139 legnames[i++] = strdup(buf);
4142 gmx_incons("number of legend entries");
4143 xvgr_legend(fp,nleg,(const char**)legnames,oenv);
4144 for(i=0; i<nframes; i++) {
4145 fprintf(fp,"%10g",hb->time[i]);
4146 for (j=0; (j<grNR); j++)
4147 if ( USE_THIS_GROUP(j) )
4148 fprintf(fp," %6d",hb->danr[i][j]);