Merge release-5-0 into master
[alexxy/gromacs.git] / src / programs / mdrun / pme_loadbal.c
index 3fff053d1dae1f9e1f6f339887e9c8e3c9beeae5..dd2cd1a2e9f049b01ff6de90078d249f925c0cf7 100644 (file)
@@ -1,67 +1,72 @@
-/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+/*
+ * This file is part of the GROMACS molecular simulation package.
  *
- * 
- *                This source code is part of
- * 
- *                 G   R   O   M   A   C   S
- * 
- *          GROningen MAchine for Chemical Simulations
- * 
- *                        VERSION 4.6.0
- * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
- * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
- * Copyright (c) 2001-2011, The GROMACS development team,
- * check out http://www.gromacs.org for more information.
-
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
+ * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
  * of the License, or (at your option) any later version.
- * 
- * If you want to redistribute modifications, please consider that
- * scientific software is very special. Version control is crucial -
- * bugs must be traceable. We will be happy to consider code for
- * inclusion in the official distribution, but derived work must not
- * be called official GROMACS. Details are found in the README & COPYING
- * files - if they are missing, get the official version at www.gromacs.org.
- * 
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
  * To help us fund GROMACS development, we humbly ask that you cite
- * the papers on the package - you can find them in the top README file.
- * 
- * For more info, check our website at http://www.gromacs.org
- * 
- * And Hey:
- * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
+ * the research papers on the package. Check out http://www.gromacs.org.
  */
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "smalloc.h"
-#include "network.h"
-#include "calcgrid.h"
-#include "pme.h"
-#include "vec.h"
-#include "domdec.h"
-#include "nbnxn_cuda_data_mgmt.h"
-#include "force.h"
-#include "macros.h"
+#include "gmxpre.h"
+
 #include "pme_loadbal.h"
 
+#include "config.h"
+
+#include "gromacs/legacyheaders/calcgrid.h"
+#include "gromacs/legacyheaders/domdec.h"
+#include "gromacs/legacyheaders/force.h"
+#include "gromacs/legacyheaders/macros.h"
+#include "gromacs/legacyheaders/md_logging.h"
+#include "gromacs/legacyheaders/network.h"
+#include "gromacs/legacyheaders/pme.h"
+#include "gromacs/legacyheaders/sim_util.h"
+#include "gromacs/legacyheaders/types/commrec.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/mdlib/nbnxn_cuda/nbnxn_cuda_data_mgmt.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/smalloc.h"
+
 /* Parameters and setting for one PP-PME setup */
 typedef struct {
-    real rcut_coulomb;    /* Coulomb cut-off                              */
-    real rlist;           /* pair-list cut-off                            */
-    real rlistlong;       /* LR pair-list cut-off                         */
-    int  nstcalclr;       /* frequency of evaluating long-range forces for group scheme */
-    real spacing;         /* (largest) PME grid spacing                   */
-    ivec grid;            /* the PME grid dimensions                      */
-    real grid_efficiency; /* ineffiency factor for non-uniform grids <= 1 */
-    real ewaldcoeff;      /* the Ewald coefficient                        */
-    gmx_pme_t pmedata;    /* the data structure used in the PME code      */
-
-    int  count;           /* number of times this setup has been timed    */
-    double cycles;        /* the fastest time for this setup in cycles    */
+    real      rcut_coulomb;    /* Coulomb cut-off                              */
+    real      rlist;           /* pair-list cut-off                            */
+    real      rlistlong;       /* LR pair-list cut-off                         */
+    int       nstcalclr;       /* frequency of evaluating long-range forces for group scheme */
+    real      spacing;         /* (largest) PME grid spacing                   */
+    ivec      grid;            /* the PME grid dimensions                      */
+    real      grid_efficiency; /* ineffiency factor for non-uniform grids <= 1 */
+    real      ewaldcoeff_q;    /* Electrostatic Ewald coefficient            */
+    real      ewaldcoeff_lj;   /* LJ Ewald coefficient, only for the call to send_switchgrid */
+    gmx_pme_t pmedata;         /* the data structure used in the PME code      */
+    int       count;           /* number of times this setup has been timed    */
+    double    cycles;          /* the fastest time for this setup in cycles    */
 } pme_setup_t;
 
 /* In the initial scan, step by grids that are at least a factor 0.8 coarser */
@@ -77,57 +82,59 @@ typedef struct {
  */
 #define PME_LB_ACCEL_TOL 1.02
 
-enum { epmelblimNO, epmelblimBOX, epmelblimDD, epmelblimNR };
+enum {
+    epmelblimNO, epmelblimBOX, epmelblimDD, epmelblimPMEGRID, epmelblimNR
+};
 
 const char *pmelblim_str[epmelblimNR] =
-{ "no", "box size", "domain decompostion" };
+{ "no", "box size", "domain decompostion", "PME grid restriction" };
 
 struct pme_load_balancing {
-    int  nstage;        /* the current maximum number of stages */
-
-    real cut_spacing;   /* the minimum cutoff / PME grid spacing ratio */
-    real rcut_vdw;      /* Vdw cutoff (does not change) */
-    real rcut_coulomb_start; /* Initial electrostatics cutoff */
-    int  nstcalclr_start; /* Initial electrostatics cutoff */
-    real rbuf_coulomb;  /* the pairlist buffer size */
-    real rbuf_vdw;      /* the pairlist buffer size */
-    matrix box_start;   /* the initial simulation box */
-    int n;              /* the count of setup as well as the allocation size */
-    pme_setup_t *setup; /* the PME+cutoff setups */
-    int cur;            /* the current setup */
-    int fastest;        /* fastest setup up till now */
-    int start;          /* start of setup range to consider in stage>0 */
-    int end;            /* end   of setup range to consider in stage>0 */
-    int elimited;       /* was the balancing limited, uses enum above */
-    int cutoff_scheme;  /* Verlet or group cut-offs */
-
-    int stage;          /* the current stage */
+    int          nstage;             /* the current maximum number of stages */
+
+    real         cut_spacing;        /* the minimum cutoff / PME grid spacing ratio */
+    real         rcut_vdw;           /* Vdw cutoff (does not change) */
+    real         rcut_coulomb_start; /* Initial electrostatics cutoff */
+    int          nstcalclr_start;    /* Initial electrostatics cutoff */
+    real         rbuf_coulomb;       /* the pairlist buffer size */
+    real         rbuf_vdw;           /* the pairlist buffer size */
+    matrix       box_start;          /* the initial simulation box */
+    int          n;                  /* the count of setup as well as the allocation size */
+    pme_setup_t *setup;              /* the PME+cutoff setups */
+    int          cur;                /* the current setup */
+    int          fastest;            /* fastest setup up till now */
+    int          start;              /* start of setup range to consider in stage>0 */
+    int          end;                /* end   of setup range to consider in stage>0 */
+    int          elimited;           /* was the balancing limited, uses enum above */
+    int          cutoff_scheme;      /* Verlet or group cut-offs */
+
+    int          stage;              /* the current stage */
 };
 
 void pme_loadbal_init(pme_load_balancing_t *pme_lb_p,
-                      const t_inputrec *ir,matrix box,
+                      const t_inputrec *ir, matrix box,
                       const interaction_const_t *ic,
                       gmx_pme_t pmedata)
 {
     pme_load_balancing_t pme_lb;
-    real spm,sp;
-    int  d;
+    real                 spm, sp;
+    int                  d;
 
-    snew(pme_lb,1);
+    snew(pme_lb, 1);
 
     /* Any number of stages >= 2 is supported */
     pme_lb->nstage   = 2;
 
     pme_lb->cutoff_scheme = ir->cutoff_scheme;
 
-    if(pme_lb->cutoff_scheme == ecutsVERLET)
+    if (pme_lb->cutoff_scheme == ecutsVERLET)
     {
         pme_lb->rbuf_coulomb = ic->rlist - ic->rcoulomb;
         pme_lb->rbuf_vdw     = pme_lb->rbuf_coulomb;
     }
     else
     {
-        if(ic->rcoulomb > ic->rlist)
+        if (ic->rcoulomb > ic->rlist)
         {
             pme_lb->rbuf_coulomb = ic->rlistlong - ic->rcoulomb;
         }
@@ -135,7 +142,7 @@ void pme_loadbal_init(pme_load_balancing_t *pme_lb_p,
         {
             pme_lb->rbuf_coulomb = ic->rlist - ic->rcoulomb;
         }
-        if(ic->rvdw > ic->rlist)
+        if (ic->rvdw > ic->rlist)
         {
             pme_lb->rbuf_vdw = ic->rlistlong - ic->rvdw;
         }
@@ -145,33 +152,34 @@ void pme_loadbal_init(pme_load_balancing_t *pme_lb_p,
         }
     }
 
-    copy_mat(box,pme_lb->box_start);
-    if (ir->ePBC==epbcXY && ir->nwall==2)
+    copy_mat(box, pme_lb->box_start);
+    if (ir->ePBC == epbcXY && ir->nwall == 2)
     {
-        svmul(ir->wall_ewald_zfac,pme_lb->box_start[ZZ],pme_lb->box_start[ZZ]);
+        svmul(ir->wall_ewald_zfac, pme_lb->box_start[ZZ], pme_lb->box_start[ZZ]);
     }
 
     pme_lb->n = 1;
-    snew(pme_lb->setup,pme_lb->n);
-
-    pme_lb->rcut_vdw              = ic->rvdw;
-    pme_lb->rcut_coulomb_start    = ir->rcoulomb;
-    pme_lb->nstcalclr_start       = ir->nstcalclr;
-    
-    pme_lb->cur = 0;
-    pme_lb->setup[0].rcut_coulomb = ic->rcoulomb;
-    pme_lb->setup[0].rlist        = ic->rlist;
-    pme_lb->setup[0].rlistlong    = ic->rlistlong;
-    pme_lb->setup[0].nstcalclr    = ir->nstcalclr;
-    pme_lb->setup[0].grid[XX]     = ir->nkx;
-    pme_lb->setup[0].grid[YY]     = ir->nky;
-    pme_lb->setup[0].grid[ZZ]     = ir->nkz;
-    pme_lb->setup[0].ewaldcoeff   = ic->ewaldcoeff;
+    snew(pme_lb->setup, pme_lb->n);
+
+    pme_lb->rcut_vdw                 = ic->rvdw;
+    pme_lb->rcut_coulomb_start       = ir->rcoulomb;
+    pme_lb->nstcalclr_start          = ir->nstcalclr;
+
+    pme_lb->cur                      = 0;
+    pme_lb->setup[0].rcut_coulomb    = ic->rcoulomb;
+    pme_lb->setup[0].rlist           = ic->rlist;
+    pme_lb->setup[0].rlistlong       = ic->rlistlong;
+    pme_lb->setup[0].nstcalclr       = ir->nstcalclr;
+    pme_lb->setup[0].grid[XX]        = ir->nkx;
+    pme_lb->setup[0].grid[YY]        = ir->nky;
+    pme_lb->setup[0].grid[ZZ]        = ir->nkz;
+    pme_lb->setup[0].ewaldcoeff_q    = ic->ewaldcoeff_q;
+    pme_lb->setup[0].ewaldcoeff_lj   = ic->ewaldcoeff_lj;
 
     pme_lb->setup[0].pmedata  = pmedata;
-    
+
     spm = 0;
-    for(d=0; d<DIM; d++)
+    for (d = 0; d < DIM; d++)
     {
         sp = norm(pme_lb->box_start[d])/pme_lb->setup[0].grid[d];
         if (sp > spm)
@@ -200,49 +208,74 @@ void pme_loadbal_init(pme_load_balancing_t *pme_lb_p,
     *pme_lb_p = pme_lb;
 }
 
-static gmx_bool pme_loadbal_increase_cutoff(pme_load_balancing_t pme_lb,
-                                            int pme_order)
+static gmx_bool pme_loadbal_increase_cutoff(pme_load_balancing_t  pme_lb,
+                                            int                   pme_order,
+                                            const gmx_domdec_t   *dd)
 {
     pme_setup_t *set;
-    real fac,sp;
-    real tmpr_coulomb,tmpr_vdw;
-    int d;
+    int          npmenodes_x, npmenodes_y;
+    real         fac, sp;
+    real         tmpr_coulomb, tmpr_vdw;
+    int          d;
+    gmx_bool     grid_ok;
 
     /* Try to add a new setup with next larger cut-off to the list */
     pme_lb->n++;
-    srenew(pme_lb->setup,pme_lb->n);
-    set = &pme_lb->setup[pme_lb->n-1];
+    srenew(pme_lb->setup, pme_lb->n);
+    set          = &pme_lb->setup[pme_lb->n-1];
     set->pmedata = NULL;
 
+    get_pme_nnodes(dd, &npmenodes_x, &npmenodes_y);
+
     fac = 1;
     do
     {
+        /* Avoid infinite while loop, which can occur at the minimum grid size.
+         * Note that in practice load balancing will stop before this point.
+         * The factor 2.1 allows for the extreme case in which only grids
+         * of powers of 2 are allowed (the current code supports more grids).
+         */
+        if (fac > 2.1)
+        {
+            pme_lb->n--;
+
+            return FALSE;
+        }
+
         fac *= 1.01;
         clear_ivec(set->grid);
-        sp = calc_grid(NULL,pme_lb->box_start,
+        sp = calc_grid(NULL, pme_lb->box_start,
                        fac*pme_lb->setup[pme_lb->cur].spacing,
                        &set->grid[XX],
                        &set->grid[YY],
                        &set->grid[ZZ]);
 
-        /* In parallel we can't have grids smaller than 2*pme_order,
-         * and we would anyhow not gain much speed at these grid sizes.
+        /* As here we can't easily check if one of the PME nodes
+         * uses threading, we do a conservative grid check.
+         * This means we can't use pme_order or less grid lines
+         * per PME node along x, which is not a strong restriction.
          */
-        for(d=0; d<DIM; d++)
-        {
-            if (set->grid[d] <= 2*pme_order)
-            {
-                pme_lb->n--;
-
-                return FALSE;
-            }
-        }
+        gmx_pme_check_restrictions(pme_order,
+                                   set->grid[XX], set->grid[YY], set->grid[ZZ],
+                                   npmenodes_x, npmenodes_y,
+                                   TRUE,
+                                   FALSE,
+                                   &grid_ok);
     }
-    while (sp <= 1.001*pme_lb->setup[pme_lb->cur].spacing);
+    while (sp <= 1.001*pme_lb->setup[pme_lb->cur].spacing || !grid_ok);
 
     set->rcut_coulomb = pme_lb->cut_spacing*sp;
+    if (set->rcut_coulomb < pme_lb->rcut_coulomb_start)
+    {
+        /* This is unlikely, but can happen when e.g. continuing from
+         * a checkpoint after equilibration where the box shrank a lot.
+         * We want to avoid rcoulomb getting smaller than rvdw
+         * and there might be more issues with decreasing rcoulomb.
+         */
+        set->rcut_coulomb = pme_lb->rcut_coulomb_start;
+    }
 
-    if(pme_lb->cutoff_scheme == ecutsVERLET)
+    if (pme_lb->cutoff_scheme == ecutsVERLET)
     {
         set->rlist        = set->rcut_coulomb + pme_lb->rbuf_coulomb;
         /* We dont use LR lists with Verlet, but this avoids if-statements in further checks */
@@ -252,16 +285,16 @@ static gmx_bool pme_loadbal_increase_cutoff(pme_load_balancing_t pme_lb,
     {
         tmpr_coulomb          = set->rcut_coulomb + pme_lb->rbuf_coulomb;
         tmpr_vdw              = pme_lb->rcut_vdw + pme_lb->rbuf_vdw;
-        set->rlist            = min(tmpr_coulomb,tmpr_vdw);
-        set->rlistlong        = max(tmpr_coulomb,tmpr_vdw);
-        
+        set->rlist            = min(tmpr_coulomb, tmpr_vdw);
+        set->rlistlong        = max(tmpr_coulomb, tmpr_vdw);
+
         /* Set the long-range update frequency */
-        if(set->rlist == set->rlistlong)
+        if (set->rlist == set->rlistlong)
         {
             /* No long-range interactions if the short-/long-range cutoffs are identical */
             set->nstcalclr = 0;
         }
-        else if(pme_lb->nstcalclr_start==0 || pme_lb->nstcalclr_start==1)
+        else if (pme_lb->nstcalclr_start == 0 || pme_lb->nstcalclr_start == 1)
         {
             /* We were not doing long-range before, but now we are since rlist!=rlistlong */
             set->nstcalclr = 1;
@@ -269,7 +302,7 @@ static gmx_bool pme_loadbal_increase_cutoff(pme_load_balancing_t pme_lb,
         else
         {
             /* We were already doing long-range interactions from the start */
-            if(pme_lb->rcut_vdw > pme_lb->rcut_coulomb_start)
+            if (pme_lb->rcut_vdw > pme_lb->rcut_coulomb_start)
             {
                 /* We were originally doing long-range VdW-only interactions.
                  * If rvdw is still longer than rcoulomb we keep the original nstcalclr,
@@ -287,56 +320,59 @@ static gmx_bool pme_loadbal_increase_cutoff(pme_load_balancing_t pme_lb,
             }
         }
     }
-    
+
     set->spacing      = sp;
     /* The grid efficiency is the size wrt a grid with uniform x/y/z spacing */
     set->grid_efficiency = 1;
-    for(d=0; d<DIM; d++)
+    for (d = 0; d < DIM; d++)
     {
         set->grid_efficiency *= (set->grid[d]*sp)/norm(pme_lb->box_start[d]);
     }
     /* The Ewald coefficient is inversly proportional to the cut-off */
-    set->ewaldcoeff =
-        pme_lb->setup[0].ewaldcoeff*pme_lb->setup[0].rcut_coulomb/set->rcut_coulomb;
+    set->ewaldcoeff_q =
+        pme_lb->setup[0].ewaldcoeff_q*pme_lb->setup[0].rcut_coulomb/set->rcut_coulomb;
+    /* We set ewaldcoeff_lj in set, even when LJ-PME is not used */
+    set->ewaldcoeff_lj =
+        pme_lb->setup[0].ewaldcoeff_lj*pme_lb->setup[0].rcut_coulomb/set->rcut_coulomb;
 
     set->count   = 0;
     set->cycles  = 0;
 
     if (debug)
     {
-        fprintf(debug,"PME loadbal: grid %d %d %d, coulomb cutoff %f\n",
-                set->grid[XX],set->grid[YY],set->grid[ZZ],set->rcut_coulomb);
+        fprintf(debug, "PME loadbal: grid %d %d %d, coulomb cutoff %f\n",
+                set->grid[XX], set->grid[YY], set->grid[ZZ], set->rcut_coulomb);
     }
     return TRUE;
 }
 
-static void print_grid(FILE *fp_err,FILE *fp_log,
+static void print_grid(FILE *fp_err, FILE *fp_log,
                        const char *pre,
                        const char *desc,
                        const pme_setup_t *set,
                        double cycles)
 {
-    char buf[STRLEN],buft[STRLEN];
+    char buf[STRLEN], buft[STRLEN];
 
     if (cycles >= 0)
     {
-        sprintf(buft,": %.1f M-cycles",cycles*1e-6);
+        sprintf(buft, ": %.1f M-cycles", cycles*1e-6);
     }
     else
     {
         buft[0] = '\0';
     }
-    sprintf(buf,"%-11s%10s pme grid %d %d %d, coulomb cutoff %.3f%s",
+    sprintf(buf, "%-11s%10s pme grid %d %d %d, coulomb cutoff %.3f%s",
             pre,
-            desc,set->grid[XX],set->grid[YY],set->grid[ZZ],set->rcut_coulomb,
+            desc, set->grid[XX], set->grid[YY], set->grid[ZZ], set->rcut_coulomb,
             buft);
     if (fp_err != NULL)
     {
-        fprintf(fp_err,"\r%s\n",buf);
+        fprintf(fp_err, "\r%s\n", buf);
     }
     if (fp_log != NULL)
     {
-        fprintf(fp_log,"%s\n",buf);
+        fprintf(fp_log, "%s\n", buf);
     }
 }
 
@@ -353,23 +389,23 @@ static int pme_loadbal_end(pme_load_balancing_t pme_lb)
     }
 }
 
-static void print_loadbal_limited(FILE *fp_err,FILE *fp_log,
-                                  gmx_large_int_t step,
+static void print_loadbal_limited(FILE *fp_err, FILE *fp_log,
+                                  gmx_int64_t step,
                                   pme_load_balancing_t pme_lb)
 {
-    char buf[STRLEN],sbuf[22];
+    char buf[STRLEN], sbuf[22];
 
-    sprintf(buf,"step %4s: the %s limited the PME load balancing to a coulomb cut-off of %.3f",
-            gmx_step_str(step,sbuf),
+    sprintf(buf, "step %4s: the %s limits the PME load balancing to a coulomb cut-off of %.3f",
+            gmx_step_str(step, sbuf),
             pmelblim_str[pme_lb->elimited],
             pme_lb->setup[pme_loadbal_end(pme_lb)-1].rcut_coulomb);
     if (fp_err != NULL)
     {
-        fprintf(fp_err,"\r%s\n",buf);
+        fprintf(fp_err, "\r%s\n", buf);
     }
     if (fp_log != NULL)
     {
-        fprintf(fp_log,"%s\n",buf);
+        fprintf(fp_log, "%s\n", buf);
     }
 }
 
@@ -404,24 +440,24 @@ static void switch_to_stage1(pme_load_balancing_t pme_lb)
     pme_lb->cur = pme_lb->start - 1;
 }
 
-gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
-                          t_commrec *cr,
-                          FILE *fp_err,
-                          FILE *fp_log,
-                          t_inputrec *ir,
-                          t_state *state,
-                          double cycles,
-                          interaction_const_t *ic,
-                          nonbonded_verlet_t *nbv,
-                          gmx_pme_t *pmedata,
-                          gmx_large_int_t step)
+gmx_bool pme_load_balance(pme_load_balancing_t        pme_lb,
+                          t_commrec                  *cr,
+                          FILE                       *fp_err,
+                          FILE                       *fp_log,
+                          t_inputrec                 *ir,
+                          t_state                    *state,
+                          double                      cycles,
+                          interaction_const_t        *ic,
+                          struct nonbonded_verlet_t  *nbv,
+                          gmx_pme_t                  *pmedata,
+                          gmx_int64_t                 step)
 {
-    gmx_bool OK;
+    gmx_bool     OK;
     pme_setup_t *set;
-    double cycles_fast;
-    char buf[STRLEN],sbuf[22];
-    real rtab;
-    gmx_bool bUsesSimpleTables = TRUE;
+    double       cycles_fast;
+    char         buf[STRLEN], sbuf[22];
+    real         rtab;
+    gmx_bool     bUsesSimpleTables = TRUE;
 
     if (pme_lb->stage == pme_lb->nstage)
     {
@@ -430,7 +466,7 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
 
     if (PAR(cr))
     {
-        gmx_sumd(1,&cycles,cr);
+        gmx_sumd(1, &cycles, cr);
         cycles /= cr->nnodes;
     }
 
@@ -447,8 +483,8 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
         return TRUE;
     }
 
-    sprintf(buf, "step %4s: ", gmx_step_str(step,sbuf));
-    print_grid(fp_err,fp_log,buf,"timed with",set,cycles);
+    sprintf(buf, "step %4s: ", gmx_step_str(step, sbuf));
+    print_grid(fp_err, fp_log, buf, "timed with", set, cycles);
 
     if (set->count <= 2)
     {
@@ -466,15 +502,15 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
 
             if (debug)
             {
-                fprintf(debug,"The performance for grid %d %d %d went from %.3f to %.1f M-cycles, this is more than %f\n"
+                fprintf(debug, "The performance for grid %d %d %d went from %.3f to %.1f M-cycles, this is more than %f\n"
                         "Increased the number stages to %d"
                         " and ignoring the previous performance\n",
-                        set->grid[XX],set->grid[YY],set->grid[ZZ],
-                        cycles*1e-6,set->cycles*1e-6,PME_LB_ACCEL_TOL,
+                        set->grid[XX], set->grid[YY], set->grid[ZZ],
+                        cycles*1e-6, set->cycles*1e-6, PME_LB_ACCEL_TOL,
                         pme_lb->nstage);
             }
         }
-        set->cycles = min(set->cycles,cycles);
+        set->cycles = min(set->cycles, cycles);
     }
 
     if (set->cycles < pme_lb->setup[pme_lb->fastest].cycles)
@@ -527,13 +563,18 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
             else
             {
                 /* Find the next setup */
-                OK = pme_loadbal_increase_cutoff(pme_lb,ir->pme_order);
+                OK = pme_loadbal_increase_cutoff(pme_lb, ir->pme_order, cr->dd);
+
+                if (!OK)
+                {
+                    pme_lb->elimited = epmelblimPMEGRID;
+                }
             }
 
             if (OK && ir->ePBC != epbcNONE)
             {
                 OK = (sqr(pme_lb->setup[pme_lb->cur+1].rlistlong)
-                      <= max_cutoff2(ir->ePBC,state->box));
+                      <= max_cutoff2(ir->ePBC, state->box));
                 if (!OK)
                 {
                     pme_lb->elimited = epmelblimBOX;
@@ -546,7 +587,7 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
 
                 if (DOMAINDECOMP(cr))
                 {
-                    OK = change_dd_cutoff(cr,state,ir,
+                    OK = change_dd_cutoff(cr, state, ir,
                                           pme_lb->setup[pme_lb->cur].rlistlong);
                     if (!OK)
                     {
@@ -562,7 +603,7 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
                  * the setup should not go further than cur.
                  */
                 pme_lb->n = pme_lb->cur + 1;
-                print_loadbal_limited(fp_err,fp_log,step,pme_lb);
+                print_loadbal_limited(fp_err, fp_log, step, pme_lb);
                 /* Switch to the next stage */
                 switch_to_stage1(pme_lb);
             }
@@ -579,7 +620,7 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
 
     if (pme_lb->stage > 0 && pme_lb->end == 1)
     {
-        pme_lb->cur = 0;
+        pme_lb->cur   = 0;
         pme_lb->stage = pme_lb->nstage;
     }
     else if (pme_lb->stage > 0 && pme_lb->end > 1)
@@ -612,7 +653,7 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
 
     if (DOMAINDECOMP(cr) && pme_lb->stage > 0)
     {
-        OK = change_dd_cutoff(cr,state,ir,pme_lb->setup[pme_lb->cur].rlistlong);
+        OK = change_dd_cutoff(cr, state, ir, pme_lb->setup[pme_lb->cur].rlistlong);
         if (!OK)
         {
             /* Failsafe solution */
@@ -625,7 +666,7 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
             pme_lb->end      = pme_lb->cur;
             pme_lb->cur      = pme_lb->start;
             pme_lb->elimited = epmelblimDD;
-            print_loadbal_limited(fp_err,fp_log,step,pme_lb);
+            print_loadbal_limited(fp_err, fp_log, step, pme_lb);
         }
     }
 
@@ -633,29 +674,60 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
 
     set = &pme_lb->setup[pme_lb->cur];
 
-    ic->rcoulomb   = set->rcut_coulomb;
-    ic->rlist      = set->rlist;
-    ic->rlistlong  = set->rlistlong;
-    ir->nstcalclr  = set->nstcalclr;
-    ic->ewaldcoeff = set->ewaldcoeff;
-
-    bUsesSimpleTables = uses_simple_tables(ir->cutoff_scheme, nbv, 0);
-    if (pme_lb->cutoff_scheme == ecutsVERLET &&
-        nbv->grp[0].kernel_type == nbnxnk8x8x8_CUDA)
+    ic->rcoulomb     = set->rcut_coulomb;
+    ic->rlist        = set->rlist;
+    ic->rlistlong    = set->rlistlong;
+    ir->nstcalclr    = set->nstcalclr;
+    ic->ewaldcoeff_q = set->ewaldcoeff_q;
+    /* TODO: centralize the code that sets the potentials shifts */
+    if (ic->coulomb_modifier == eintmodPOTSHIFT)
     {
-        nbnxn_cuda_pme_loadbal_update_param(nbv->cu_nbv,ic);
+        ic->sh_ewald = gmx_erfc(ic->ewaldcoeff_q*ic->rcoulomb);
     }
-    else
+    if (EVDW_PME(ic->vdwtype))
     {
-        init_interaction_const_tables(NULL,ic,bUsesSimpleTables,
-                                      rtab);
+        /* We have PME for both Coulomb and VdW, set rvdw equal to rcoulomb */
+        ic->rvdw            = set->rcut_coulomb;
+        ic->ewaldcoeff_lj   = set->ewaldcoeff_lj;
+        if (ic->vdw_modifier == eintmodPOTSHIFT)
+        {
+            real crc2;
+
+            ic->dispersion_shift.cpot = -pow(ic->rvdw, -6.0);
+            ic->repulsion_shift.cpot  = -pow(ic->rvdw, -12.0);
+            ic->sh_invrc6             = -ic->dispersion_shift.cpot;
+            crc2                      = sqr(ic->ewaldcoeff_lj*ic->rvdw);
+            ic->sh_lj_ewald           = (exp(-crc2)*(1 + crc2 + 0.5*crc2*crc2) - 1)*pow(ic->rvdw, -6.0);
+        }
     }
 
-    if (pme_lb->cutoff_scheme == ecutsVERLET && nbv->ngrp > 1)
+    bUsesSimpleTables = uses_simple_tables(ir->cutoff_scheme, nbv, 0);
+    nbnxn_cuda_pme_loadbal_update_param(nbv, ic);
+
+    /* With tMPI + GPUs some ranks may be sharing GPU(s) and therefore
+     * also sharing texture references. To keep the code simple, we don't
+     * treat texture references as shared resources, but this means that
+     * the coulomb_tab texture ref will get updated by multiple threads.
+     * Hence, to ensure that the non-bonded kernels don't start before all
+     * texture binding operations are finished, we need to wait for all ranks
+     * to arrive here before continuing.
+     *
+     * Note that we could omit this barrier if GPUs are not shared (or
+     * texture objects are used), but as this is initialization code, there
+     * is not point in complicating things.
+     */
+#ifdef GMX_THREAD_MPI
+    if (PAR(cr) && use_GPU(nbv))
     {
-        init_interaction_const_tables(NULL,ic,bUsesSimpleTables,
-                                      rtab);
+        gmx_barrier(cr);
     }
+#endif  /* GMX_THREAD_MPI */
+
+    /* Usually we won't need the simple tables with GPUs.
+     * But we do with hybrid acceleration and with free energy.
+     * To avoid bugs, we always re-initialize the simple tables here.
+     */
+    init_interaction_const_tables(NULL, ic, bUsesSimpleTables, rtab);
 
     if (cr->duty & DUTY_PME)
     {
@@ -665,7 +737,7 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
              * copying part of the old pointers.
              */
             gmx_pme_reinit(&set->pmedata,
-                           cr,pme_lb->setup[0].pmedata,ir,
+                           cr, pme_lb->setup[0].pmedata, ir,
                            set->grid);
         }
         *pmedata = set->pmedata;
@@ -673,17 +745,17 @@ gmx_bool pme_load_balance(pme_load_balancing_t pme_lb,
     else
     {
         /* Tell our PME-only node to switch grid */
-        gmx_pme_send_switch(cr, set->grid, set->ewaldcoeff);
+        gmx_pme_send_switchgrid(cr, set->grid, set->ewaldcoeff_q, set->ewaldcoeff_lj);
     }
 
     if (debug)
     {
-        print_grid(NULL,debug,"","switched to",set,-1);
+        print_grid(NULL, debug, "", "switched to", set, -1);
     }
 
     if (pme_lb->stage == pme_lb->nstage)
     {
-        print_grid(fp_err,fp_log,"","optimal",set,-1);
+        print_grid(fp_err, fp_log, "", "optimal", set, -1);
     }
 
     return TRUE;
@@ -699,59 +771,90 @@ static int pme_grid_points(const pme_setup_t *setup)
     return setup->grid[XX]*setup->grid[YY]*setup->grid[ZZ];
 }
 
-static void print_pme_loadbal_setting(FILE *fplog,
-                                     char *name,
-                                     const pme_setup_t *setup)
+static real pme_loadbal_rlist(const pme_setup_t *setup)
+{
+    /* With the group cut-off scheme we can have twin-range either
+     * for Coulomb or for VdW, so we need a check here.
+     * With the Verlet cut-off scheme rlist=rlistlong.
+     */
+    if (setup->rcut_coulomb > setup->rlist)
+    {
+        return setup->rlistlong;
+    }
+    else
+    {
+        return setup->rlist;
+    }
+}
+
+static void print_pme_loadbal_setting(FILE              *fplog,
+                                      char              *name,
+                                      const pme_setup_t *setup)
 {
     fprintf(fplog,
             "   %-7s %6.3f nm %6.3f nm     %3d %3d %3d   %5.3f nm  %5.3f nm\n",
             name,
-            setup->rcut_coulomb,setup->rlist,
-            setup->grid[XX],setup->grid[YY],setup->grid[ZZ],
-            setup->spacing,1/setup->ewaldcoeff);
+            setup->rcut_coulomb, pme_loadbal_rlist(setup),
+            setup->grid[XX], setup->grid[YY], setup->grid[ZZ],
+            setup->spacing, 1/setup->ewaldcoeff_q);
 }
 
 static void print_pme_loadbal_settings(pme_load_balancing_t pme_lb,
-                                       FILE *fplog)
+                                       t_commrec           *cr,
+                                       FILE                *fplog,
+                                       gmx_bool             bNonBondedOnGPU)
 {
-    double pp_ratio,grid_ratio;
+    double pp_ratio, grid_ratio;
 
-    pp_ratio   = pow(pme_lb->setup[pme_lb->cur].rlist/pme_lb->setup[0].rlistlong,3.0);
+    pp_ratio   = pow(pme_loadbal_rlist(&pme_lb->setup[pme_lb->cur])/pme_loadbal_rlist(&pme_lb->setup[0]), 3.0);
     grid_ratio = pme_grid_points(&pme_lb->setup[pme_lb->cur])/
         (double)pme_grid_points(&pme_lb->setup[0]);
 
-    fprintf(fplog,"\n");
-    fprintf(fplog,"       P P   -   P M E   L O A D   B A L A N C I N G\n");
-    fprintf(fplog,"\n");
+    fprintf(fplog, "\n");
+    fprintf(fplog, "       P P   -   P M E   L O A D   B A L A N C I N G\n");
+    fprintf(fplog, "\n");
     /* Here we only warn when the optimal setting is the last one */
     if (pme_lb->elimited != epmelblimNO &&
         pme_lb->cur == pme_loadbal_end(pme_lb)-1)
     {
-        fprintf(fplog," NOTE: The PP/PME load balancing was limited by the %s,\n",
+        fprintf(fplog, " NOTE: The PP/PME load balancing was limited by the %s,\n",
                 pmelblim_str[pme_lb->elimited]);
-        fprintf(fplog,"       you might not have reached a good load balance.\n");
+        fprintf(fplog, "       you might not have reached a good load balance.\n");
         if (pme_lb->elimited == epmelblimDD)
         {
-            fprintf(fplog,"       Try different mdrun -dd settings or lower the -dds value.\n");
+            fprintf(fplog, "       Try different mdrun -dd settings or lower the -dds value.\n");
         }
-        fprintf(fplog,"\n");
-    }
-    fprintf(fplog," PP/PME load balancing changed the cut-off and PME settings:\n");
-    fprintf(fplog,"           particle-particle                    PME\n");
-    fprintf(fplog,"            rcoulomb  rlist            grid      spacing   1/beta\n");
-    print_pme_loadbal_setting(fplog,"initial",&pme_lb->setup[0]);
-    print_pme_loadbal_setting(fplog,"final"  ,&pme_lb->setup[pme_lb->cur]);
-    fprintf(fplog," cost-ratio           %4.2f             %4.2f\n",
-            pp_ratio,grid_ratio);
-    fprintf(fplog," (note that these numbers concern only part of the total PP and PME load)\n");
-    fprintf(fplog,"\n");
+        fprintf(fplog, "\n");
+    }
+    fprintf(fplog, " PP/PME load balancing changed the cut-off and PME settings:\n");
+    fprintf(fplog, "           particle-particle                    PME\n");
+    fprintf(fplog, "            rcoulomb  rlist            grid      spacing   1/beta\n");
+    print_pme_loadbal_setting(fplog, "initial", &pme_lb->setup[0]);
+    print_pme_loadbal_setting(fplog, "final", &pme_lb->setup[pme_lb->cur]);
+    fprintf(fplog, " cost-ratio           %4.2f             %4.2f\n",
+            pp_ratio, grid_ratio);
+    fprintf(fplog, " (note that these numbers concern only part of the total PP and PME load)\n");
+
+    if (pp_ratio > 1.5 && !bNonBondedOnGPU)
+    {
+        md_print_warn(cr, fplog,
+                      "NOTE: PME load balancing increased the non-bonded workload by more than 50%%.\n"
+                      "      For better performance, use (more) PME ranks (mdrun -npme),\n"
+                      "      or if you are beyond the scaling limit, use fewer total ranks (or nodes).\n");
+    }
+    else
+    {
+        fprintf(fplog, "\n");
+    }
 }
 
-void pme_loadbal_done(pme_load_balancing_t pme_lb, FILE *fplog)
+void pme_loadbal_done(pme_load_balancing_t pme_lb,
+                      t_commrec *cr, FILE *fplog,
+                      gmx_bool bNonBondedOnGPU)
 {
     if (fplog != NULL && (pme_lb->cur > 0 || pme_lb->elimited != epmelblimNO))
     {
-        print_pme_loadbal_settings(pme_lb,fplog);
+        print_pme_loadbal_settings(pme_lb, cr, fplog, bNonBondedOnGPU);
     }
 
     /* TODO: Here we should free all pointers in pme_lb,