Merge release-4-5-patches into release-4-6
[alexxy/gromacs.git] / src / tools / gmx_hbond.c
index f851d545029d2426d8e38044305f3c0928e737fc..ea53b9d95c95e2872b6d81c2999a043aee09c46b 100644 (file)
 #include <math.h>
 
 /*#define HAVE_NN_LOOPS*/
-/* Set environment variable CFLAGS = "-fopenmp" when running
- * configure and define DOUSEOPENMP to make use of parallelized
- * calculation of autocorrelation function.
- * It also adds a new option -nthreads which sets the number of threads.
- * */
-/*#define DOUSEOPENMP*/
-
-#ifdef DOUSEOPENMP
-#define HAVE_OPENMP
-#include "omp.h"
-#endif
+
+#include "gmx_omp.h"
 
 #include "statutil.h"
 #include "copyrite.h"
@@ -81,7 +72,7 @@ const char *hxtypenames[NRHXTYPES]=
 {"n-n","n-n+1","n-n+2","n-n+3","n-n+4","n-n+5","n-n>6"};
 #define MAXHH 4
 
-#ifdef HAVE_OPENMP
+#ifdef GMX_OPENMP
 #define MASTER_THREAD_ONLY(threadNr) ((threadNr)==0)
 #else
 #define MASTER_THREAD_ONLY(threadNr) ((threadNr)==(threadNr))
@@ -291,10 +282,7 @@ static PSTYPE periodicIndex(ivec r, t_gemPeriod *per, gmx_bool daSwap) {
     /* Not found apparently. Add it to the list! */
     /* printf("New shift found: %i,%i,%i\n",r[XX],r[YY],r[ZZ]); */
 
-/* Unfortunately this needs to be critical it seems. */
-#ifdef HAVE_OPENMP
 #pragma omp critical
-#endif
     {
         if (!per->p2i) {
             fprintf(stderr, "p2i not initialized. This shouldn't happen!\n");
@@ -567,9 +555,8 @@ static void storeHbEnergy(t_hbdata *hb, int d, int a, int h, t_E E, int frame){
         E = 0;
 
     hb->hbE.E[d][a][h][frame] = E;
-#ifdef HAVE_OPENMP
+
 #pragma omp critical
-#endif
     {
         hb->hbE.Etot[frame] += E;
     }
@@ -729,6 +716,7 @@ static void add_ff(t_hbdata *hbd,int id,int h,int ia,int frame,int ihb, PSTYPE p
       
         }
     }
+
 }
 
 static void inc_nhbonds(t_donors *ddd,int d, int h)
@@ -849,12 +837,16 @@ static void add_hbond(t_hbdata *hb,int d,int a,int h,int grpd,int grpa,
             k = 0;
     
         if (hb->bHBmap) {
-            if (hb->hbmap[id][ia] == NULL) {
-                snew(hb->hbmap[id][ia],1);
-                snew(hb->hbmap[id][ia]->h,hb->maxhydro);
-                snew(hb->hbmap[id][ia]->g,hb->maxhydro);
+
+#pragma omp critical
+            {
+                if (hb->hbmap[id][ia] == NULL) {
+                    snew(hb->hbmap[id][ia],1);
+                    snew(hb->hbmap[id][ia]->h,hb->maxhydro);
+                    snew(hb->hbmap[id][ia]->g,hb->maxhydro);
+                }
+                add_ff(hb,id,k,ia,frame,ihb,p);
             }
-            add_ff(hb,id,k,ia,frame,ihb,p);
         }
     
         /* Strange construction with frame >=0 is a relic from old code
@@ -899,20 +891,6 @@ static void add_hbond(t_hbdata *hb,int d,int a,int h,int grpd,int grpa,
         inc_nhbonds(&(hb->d),d,h);
 }
 
-/* Now a redundant function. It might find use at some point though. */
-static gmx_bool in_list(atom_id selection,int isize,atom_id *index)
-{
-    int i;
-    gmx_bool bFound;
-  
-    bFound=FALSE;
-    for(i=0; (i<isize) && !bFound; i++)
-        if(selection == index[i])
-            bFound=TRUE;
-  
-    return bFound;
-}
-
 static char *mkatomname(t_atoms *atoms,int i)
 {
     static char buf[32];
@@ -1040,7 +1018,7 @@ static void search_donors(t_topology *top, int isize, atom_id *index,
     int        i,j,nra,n;
     t_functype func_type;
     t_ilist    *interaction;
-    atom_id    nr1,nr2;
+    atom_id    nr1,nr2,nr3;
     gmx_bool       stop;
 
     if (!ddd->dptr) {
@@ -1075,14 +1053,16 @@ static void search_donors(t_topology *top, int isize, atom_id *index,
        
                 /* check out this functype */
                 if (func_type == F_SETTLE) {
-                    nr1=interaction->iatoms[i+1];
+                    nr1 = interaction->iatoms[i+1];
+                    nr2 = interaction->iatoms[i+2];
+                    nr3 = interaction->iatoms[i+3];
          
                     if (ISINGRP(datable[nr1])) {
-                        if (ISINGRP(datable[nr1+1])) {
+                        if (ISINGRP(datable[nr2])) {
                             datable[nr1] |= DON;
                             add_dh(ddd,nr1,nr1+1,grp,datable);
                         }
-                        if (ISINGRP(datable[nr1+2])) {
+                        if (ISINGRP(datable[nr3])) {
                             datable[nr1] |= DON;
                             add_dh(ddd,nr1,nr1+2,grp,datable);
                         }
@@ -1160,21 +1140,6 @@ static t_gridcell ***init_grid(gmx_bool bBox,rvec box[],real rcut,ivec ngrid)
     return grid;
 }
 
-static void control_pHist(t_hbdata *hb, int nframes)
-{
-    int i,j,k;
-    PSTYPE p;
-    for (i=0;i<hb->d.nrd;i++)
-        for (j=0;j<hb->a.nra;j++)
-            if (hb->per->pHist[i][j].len != 0)
-                for (k=hb->hbmap[i][j][0].n0; k<nframes; k++) {
-                    p = getPshift(hb->per->pHist[i][j], k);
-                    if (p>hb->per->nper)
-                        fprintf(stderr, "Weird stuff in pHist[%i][%i].p at frame %i: p=%i\n",
-                                i,j,k,p);
-                }
-}
-
 static void reset_nhbonds(t_donors *ddd)
 {
     int i,j;
@@ -1360,23 +1325,18 @@ static void count_da_grid(ivec ngrid, t_gridcell ***grid, t_icell danr)
  * This could be implemented slightly more efficient, but the code
  * would get much more complicated.
  */
-#define B(n,x,bTric,bEdge) ((n==1) ? x : bTric&&(bEdge) ? 0   : (x-1))
-#define E(n,x,bTric,bEdge) ((n==1) ? x : bTric&&(bEdge) ? n-1 : (x+1))
-#define GRIDMOD(j,n) (j+n)%(n)
-#define LOOPGRIDINNER(x,y,z,xx,yy,zz,xo,yo,zo,n,bTric)                  \
-    for(zz=B(n[ZZ],zo,bTric,FALSE); zz<=E(n[ZZ],zo,bTric,FALSE); zz++) { \
-    z=GRIDMOD(zz,n[ZZ]);                                                \
-    for(yy=B(n[YY],yo,bTric,z==0||z==n[ZZ]-1);                          \
-        yy<=E(n[YY],yo,bTric,z==0||z==n[ZZ]-1); yy++) {                 \
-    y=GRIDMOD(yy,n[YY]);                                                \
-    for(xx=B(n[XX],xo,bTric,y==0||y==n[YY]-1||z==0||z==n[ZZ]-1);               \
-        xx<=E(n[XX],xo,bTric,y==0||y==n[YY]-1||z==0||z==n[ZZ]-1); xx++) { \
-    x=GRIDMOD(xx,n[XX]);
-#define ENDLOOPGRIDINNER                                                \
-    }                                                                   \
-        }                                                               \
-                                        }                                                              \
-                                                                        \
+static inline gmx_bool grid_loop_begin(int n, int x, gmx_bool bTric, gmx_bool bEdge)
+{
+    return ((n==1) ? x : bTric && bEdge ? 0     : (x-1));
+}
+static inline gmx_bool grid_loop_end(int n, int x, gmx_bool bTric, gmx_bool bEdge)
+{
+    return ((n==1) ? x : bTric && bEdge ? (n-1) : (x+1));
+}
+static inline int grid_mod(int j, int n)
+{
+    return (j+n) % (n);
+}
 
 static void dump_grid(FILE *fp, ivec ngrid, t_gridcell ***grid)
 {
@@ -1432,17 +1392,6 @@ static void free_grid(ivec ngrid, t_gridcell ****grid)
     g=NULL;
 }
 
-static void pbc_correct(rvec dx,matrix box,rvec hbox)
-{
-    int m;
-    for(m=DIM-1; m>=0; m--) {
-        if ( dx[m] < -hbox[m] )
-            rvec_inc(dx,box[m]);
-        else if ( dx[m] >= hbox[m] )
-            rvec_dec(dx,box[m]);
-    }
-}
-
 void pbc_correct_gem(rvec dx,matrix box,rvec hbox)
 {
     int m;
@@ -1550,10 +1499,7 @@ static int is_hbond(t_hbdata *hb,int grpd,int grpa,int d,int a,
         if (bDA || (!bDA && (rha2 <= rc2))) {
             rvec_sub(x[d],x[hh],r_dh);
             if (bBox) {
-                if (hb->bGem)
-                    pbc_correct_gem(r_dh,box,hbox);
-                else
-                    pbc_correct_gem(r_dh,box,hbox);
+                pbc_correct_gem(r_dh,box,hbox);
             }
        
             if (!bDA)
@@ -2282,14 +2228,14 @@ static void do_hbac(const char *fn,t_hbdata *hb,
                                 "Ac(t)",
                                 "Cc\\scontact,hb\\v{}\\z{}(t)",
                                 "-dAc\\sfs\\v{}\\z{}/dt" };
-    gmx_bool bNorm=FALSE;
+    gmx_bool bNorm=FALSE, bOMP=FALSE;
     double nhb = 0;
     int nhbi=0;
     real *rhbex=NULL,*ht,*gt,*ght,*dght,*kt;
     real *ct,*p_ct,tail,tail2,dtail,ct_fac,ght_fac,*cct;
     const real tol = 1e-3;
     int   nframes = hb->nframes,nf;
-    unsigned int **h,**g;
+    unsigned int **h=NULL,**g=NULL;
     int   nh,nhbonds,nhydro,ngh;
     t_hbond *hbh;
     PSTYPE p, *pfound = NULL, np;
@@ -2301,15 +2247,16 @@ static void do_hbac(const char *fn,t_hbdata *hb,
     t_E *E;
     double *ctdouble, *timedouble, *fittedct;
     double fittolerance=0.1;
+    int *dondata=NULL, thisThread;
 
     enum {AC_NONE, AC_NN, AC_GEM, AC_LUZAR};
 
-
-#ifdef HAVE_OPENMP
-    int *dondata=NULL, thisThread;
+#ifdef GMX_OPENMP
+    bOMP = TRUE;
+#else
+    bOMP = FALSE;
 #endif
 
-
     printf("Doing autocorrelation ");
 
     /* Decide what kind of ACF calculations to do. */
@@ -2335,7 +2282,9 @@ static void do_hbac(const char *fn,t_hbdata *hb,
         if (bGemFit)
             sprintf(legGem[(bBallistic ? 3:2)], "Ac\\s%s,fit\\v{}\\z{}(t)", gemType);
 
-    } else {
+    }
+    else
+    {
         acType = AC_LUZAR;
         printf("according to the theory of Luzar and Chandler.\n");
     }
@@ -2349,13 +2298,7 @@ static void do_hbac(const char *fn,t_hbdata *hb,
   
     nn = nframes/2;
   
-    if (acType != AC_NN ||
-#ifndef HAVE_OPENMP
-        TRUE
-#else
-        FALSE
-#endif
-        ) {
+    if (acType != AC_NN || bOMP) {
         snew(h,hb->maxhydro);
         snew(g,hb->maxhydro);
     }
@@ -2368,26 +2311,11 @@ static void do_hbac(const char *fn,t_hbdata *hb,
     ngh     = 0;
     anhb    = 0;
 
-    /* ------------------------------------------------
-     * I got tired of waiting for the acf calculations
-     * and parallelized it with openMP
-     * set environment variable CFLAGS = "-fopenmp" when running
-     * configure and define DOUSEOPENMP to make use of it.
-     */
-
-#ifdef HAVE_OPENMP  /* ================================================= \
-                     * Set up the OpenMP stuff,                           |
-                     * like the number of threads and such                |
-                     */
-    if (acType != AC_LUZAR)
+    if (acType != AC_LUZAR && bOMP)
     {
-/* #if (_OPENMP >= 200805) /\* =====================\ *\/ */
-/*         nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_thread_limit()); */
-/* #else */
-        nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_num_procs());
-/* #endif /\* _OPENMP >= 200805 ====================/ *\/ */
+        nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, gmx_omp_get_max_threads());
 
-        omp_set_num_threads(nThreads);
+        gmx_omp_set_num_threads(nThreads);
         snew(dondata, nThreads);
         for (i=0; i<nThreads; i++)
             dondata[i] = -1;
@@ -2402,9 +2330,8 @@ static void do_hbac(const char *fn,t_hbdata *hb,
                 fprintf(stderr, "%-7s", tmpstr);
             }
         }
-        fprintf(stderr, "\n"); /*                                         | */
-    }  /*                                                                 | */
-#endif /* HAVE_OPENMP ===================================================/  */
+        fprintf(stderr, "\n");
+    }
 
 
     /* Build the ACF according to acType */
@@ -2415,37 +2342,34 @@ static void do_hbac(const char *fn,t_hbdata *hb,
 #ifdef HAVE_NN_LOOPS
         /* Here we're using the estimated energy for the hydrogen bonds. */
         snew(ct,nn);
-#ifdef HAVE_OPENMP /* ==================================\ */      
+
 #pragma omp parallel                            \
-    private(i, j, k, nh, E, rhbex, thisThread),        \
+    private(i, j, k, nh, E, rhbex, thisThread)  \
     default(shared)
         {
 #pragma omp barrier
-            thisThread = omp_get_thread_num();
+            thisThread = gmx_omp_get_thread_num();
             rhbex = NULL;
-#endif /* ==============================================/ */
 
             snew(rhbex, n2);
             memset(rhbex, 0, n2*sizeof(real)); /* Trust no-one, not even malloc()! */
 
-#ifdef HAVE_OPENMP /* ################################################## \
-                    *                                                    #
-                    *                                                    #
-                    */
 #pragma omp barrier
 #pragma omp for schedule (dynamic)
-#endif
             for (i=0; i<hb->d.nrd; i++) /* loop over donors */
             {
-#ifdef HAVE_OPENMP /* ====== Write some output ======\ */
+                if (bOMP)
+                {
 #pragma omp critical
+                    {
+                        dondata[thisThread] = i;
+                        parallel_print(dondata, nThreads);
+                    }
+                }
+                else
                 {
-                    dondata[thisThread] = i;
-                    parallel_print(dondata, nThreads);
+                    fprintf(stderr, "\r %i", i);
                 }
-#else
-                fprintf(stderr, "\r %i", i);
-#endif /* ===========================================/ */
 
                 for (j=0; j<hb->a.nra; j++) /* loop over acceptors */
                 {
@@ -2462,9 +2386,7 @@ static void do_hbac(const char *fn,t_hbdata *hb,
                      
                             low_do_autocorr(NULL,oenv,NULL,nframes,1,-1,&(rhbex),hb->time[1]-hb->time[0],
                                             eacNormal,1,FALSE,bNorm,FALSE,0,-1,0,1);
-#ifdef HAVE_OPENMP
 #pragma omp critical
-#endif
                             {
                                 for(k=0; (k<nn); k++)
                                     ct[k] += rhbex[k];
@@ -2475,12 +2397,12 @@ static void do_hbac(const char *fn,t_hbdata *hb,
             }           /* i loop */
             sfree(rhbex);
 #pragma omp barrier
-#ifdef HAVE_OPENMP 
-            /*                                                           # */
-        } /* End of parallel block                                       # */
-        /* ##############################################################/ */
-        sfree(dondata);
-#endif
+        }
+
+        if (bOMP)
+        {
+            sfree(dondata);
+        }
         normalizeACF(ct, NULL, 0, nn);
         snew(ctdouble, nn);
         snew(timedouble, nn);
@@ -2515,7 +2437,7 @@ static void do_hbac(const char *fn,t_hbdata *hb,
                     hb->time[j]-hb->time[0],
                     ct[j],
                     ctdouble[j]);
-        fclose(fp);
+        xvgrclose(fp);
         sfree(ct);
         sfree(ctdouble);
         sfree(timedouble);
@@ -2525,25 +2447,22 @@ static void do_hbac(const char *fn,t_hbdata *hb,
     case AC_GEM:
         snew(ct,2*n2);
         memset(ct,0,2*n2*sizeof(real));
-#ifndef HAVE_OPENMP
+#ifndef GMX_OPENMP
         fprintf(stderr, "Donor:\n");
 #define __ACDATA ct
 #else
 #define __ACDATA p_ct
 #endif
 
-#ifdef HAVE_OPENMP /*  =========================================\
-                    *                                          */
-#pragma omp parallel default(none)                              \
+#pragma omp parallel                                            \
     private(i, k, nh, hbh, pHist, h, g, n0, nf, np, j, m,              \
             pfound, poff, rHbExGem, p, ihb, mMax,               \
             thisThread, p_ct)                                   \
-    shared(hb, dondata, ct, nn, nThreads, n2, stderr, bNorm,    \
-           nframes, bMerge, bContact)
+    default(shared)
         { /* ##########  THE START OF THE ENORMOUS PARALLELIZED BLOCK!  ########## */
             h = NULL;
             g = NULL;
-            thisThread = omp_get_thread_num();
+            thisThread = gmx_omp_get_thread_num();
             snew(h,hb->maxhydro);
             snew(g,hb->maxhydro);
             mMax = INT_MIN;
@@ -2557,20 +2476,21 @@ static void do_hbac(const char *fn,t_hbdata *hb,
             /* I'm using a chunk size of 1, since I expect      \
              * the overhead to be really small compared         \
              * to the actual calculations                       \ */
-#pragma omp for schedule(dynamic,1) nowait /*                   \ */
-#endif /* HAVE_OPENMP  =========================================/ */
-      
+#pragma omp for schedule(dynamic,1) nowait
             for (i=0; i<hb->d.nrd; i++) {
-#ifdef HAVE_OPENMP
+
+                if (bOMP)
+                {
 #pragma omp critical
+                    {
+                        dondata[thisThread] = i;
+                        parallel_print(dondata, nThreads);
+                    }
+                }
+                else
                 {
-                    dondata[thisThread] = i;
-                    parallel_print(dondata, nThreads);
+                    fprintf(stderr, "\r %i", i);
                 }
-#else
-                fprintf(stderr, "\r %i", i);
-#endif
-       
                 for (k=0; k<hb->a.nra; k++) {
                     for (nh=0; nh < ((bMerge || bContact) ? 1 : hb->d.nhydro[i]); nh++) {
                         hbh = hb->hbmap[i][k];
@@ -2581,10 +2501,6 @@ static void do_hbac(const char *fn,t_hbdata *hb,
                             pHist = &(hb->per->pHist[i][k]);
                             if (ISHB(hbh->history[nh]) && pHist->len != 0) {
 
-/* No need for a critical section */
-/* #ifdef HAVE_OPENMP */
-/* #pragma omp critical */
-/* #endif */
                                 {
                                     h[nh] = hbh->h[nh];
                                     g[nh] = hb->per->gemtype==gemAD ? hbh->g[nh] : NULL;
@@ -2613,10 +2529,6 @@ static void do_hbac(const char *fn,t_hbdata *hb,
                                                 srenew(poff,np);
                                             }
 
-/* This shouldn't have to be critical, right? */
-/* #ifdef HAVE_OPENMP */
-/* #pragma omp critical */
-/* #endif */
                                             {
                                                 if (rHbExGem != NULL && rHbExGem[m] != NULL) {
                                                     /* This must be done, as this array was most likey
@@ -2694,17 +2606,22 @@ static void do_hbac(const char *fn,t_hbdata *hb,
 
             sfree(h);
             sfree(g);
-#ifdef HAVE_OPENMP /* =======================================\ */
-#pragma omp critical
+
+            if (bOMP)
             {
-                for (i=0; i<nn; i++)
-                    ct[i] += p_ct[i];
+#pragma omp critical
+                {
+                    for (i=0; i<nn; i++)
+                        ct[i] += p_ct[i];
+                }
+                sfree(p_ct);
             }
-            sfree(p_ct);
 
         } /* ########## THE END OF THE ENORMOUS PARALLELIZED BLOCK ########## */
-        sfree(dondata);
-#endif /* HAVE_OPENMP =======================================/ */
+        if (bOMP)
+        {
+            sfree(dondata);
+        }
 
         normalizeACF(ct, NULL, 0, nn);
 
@@ -2758,7 +2675,7 @@ static void do_hbac(const char *fn,t_hbdata *hb,
                 fprintf(fp,"  %10g", fittedct[j]);
             fprintf(fp,"\n");
         }
-        fclose(fp);
+        xvgrclose(fp);
 
         sfree(ctdouble);
         sfree(timedouble);
@@ -2978,10 +2895,7 @@ static void dump_hbmap(t_hbdata *hb,
     fp = opt2FILE("-hbn",nfile,fnm,"w");
     if (opt2bSet("-g",nfile,fnm)) {
         fplog = ffopen(opt2fn("-g",nfile,fnm),"w");
-        if (bContact)
-            fprintf(fplog,"# %10s  %12s  %12s\n","Donor","Hydrogen","Acceptor");
-        else
-            fprintf(fplog,"# %10s  %12s  %12s\n","Donor","Hydrogen","Acceptor");
+        fprintf(fplog,"# %10s  %12s  %12s\n","Donor","Hydrogen","Acceptor");
     }
     else
         fplog = NULL;
@@ -3052,7 +2966,6 @@ static void dump_hbmap(t_hbdata *hb,
         ffclose(fplog);
 }
 
-#ifdef HAVE_OPENMP
 /* sync_hbdata() updates the parallel t_hbdata p_hb using hb as template.
  * It mimics add_frames() and init_frame() to some extent. */
 static void sync_hbdata(t_hbdata *hb, t_hbdata *p_hb,
@@ -3085,7 +2998,6 @@ static void sync_hbdata(t_hbdata *hb, t_hbdata *p_hb,
      * even though the data its members point to will change,
      * hence no need for re-syncing. */
 }
-#endif
 
 int gmx_hbond(int argc,char *argv[])
 {
@@ -3106,6 +3018,15 @@ int gmx_hbond(int argc,char *argv[])
         "which should contain exactly one atom. In this case, only hydrogen",
         "bonds between atoms within the shell distance from the one atom are",
         "considered.[PAR]",
+
+        "With option -ac, rate constants for hydrogen bonding can be derived with the model of Luzar and Chandler",
+        "(Nature 394, 1996; J. Chem. Phys. 113:23, 2000) or that of Markovitz and Agmon (J. Chem. Phys 129, 2008).",
+        "If contact kinetics are analyzed by using the -contact option, then",
+        "n(t) can be defined as either all pairs that are not within contact distance r at time t",
+        "(corresponding to leaving the -r2 option at the default value 0) or all pairs that",
+        "are within distance r2 (corresponding to setting a second cut-off value with option -r2).",
+        "See mentioned literature for more details and definitions."
+        "[PAR]",
     
         /*    "It is also possible to analyse specific hydrogen bonds with",
               "[TT]-sel[tt]. This index file must contain a group of atom triplets",
@@ -3197,7 +3118,7 @@ int gmx_hbond(int argc,char *argv[])
           "Use reversible geminate recombination for the kinetics/thermodynamics calclations. See Markovitch et al., J. Chem. Phys 129, 084505 (2008) for details."},
         { "-diff", FALSE, etREAL, {&D},
           "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."},
-#ifdef HAVE_OPENMP
+#ifdef GMX_OPENMP
         { "-nthreads", FALSE, etINT, {&nThreads},
           "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)"},
 #endif
@@ -3243,15 +3164,15 @@ int gmx_hbond(int argc,char *argv[])
     matrix  box;
     real    t,ccut,dist=0.0,ang=0.0;
     double  max_nhb,aver_nhb,aver_dist;
-    int     h=0,i,j,k=0,l,start,end,id,ja,ogrp,nsel;
+    int     h=0,i=0,j,k=0,l,start,end,id,ja,ogrp,nsel;
     int     xi,yi,zi,ai;
     int     xj,yj,zj,aj,xjj,yjj,zjj;
     int     xk,yk,zk,ak,xkk,ykk,zkk;
     gmx_bool    bSelected,bHBmap,bStop,bTwo,was,bBox,bTric;
-    int     *adist,*rdist;
+    int     *adist,*rdist,*aptr,*rprt;
     int        grp,nabin,nrbin,bin,resdist,ihb;
     char       **leg;
-    t_hbdata   *hb;
+    t_hbdata   *hb,*hbptr;
     FILE       *fp,*fpins=NULL,*fpnhb=NULL;
     t_gridcell ***grid;
     t_ncell    *icell,*jcell,*kcell;
@@ -3265,8 +3186,18 @@ int gmx_hbond(int argc,char *argv[])
     int     threadNr=0;
     gmx_bool    bGem, bNN, bParallel;
     t_gemParams *params=NULL;
+    gmx_bool    bEdge_yjj, bEdge_xjj, bOMP;
     
-    CopyRight(stdout,argv[0]);
+    t_hbdata **p_hb=NULL;               /* one per thread, then merge after the frame loop */
+    int **p_adist=NULL, **p_rdist=NULL; /* a histogram for each thread. */
+
+#ifdef GMX_OPENMP
+    bOMP = TRUE;
+#else
+    bOMP = FALSE;
+#endif
+
+    CopyRight(stderr,argv[0]);
 
     npargs = asize(pa);  
     ppa    = add_acf_pargs(&npargs,pa);
@@ -3340,13 +3271,6 @@ int gmx_hbond(int argc,char *argv[])
             gmx_fatal(FARGS,"Can not analyze contact between H and A: turn off -noda");
         }
     }
-
-#ifndef HAVE_LIBGSL
-    /* Don't pollute stdout with information about external libraries.
-     *
-     * printf("NO GSL! Can't find and take away ballistic term in ACF without GSL\n.");
-     */
-#endif
   
     /* Initiate main data structure! */
     bHBmap = (opt2bSet("-ac",NFILE,fnm) ||
@@ -3355,17 +3279,6 @@ int gmx_hbond(int argc,char *argv[])
               opt2bSet("-hbm",NFILE,fnm) ||
               bGem);
   
-#ifdef HAVE_OPENMP
-    /* Same thing here. There is no reason whatsoever to write the specific version of
-     * OpenMP used for compilation to stdout for normal usage.
-     *
-     * printf("Compiled with OpenMP (%i)\n", _OPENMP);
-     */
-#endif
-
-    /*   if (bContact && bGem) */
-    /*     gmx_fatal(FARGS, "Can't do reversible geminate recombination with -contact yet."); */
-
     if (opt2bSet("-nhbdist",NFILE,fnm)) {
         const char *leg[MAXHH+1] = { "0 HBs", "1 HB", "2 HBs", "3 HBs", "Total" };
         fpnhb = xvgropen(opt2fn("-nhbdist",NFILE,fnm),
@@ -3546,11 +3459,11 @@ int gmx_hbond(int argc,char *argv[])
 
     bParallel = FALSE;
 
-#ifndef HAVE_OPENMP
+#ifndef GMX_OPENMP
 #define __ADIST adist
 #define __RDIST rdist
 #define __HBDATA hb
-#else /* HAVE_OPENMP ==================================================        \
+#else /* GMX_OPENMP ================================================== \
        * Set up the OpenMP stuff,                                       |
        * like the number of threads and such                            |
        * Also start the parallel loop.                                  |
@@ -3558,94 +3471,88 @@ int gmx_hbond(int argc,char *argv[])
 #define __ADIST p_adist[threadNr]
 #define __RDIST p_rdist[threadNr]
 #define __HBDATA p_hb[threadNr]
+#endif
+    if (bOMP)
+    {
+        bParallel = !bSelected;
 
-    bParallel = !bSelected;
+        if (bParallel)
+        {
+            actual_nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, gmx_omp_get_max_threads());
 
-    if (bParallel)
-    {
-/* #if (_OPENMP > 200805) */
-/*         actual_nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_thread_limit()); */
-/* #else */
-        actual_nThreads = min((nThreads <= 0) ? INT_MAX : nThreads, omp_get_num_procs());
-/* #endif */
-        omp_set_num_threads(actual_nThreads);
-        printf("Frame loop parallelized with OpenMP using %i threads.\n", actual_nThreads);
-        fflush(stdout);
-    }
-    else
-    {
-        actual_nThreads = 1;
-    }
+            gmx_omp_set_num_threads(actual_nThreads);
+            printf("Frame loop parallelized with OpenMP using %i threads.\n", actual_nThreads);
+            fflush(stdout);
+        }
+        else
+        {
+            actual_nThreads = 1;
+        }
 
-    t_hbdata **p_hb;          /* one per thread, then merge after the frame loop */
-    int **p_adist, **p_rdist; /* a histogram for each thread. */
-    snew(p_hb,    actual_nThreads);
-    snew(p_adist, actual_nThreads);
-    snew(p_rdist, actual_nThreads);
-    for (i=0; i<actual_nThreads; i++)
-    {
-        snew(p_hb[i], 1);
-        snew(p_adist[i], nabin+1);
-        snew(p_rdist[i], nrbin+1);
-
-        p_hb[i]->max_frames = 0;
-        p_hb[i]->nhb = NULL;
-        p_hb[i]->ndist = NULL;
-        p_hb[i]->n_bound = NULL;
-        p_hb[i]->time = NULL;
-        p_hb[i]->nhx = NULL;
-
-        p_hb[i]->bHBmap     = hb->bHBmap;
-        p_hb[i]->bDAnr      = hb->bDAnr;
-        p_hb[i]->bGem       = hb->bGem;
-        p_hb[i]->wordlen    = hb->wordlen;
-        p_hb[i]->nframes    = hb->nframes;
-        p_hb[i]->maxhydro   = hb->maxhydro;
-        p_hb[i]->danr       = hb->danr;
-        p_hb[i]->d          = hb->d;
-        p_hb[i]->a          = hb->a;
-        p_hb[i]->hbmap      = hb->hbmap;
-        p_hb[i]->time       = hb->time; /* This may need re-syncing at every frame. */
-        p_hb[i]->per        = hb->per;
+        snew(p_hb,    actual_nThreads);
+        snew(p_adist, actual_nThreads);
+        snew(p_rdist, actual_nThreads);
+        for (i=0; i<actual_nThreads; i++)
+        {
+            snew(p_hb[i], 1);
+            snew(p_adist[i], nabin+1);
+            snew(p_rdist[i], nrbin+1);
+
+            p_hb[i]->max_frames = 0;
+            p_hb[i]->nhb = NULL;
+            p_hb[i]->ndist = NULL;
+            p_hb[i]->n_bound = NULL;
+            p_hb[i]->time = NULL;
+            p_hb[i]->nhx = NULL;
+
+            p_hb[i]->bHBmap     = hb->bHBmap;
+            p_hb[i]->bDAnr      = hb->bDAnr;
+            p_hb[i]->bGem       = hb->bGem;
+            p_hb[i]->wordlen    = hb->wordlen;
+            p_hb[i]->nframes    = hb->nframes;
+            p_hb[i]->maxhydro   = hb->maxhydro;
+            p_hb[i]->danr       = hb->danr;
+            p_hb[i]->d          = hb->d;
+            p_hb[i]->a          = hb->a;
+            p_hb[i]->hbmap      = hb->hbmap;
+            p_hb[i]->time       = hb->time; /* This may need re-syncing at every frame. */
+            p_hb[i]->per        = hb->per;
 
 #ifdef HAVE_NN_LOOPS
-        p_hb[i]->hbE = hb->hbE;
+            p_hb[i]->hbE = hb->hbE;
 #endif
 
-        p_hb[i]->nrhb   = 0;
-        p_hb[i]->nrdist = 0;
+            p_hb[i]->nrhb   = 0;
+            p_hb[i]->nrdist = 0;
+        }
     }
   
     /* Make a thread pool here,
      * instead of forking anew at every frame. */
   
 #pragma omp parallel                                    \
-    private(i, j, h, ii, jj, hh, E,                     \
+    firstprivate(i)                                     \
+    private(j, h, ii, jj, hh, E,                        \
             xi, yi, zi, xj, yj, zj, threadNr,           \
             dist, ang, peri, icell, jcell,              \
             grp, ogrp, ai, aj, xjj, yjj, zjj,           \
             xk, yk, zk, ihb, id,  resdist,              \
-            xkk, ykk, zkk, kcell, ak, k, bTric)        \
-    default(none)                                       \
-    shared(hb, p_hb, p_adist, p_rdist, actual_nThreads, \
-           x, bBox, box, hbox, rcut, r2cut, rshell,     \
-           shatom, ngrid, grid, nframes, t,             \
-           bParallel, bNN, index, bMerge, bContact,     \
-           bTwo, bDA,ccut, abin, rbin, top,             \
-           bSelected, bDebug, stderr, nsel,             \
-           bGem, oenv, fnm, fpnhb, trrStatus, natoms,   \
-           status, nabin, nrbin, adist, rdist, debug)
+            xkk, ykk, zkk, kcell, ak, k, bTric,         \
+            bEdge_xjj, bEdge_yjj)                       \
+    default(shared)
     {    /* Start of parallel region */
-        threadNr = omp_get_thread_num();
-#endif /* HAVE_OPENMP ================================================= */
+        threadNr = gmx_omp_get_thread_num();
+
         do
         {
+            
             bTric = bBox && TRICLINIC(box);
 
-#ifdef HAVE_OPENMP
-            sync_hbdata(hb, p_hb[threadNr], nframes, t);
+            if (bOMP)
+            {
+                sync_hbdata(hb, p_hb[threadNr], nframes, t);
+            }
 #pragma omp single
-#endif
             {
                 build_grid(hb,x,x[shatom], bBox,box,hbox, (rcut>r2cut)?rcut:r2cut, 
                            rshell, ngrid,grid);
@@ -3660,23 +3567,24 @@ int gmx_hbond(int argc,char *argv[])
                 if (hb->bDAnr)
                     count_da_grid(ngrid, grid, hb->danr[nframes]);
             } /* omp single */
-#ifdef HAVE_OPENMP
-            p_hb[threadNr]->time = hb->time; /* This pointer may have changed. */
-#endif
+
+            if (bOMP)
+            {
+                p_hb[threadNr]->time = hb->time; /* This pointer may have changed. */
+            }
+
             if (bNN)
             {
 #ifdef HAVE_NN_LOOPS /* Unlock this feature when testing */
                 /* Loop over all atom pairs and estimate interaction energy */
-#ifdef HAVE_OPENMP /* ------- */
+
 #pragma omp single
-#endif /* HAVE_OPENMP ------- */
                 {
                     addFramesNN(hb, nframes);
                 }
-#ifdef HAVE_OPENMP /* ---------------- */
+
 #pragma omp barrier
 #pragma omp for schedule(dynamic)
-#endif /* HAVE_OPENMP ---------------- */
                 for (i=0; i<hb->d.nrd; i++)
                 {
                     for(j=0;j<hb->a.nra; j++)
@@ -3706,9 +3614,8 @@ int gmx_hbond(int argc,char *argv[])
             {
                 if (bSelected)
                 {
-#ifdef HAVE_OPENMP
+
 #pragma omp single
-#endif
                     {
                         /* Do not parallelize this just yet. */
                         /* int ii; */
@@ -3729,20 +3636,18 @@ int gmx_hbond(int argc,char *argv[])
                 } /* if (bSelected) */
                 else
                 {
-#ifdef HAVE_OPENMP
+
 #pragma omp single
                     {
-#endif
-                    if (bGem)
-                        calcBoxProjection(box, hb->per->P);
+                        if (bGem)
+                            calcBoxProjection(box, hb->per->P);
+
+                        /* loop over all gridcells (xi,yi,zi)      */
+                        /* Removed confusing macro, DvdS 27/12/98  */
 
-                    /* loop over all gridcells (xi,yi,zi)      */
-                    /* Removed confusing macro, DvdS 27/12/98  */
-#ifdef HAVE_OPENMP
                     }
                     /* The outer grid loop will have to do for now. */
 #pragma omp for schedule(dynamic)
-#endif
                     for(xi=0; xi<ngrid[XX]; xi++)
                         for(yi=0; (yi<ngrid[YY]); yi++)
                             for(zi=0; (zi<ngrid[ZZ]); zi++) {
@@ -3763,130 +3668,150 @@ int gmx_hbond(int argc,char *argv[])
                                         i  = icell->atoms[ai];
                
                                         /* loop over all adjacent gridcells (xj,yj,zj) */
-                                        /* This is a macro!!! */
-                                        LOOPGRIDINNER(xj,yj,zj,xjj,yjj,zjj,xi,yi,zi,ngrid,bTric) {
-                                            jcell=&(grid[zj][yj][xj].a[ogrp]);
-                                            /* loop over acceptor atoms from other group (ogrp) 
-                                             * in this adjacent gridcell (jcell) 
-                                             */
-                                            for (aj=0; (aj<jcell->nr); aj++) {
-                                                j = jcell->atoms[aj];
-                 
-                                                /* check if this once was a h-bond */
-                                                peri = -1;
-                                                ihb = is_hbond(__HBDATA,grp,ogrp,i,j,rcut,r2cut,ccut,x,bBox,box,
-                                                               hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
-                   
-                                                if (ihb) {
-                                                    /* add to index if not already there */
-                                                    /* Add a hbond */
-                                                    add_hbond(__HBDATA,i,j,h,grp,ogrp,nframes,bMerge,ihb,bContact,peri);
-                     
-                                                    /* make angle and distance distributions */
-                                                    if (ihb == hbHB && !bContact) {
-                                                        if (dist>rcut)
-                                                            gmx_fatal(FARGS,"distance is higher than what is allowed for an hbond: %f",dist);
-                                                        ang*=RAD2DEG;
-                                                        __ADIST[(int)( ang/abin)]++;
-                                                        __RDIST[(int)(dist/rbin)]++;
-                                                        if (!bTwo) {
-                                                            int id,ia;
-                                                            if ((id = donor_index(&hb->d,grp,i)) == NOTSET)
-                                                                gmx_fatal(FARGS,"Invalid donor %d",i);
-                                                            if ((ia = acceptor_index(&hb->a,ogrp,j)) == NOTSET)
-                                                                gmx_fatal(FARGS,"Invalid acceptor %d",j);
-                                                            resdist=abs(top.atoms.atom[i].resind-
-                                                                        top.atoms.atom[j].resind);
-                                                            if (resdist >= max_hx)
-                                                                resdist = max_hx-1;
-                                                            __HBDATA->nhx[nframes][resdist]++;
+                                        for(zjj = grid_loop_begin(ngrid[ZZ],zi,bTric,FALSE);
+                                            zjj <= grid_loop_end(ngrid[ZZ],zi,bTric,FALSE);
+                                            zjj++)
+                                        {
+                                            zj = grid_mod(zjj,ngrid[ZZ]);
+                                            bEdge_yjj = (zj == 0) || (zj == ngrid[ZZ] - 1);
+                                            for(yjj = grid_loop_begin(ngrid[YY],yi,bTric,bEdge_yjj);
+                                                yjj <= grid_loop_end(ngrid[YY],yi,bTric,bEdge_yjj);
+                                                yjj++)
+                                            {
+                                                yj = grid_mod(yjj,ngrid[YY]);
+                                                bEdge_xjj =
+                                                    (yj == 0) || (yj == ngrid[YY] - 1) ||
+                                                    (zj == 0) || (zj == ngrid[ZZ] - 1);
+                                                for(xjj = grid_loop_begin(ngrid[XX],xi,bTric,bEdge_xjj);
+                                                    xjj <= grid_loop_end(ngrid[XX],xi,bTric,bEdge_xjj);
+                                                    xjj++)
+                                                {
+                                                    xj = grid_mod(xjj,ngrid[XX]);
+                                                    jcell=&(grid[zj][yj][xj].a[ogrp]);
+                                                    /* loop over acceptor atoms from other group (ogrp) 
+                                                     * in this adjacent gridcell (jcell) 
+                                                     */
+                                                    for (aj=0; (aj<jcell->nr); aj++) {
+                                                        j = jcell->atoms[aj];
+                                                        
+                                                        /* check if this once was a h-bond */
+                                                        peri = -1;
+                                                        ihb = is_hbond(__HBDATA,grp,ogrp,i,j,rcut,r2cut,ccut,x,bBox,box,
+                                                                       hbox,&dist,&ang,bDA,&h,bContact,bMerge,&peri);
+                                                        
+                                                        if (ihb) {
+                                                            /* add to index if not already there */
+                                                            /* Add a hbond */
+                                                            add_hbond(__HBDATA,i,j,h,grp,ogrp,nframes,bMerge,ihb,bContact,peri);
+                                                            
+                                                            /* make angle and distance distributions */
+                                                            if (ihb == hbHB && !bContact) {
+                                                                if (dist>rcut)
+                                                                    gmx_fatal(FARGS,"distance is higher than what is allowed for an hbond: %f",dist);
+                                                                ang*=RAD2DEG;
+                                                                __ADIST[(int)( ang/abin)]++;
+                                                                __RDIST[(int)(dist/rbin)]++;
+                                                                if (!bTwo) {
+                                                                    int id,ia;
+                                                                    if ((id = donor_index(&hb->d,grp,i)) == NOTSET)
+                                                                        gmx_fatal(FARGS,"Invalid donor %d",i);
+                                                                    if ((ia = acceptor_index(&hb->a,ogrp,j)) == NOTSET)
+                                                                        gmx_fatal(FARGS,"Invalid acceptor %d",j);
+                                                                    resdist=abs(top.atoms.atom[i].resind-
+                                                                                top.atoms.atom[j].resind);
+                                                                    if (resdist >= max_hx)
+                                                                        resdist = max_hx-1;
+                                                                    __HBDATA->nhx[nframes][resdist]++;
+                                                                }
+                                                            }
+                                                            
                                                         }
-                                                    }
-
-                                                }
-                                            } /* for aj  */
-                                        }
-                                        ENDLOOPGRIDINNER;
+                                                    } /* for aj  */
+                                                } /* for xjj */
+                                            } /* for yjj */
+                                        } /* for zjj */
                                     } /* for ai  */
                                 } /* for grp */
                             } /* for xi,yi,zi */
                 } /* if (bSelected) {...} else */ 
 
-#ifdef HAVE_OPENMP /* ---------------------------- */
+
                 /* Better wait for all threads to finnish using x[] before updating it. */
-                k = nframes;            /*         */
-#pragma omp barrier                     /*         */
-#pragma omp critical                    /*         */
-                {                       /*         */
+                k = nframes;
+#pragma omp barrier
+#pragma omp critical
+                {
                     /* Sum up histograms and counts from p_hb[] into hb */
-                    {                   /*         */
+                    if (bOMP)
+                    {
                         hb->nhb[k]   += p_hb[threadNr]->nhb[k];
                         hb->ndist[k] += p_hb[threadNr]->ndist[k];
-                        for (j=0; j<max_hx; j++) /**/
+                        for (j=0; j<max_hx; j++)
                             hb->nhx[k][j]  += p_hb[threadNr]->nhx[k][j];
-                    }                   /*         */
-                }                       /*         */
-                /*                                 */
+                    }
+                }
+
                 /* Here are a handful of single constructs
                  * to share the workload a bit. The most
                  * important one is of course the last one,
                  * where there's a potential bottleneck in form
                  * of slow I/O.                    */
-#pragma omp single /* ++++++++++++++++,            */
-#endif /* HAVE_OPENMP ----------------+------------*/
-                { /*                  +   */
-                    if (hb != NULL)  /*   */
-                    { /*              +   */
+#pragma omp barrier
+#pragma omp single
+                {
+                    if (hb != NULL)
+                    {
                         analyse_donor_props(opt2fn_null("-don",NFILE,fnm),hb,k,t,oenv);
-                    } /*              +   */
-                } /*                  +   */
-#ifdef HAVE_OPENMP /*                 +   */
-#pragma omp single /* +++           +++   */
-#endif       /*                       +   */
-                {  /*                 +   */
-                    if (fpnhb)  /*    +   */
+                    }
+                }
+
+#pragma omp single
+                {
+                    if (fpnhb)
                         do_nhb_dist(fpnhb,hb,t);
-                }  /*                 +   */
+                }
             } /* if (bNN) {...} else  +   */
-#ifdef HAVE_OPENMP /*                 +   */
-#pragma omp single /* +++           +++   */
-#endif       /*                       +   */
-            {      /*                 +   */
+
+#pragma omp single
+            {
                 trrStatus = (read_next_x(oenv,status,&t,natoms,x,box));
-                nframes++;      /*    +   */
-            }      /*                 +   */
-#ifdef HAVE_OPENMP /* +++++++++++++++++   */
+                nframes++;
+            }
+
 #pragma omp barrier
-#endif
         } while (trrStatus);
 
-#ifdef HAVE_OPENMP
-#pragma omp critical
+        if (bOMP)
         {
-            hb->nrhb += p_hb[threadNr]->nrhb;
-            hb->nrdist += p_hb[threadNr]->nrdist;
-        }
-        /* Free parallel datastructures */
-        sfree(p_hb[threadNr]->nhb);
-        sfree(p_hb[threadNr]->ndist);
-        sfree(p_hb[threadNr]->nhx);
+#pragma omp critical
+            {
+                hb->nrhb += p_hb[threadNr]->nrhb;
+                hb->nrdist += p_hb[threadNr]->nrdist;
+            }
+            /* Free parallel datastructures */
+            sfree(p_hb[threadNr]->nhb);
+            sfree(p_hb[threadNr]->ndist);
+            sfree(p_hb[threadNr]->nhx);
 
 #pragma omp for
-        for (i=0; i<nabin; i++)
-            for (j=0; j<actual_nThreads; j++)
+            for (i=0; i<nabin; i++)
+                for (j=0; j<actual_nThreads; j++)
 
-                adist[i] += p_adist[j][i];
+                    adist[i] += p_adist[j][i];
 #pragma omp for
-        for (i=0; i<=nrbin; i++)
-            for (j=0; j<actual_nThreads; j++)
-                rdist[i] += p_rdist[j][i];
+            for (i=0; i<=nrbin; i++)
+                for (j=0; j<actual_nThreads; j++)
+                    rdist[i] += p_rdist[j][i];
     
-        sfree(p_adist[threadNr]);
-        sfree(p_rdist[threadNr]);
+            sfree(p_adist[threadNr]);
+            sfree(p_rdist[threadNr]);
+        }
     } /* End of parallel region */
-    sfree(p_adist);
-    sfree(p_rdist);
-#endif
+    if (bOMP)
+    {
+        sfree(p_adist);
+        sfree(p_rdist);
+    }
   
     if(nframes <2 && (opt2bSet("-ac",NFILE,fnm) || opt2bSet("-life",NFILE,fnm)))
     {