Merge release-5-0 into master
authorRoland Schulz <roland@utk.edu>
Fri, 5 Jun 2015 18:15:33 +0000 (14:15 -0400)
committerRoland Schulz <roland@utk.edu>
Fri, 5 Jun 2015 18:15:42 +0000 (14:15 -0400)
Conflicts:
src/gromacs/gmxlib/rbin.c
(added also ICC16)
src/gromacs/mdlib/tpi.cpp
(gmx_simd_check_and_reset_overflow was removed already
in master branch, so no changes needed here)
        src/programs/mdrun/runner.cpp
(adjacent additions)

Change-Id: I85d92bce71ebeb6e38ac63bb61c3931d5bb54c16

1  2 
src/gromacs/gmxana/gmx_genion.c
src/gromacs/gmxlib/rbin.c
src/gromacs/gmxpreprocess/solvate.cpp
src/programs/mdrun/membed.c
src/programs/mdrun/runner.cpp

index 4db8d1ceabe5c6781d894b62437e2a3b0cfebd0a,6b03ea738b616fea6f89d93ebadff13593efc0f2..2790a4213f8243f70129ec48b79f60cd0d63b73d
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "gmxpre.h"
  
  #include <ctype.h>
 +#include <stdlib.h>
  #include <string.h>
  
 -#include "gromacs/utility/cstringutil.h"
 -#include "gromacs/utility/smalloc.h"
 -#include "sysstuff.h"
 -#include "gromacs/fileio/confio.h"
  #include "gromacs/commandline/pargs.h"
 -#include "pbc.h"
 -#include "force.h"
 -#include "gmx_fatal.h"
 -#include "gromacs/fileio/futil.h"
 -#include "gromacs/math/utilities.h"
 -#include "macros.h"
 -#include "vec.h"
 +#include "gromacs/fileio/confio.h"
  #include "gromacs/fileio/tpxio.h"
 -#include "mdrun.h"
 -#include "main.h"
 +#include "gromacs/gmxana/gmx_ana.h"
 +#include "gromacs/legacyheaders/force.h"
 +#include "gromacs/legacyheaders/macros.h"
 +#include "gromacs/legacyheaders/mdrun.h"
 +#include "gromacs/math/utilities.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/pbcutil/pbc.h"
  #include "gromacs/random/random.h"
 -#include "index.h"
 -#include "mtop_util.h"
 -#include "gmx_ana.h"
 +#include "gromacs/topology/index.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/futil.h"
 +#include "gromacs/utility/smalloc.h"
  
  static void insert_ion(int nsa, int *nwater,
                         gmx_bool bSet[], int repl[], atom_id index[],
@@@ -122,7 -126,7 +122,7 @@@ static char *aname(const char *mname
      char *str;
      int   i;
  
 -    str = strdup(mname);
 +    str = gmx_strdup(mname);
      i   = strlen(str)-1;
      while (i > 1 && (isdigit(str[i]) || (str[i] == '+') || (str[i] == '-')))
      {
@@@ -176,14 -180,14 +176,14 @@@ void sort_ions(int nsa, int nw, int rep
          if (np)
          {
              snew(pptr, 1);
 -            pptr[0] = strdup(p_name);
 +            pptr[0] = gmx_strdup(p_name);
              snew(paptr, 1);
              paptr[0] = aname(p_name);
          }
          if (nn)
          {
              snew(nptr, 1);
 -            nptr[0] = strdup(n_name);
 +            nptr[0] = gmx_strdup(n_name);
              snew(naptr, 1);
              naptr[0] = aname(n_name);
          }
  static void update_topol(const char *topinout, int p_num, int n_num,
                           const char *p_name, const char *n_name, char *grpname)
  {
- #define TEMP_FILENM "temp.top"
      FILE    *fpin, *fpout;
      char     buf[STRLEN], buf2[STRLEN], *temp, **mol_line = NULL;
      int      line, i, nsol, nmol_line, sol_line, nsol_last;
      gmx_bool bMolecules;
+     char     temporary_filename[STRLEN];
  
      printf("\nProcessing topology\n");
      fpin  = gmx_ffopen(topinout, "r");
-     fpout = gmx_ffopen(TEMP_FILENM, "w");
+     strncpy(temporary_filename, "temp.topXXXXXX", STRLEN);
+     gmx_tmpnam(temporary_filename);
+     fpout = gmx_ffopen(temporary_filename, "w");
  
      line       = 0;
      bMolecules = FALSE;
              }
              /* Store this molecules section line */
              srenew(mol_line, nmol_line+1);
 -            mol_line[nmol_line] = strdup(buf);
 +            mol_line[nmol_line] = gmx_strdup(buf);
              nmol_line++;
          }
      }
      /* use gmx_ffopen to generate backup of topinout */
      fpout = gmx_ffopen(topinout, "w");
      gmx_ffclose(fpout);
-     rename(TEMP_FILENM, topinout);
- #undef TEMP_FILENM
+     rename(temporary_filename, topinout);
  }
  
  int gmx_genion(int argc, char *argv[])
          { "-rmin",  FALSE, etREAL, {&rmin},  "Minimum distance between ions" },
          { "-seed",  FALSE, etINT,  {&seed},  "Seed for random number generator" },
          { "-conc",  FALSE, etREAL, {&conc},
 -          "Specify salt concentration (mol/liter). This will add sufficient ions to reach up to the specified concentration as computed from the volume of the cell in the input [TT].tpr[tt] file. Overrides the [TT]-np[tt] and [TT]-nn[tt] options." },
 +          "Specify salt concentration (mol/liter). This will add sufficient ions to reach up to the specified concentration as computed from the volume of the cell in the input [REF].tpr[ref] file. Overrides the [TT]-np[tt] and [TT]-nn[tt] options." },
          { "-neutral", FALSE, etBOOL, {&bNeutral}, "This option will add enough ions to neutralize the system. These ions are added on top of those specified with [TT]-np[tt]/[TT]-nn[tt] or [TT]-conc[tt]. "}
      };
      t_topology         top;
      output_env_t       oenv;
      gmx_rng_t          rng;
      t_filenm           fnm[] = {
 -        { efTPX, NULL,  NULL,      ffREAD  },
 +        { efTPR, NULL,  NULL,      ffREAD  },
          { efNDX, NULL,  NULL,      ffOPTRD },
          { efSTO, "-o",  NULL,      ffWRITE },
          { efTOP, "-p",  "topol",   ffOPTRW }
      };
  #define NFILE asize(fnm)
  
 -    if (!parse_common_args(&argc, argv, PCA_BE_NICE, NFILE, fnm, asize(pa), pa,
 +    if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa,
                             asize(desc), desc, asize(bugs), bugs, &oenv))
      {
          return 0;
      }
  
      /* Read atom positions and charges */
 -    read_tps_conf(ftp2fn(efTPX, NFILE, fnm), title, &top, &ePBC, &x, &v, box, FALSE);
 +    read_tps_conf(ftp2fn(efTPR, NFILE, fnm), title, &top, &ePBC, &x, &v, box, FALSE);
      atoms = top.atoms;
  
      /* Compute total charge */
index 4b0596482c819ae73435dad5656ea08911eb5f0e,202392c365cd7cf599d6bf2b8597176fe4ca291d..3bd1e820beb1a58253349dd9ada86f0db97c3769
   * the research papers on the package. Check out http://www.gromacs.org.
   */
  /* This file is completely threadsafe - keep it that way! */
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "gmxpre.h"
  
 -#include "typedefs.h"
 -#include "main.h"
 -#include "network.h"
 -#include "rbin.h"
 +#include "gromacs/legacyheaders/rbin.h"
 +
 +#include "gromacs/legacyheaders/network.h"
 +#include "gromacs/legacyheaders/typedefs.h"
  #include "gromacs/utility/smalloc.h"
  
  t_bin *mk_bin(void)
@@@ -86,7 -88,7 +86,7 @@@ int add_binr(t_bin *b, int nr, real r[]
      /* Copy pointer */
      rbuf = b->rbuf+b->nreal;
  
- #if (__ICC == 1500 || __ICL == 1500) && defined __MIC__
+ #if (__ICC >= 1500 || __ICL >= 1500) && defined __MIC__
  #pragma novector /* Work-around for incorrect vectorization */
  #endif
      for (i = 0; (i < nr); i++)
index 137dd3e720d6cf98ed53684eaaa4763a5944282d,6cfe8ef86d82b7042104c52a2a09a7d7fc551326..76eace9657d4d77101d4940cde264aa58e1fcdbe
@@@ -3,7 -3,7 +3,7 @@@
   *
   * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
   * Copyright (c) 2001-2004, The GROMACS development team.
-  * Copyright (c) 2013,2014, by the GROMACS development team, led by
+  * Copyright (c) 2013,2014,2015, 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.
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -#include "solvate.h"
 +#include "gmxpre.h"
  
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "solvate.h"
  
  #include <string.h>
  
 -#include "sysstuff.h"
 -#include "typedefs.h"
 -#include "gromacs/utility/smalloc.h"
 -#include "gromacs/utility/cstringutil.h"
 -#include "gromacs/fileio/confio.h"
 -#include "macros.h"
 -#include "gromacs/fileio/futil.h"
 -#include "atomprop.h"
 -#include "names.h"
 -#include "vec.h"
 -#include "gmx_fatal.h"
 +#include <algorithm>
 +
  #include "gromacs/commandline/pargs.h"
 -#include "gromacs/gmxlib/conformation-utilities.h"
 -#include "addconf.h"
 -#include "read-conformation.h"
 +#include "gromacs/fileio/confio.h"
  #include "gromacs/fileio/pdbio.h"
 -#include "pbc.h"
 -
 -#ifdef DEBUG
 -static void print_stat(rvec *x, int natoms, matrix box)
 -{
 -    int  i, m;
 -    rvec xmin, xmax;
 -    for (m = 0; (m < DIM); m++)
 -    {
 -        xmin[m] = x[0][m];
 -        xmax[m] = x[0][m];
 -    }
 -    for (i = 0; (i < natoms); i++)
 -    {
 -        for (m = 0; m < DIM; m++)
 -        {
 -            xmin[m] = min(xmin[m], x[i][m]);
 -            xmax[m] = max(xmax[m], x[i][m]);
 -        }
 -    }
 -    for (m = 0; (m < DIM); m++)
 -    {
 -        fprintf(stderr, "DIM %d XMIN %8.3f XMAX %8.3f BOX %8.3f\n",
 -                m, xmin[m], xmax[m], box[m][m]);
 -    }
 -}
 -#endif
 +#include "gromacs/gmxlib/conformation-utilities.h"
 +#include "gromacs/gmxpreprocess/read-conformation.h"
 +#include "gromacs/legacyheaders/macros.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/pbcutil/pbc.h"
 +#include "gromacs/selection/nbsearch.h"
 +#include "gromacs/topology/atomprop.h"
 +#include "gromacs/topology/atoms.h"
 +#include "gromacs/topology/atomsbuilder.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/futil.h"
 +#include "gromacs/utility/gmxassert.h"
 +#include "gromacs/utility/smalloc.h"
  
  typedef struct {
      char *name;
@@@ -88,18 -114,16 +88,18 @@@ static void sort_molecule(t_atoms **ato
          if ( (i == 0) || (atoms->atom[i].resind != atoms->atom[i-1].resind) )
          {
              /* see if this was a molecule type we haven't had yet: */
 -            moltp = NOTSET;
 -            for (j = 0; (j < nrmoltypes) && (moltp == NOTSET); j++)
 +            moltp = -1;
 +            for (j = 0; (j < nrmoltypes) && (moltp == -1); j++)
              {
 -                if (strcmp(*(atoms->resinfo[atoms->atom[i].resind].name),
 -                           moltypes[j].name) == 0)
 +                /* cppcheck-suppress nullPointer
 +                 * moltypes is guaranteed to be allocated because otherwise
 +                 * nrmoltypes is 0. */
 +                if (strcmp(*(atoms->resinfo[atoms->atom[i].resind].name), moltypes[j].name) == 0)
                  {
                      moltp = j;
                  }
              }
 -            if (moltp == NOTSET)
 +            if (moltp == -1)
              {
                  moltp = nrmoltypes;
                  nrmoltypes++;
@@@ -281,428 -305,186 +281,427 @@@ static void rm_res_pbc(t_atoms *atoms, 
      }
  }
  
 -/* Make a new configuration by adding boxes*/
 -static void make_new_conformation(t_atoms *atoms, rvec *x, rvec *v, real *r, matrix box, ivec n_box)
 +/*! \brief
 + * Generates a solvent configuration of desired size by stacking solvent boxes.
 + *
 + * \param[in,out] atoms      Solvent atoms.
 + * \param[in,out] x          Solvent positions.
 + * \param[in,out] v          Solvent velocities (`*v` can be NULL).
 + * \param[in,out] r          Solvent exclusion radii.
 + * \param[in]     box        Initial solvent box.
 + * \param[in]     boxTarget  Target box size.
 + *
 + * The solvent box of desired size is created by stacking the initial box in
 + * the smallest k*l*m array that covers the box, and then removing any residue
 + * where all atoms are outside the target box (with a small margin).
 + * This function does not remove overlap between solvent atoms across the
 + * edges.
 + *
 + * Note that the input configuration should be in the rectangular unit cell and
 + * have whole residues.
 + */
 +static void replicateSolventBox(t_atoms *atoms, rvec **x, rvec **v, real **r,
 +                                const matrix box, const matrix boxTarget)
  {
 -    int     i, ix, iy, iz, m, j, imol, offset;
 -    rvec    delta;
 -    int     nmol;
 +    // Calculate the box multiplication factors.
 +    ivec n_box;
 +    int  nmol = 1;
 +    for (int i = 0; i < DIM; ++i)
 +    {
 +        n_box[i] = 1;
 +        while (n_box[i] * box[i][i] < boxTarget[i][i])
 +        {
 +            n_box[i]++;
 +        }
 +        nmol *= n_box[i];
 +    }
 +    fprintf(stderr, "Will generate new solvent configuration of %dx%dx%d boxes\n",
 +            n_box[XX], n_box[YY], n_box[ZZ]);
 +
 +    // Create arrays for storing the generated system (cannot be done in-place
 +    // in case the target box is smaller than the original in one dimension,
 +    // but not in all).
 +    t_atoms           newAtoms;
 +    init_t_atoms(&newAtoms, 0, FALSE);
 +    gmx::AtomsBuilder builder(&newAtoms);
 +    builder.reserve(atoms->nr * nmol, atoms->nres * nmol);
 +    rvec             *newX;
 +    rvec             *newV = NULL;
 +    real             *newR;
 +    snew(newX,              atoms->nr * nmol);
 +    snew(newR,              atoms->nr * nmol);
 +    if (*v)
 +    {
 +        snew(newV,          atoms->nr * nmol);
 +    }
  
 -    nmol = n_box[XX]*n_box[YY]*n_box[ZZ];
 +    const real maxRadius = *std::max_element(*r, *r + atoms->nr);
 +    rvec       boxWithMargin;
 +    for (int i = 0; i < DIM; ++i)
 +    {
 +        // The code below is only interested about the box diagonal.
 +        boxWithMargin[i] = boxTarget[i][i] + 3*maxRadius;
 +    }
  
 -    /*print message*/
 -    fprintf(stderr, "Generating configuration\n");
 -    imol = 0;
 -    for (ix = 0; (ix < n_box[XX]); ix++)
 +    for (int ix = 0; ix < n_box[XX]; ++ix)
      {
 +        rvec delta;
          delta[XX] = ix*box[XX][XX];
 -        for (iy = 0; (iy < n_box[YY]); iy++)
 +        for (int iy = 0; iy < n_box[YY]; ++iy)
          {
              delta[YY] = iy*box[YY][YY];
 -            for (iz = 0; (iz < n_box[ZZ]); iz++)
 +            for (int iz = 0; iz < n_box[ZZ]; ++iz)
              {
                  delta[ZZ] = iz*box[ZZ][ZZ];
 -                offset    = imol*atoms->nr;
 -                for (i = 0; (i < atoms->nr); i++)
 +                bool bKeepResidue     = false;
 +                for (int i = 0; i < atoms->nr; ++i)
                  {
 -                    for (m = 0; (m < DIM); m++)
 +                    const int newIndex  = builder.currentAtomCount();
 +                    bool      bKeepAtom = true;
 +                    for (int m = 0; m < DIM; ++m)
                      {
 -                        x[offset+i][m] = delta[m]+x[i][m];
 +                        const real newCoord = delta[m] + (*x)[i][m];
 +                        bKeepAtom         = bKeepAtom && (newCoord < boxWithMargin[m]);
 +                        newX[newIndex][m] = newCoord;
                      }
 -                    if (v)
 +                    bKeepResidue = bKeepResidue || bKeepAtom;
 +                    if (newV)
                      {
 -                        for (m = 0; (m < DIM); m++)
 +                        copy_rvec((*v)[i], newV[newIndex]);
 +                    }
 +                    newR[newIndex] = (*r)[i];
 +                    builder.addAtom(*atoms, i);
 +                    if (i == atoms->nr - 1
 +                        || atoms->atom[i+1].resind != atoms->atom[i].resind)
 +                    {
 +                        if (bKeepResidue)
 +                        {
 +                            builder.finishResidue(atoms->resinfo[atoms->atom[i].resind]);
 +                        }
 +                        else
                          {
 -                            v[offset+i][m] = v[i][m];
 +                            builder.discardCurrentResidue();
                          }
 +                        // Reset state for the next residue.
 +                        bKeepResidue     = false;
                      }
 -                    r[offset+i] = r[i];
                  }
 -                imol++;
              }
          }
      }
 -    for (i = 1; (i < nmol); i++)
 +    sfree(atoms->atom);
 +    sfree(atoms->atomname);
 +    sfree(atoms->resinfo);
 +    atoms->nr       = newAtoms.nr;
 +    atoms->nres     = newAtoms.nres;
 +    atoms->atom     = newAtoms.atom;
 +    atoms->atomname = newAtoms.atomname;
 +    atoms->resinfo  = newAtoms.resinfo;
 +    sfree(*x);
 +    sfree(*v);
 +    sfree(*r);
 +    *x = newX;
 +    *v = newV;
 +    *r = newR;
 +    fprintf(stderr, "Solvent box contains %d atoms in %d residues\n",
 +            atoms->nr, atoms->nres);
 +}
 +
 +/*! \brief
 + * Removes overlap of solvent atoms across the edges.
 + *
 + * \param[in,out] atoms      Solvent atoms.
 + * \param[in,out] x          Solvent positions.
 + * \param[in,out] v          Solvent velocities (can be NULL).
 + * \param[in,out] r          Solvent exclusion radii.
 + * \param[in]     pbc        PBC information.
 + *
 + * Solvent residues that lay on the edges that do not touch the origin are
 + * removed if they overlap with other solvent atoms across the PBC.
 + * This is done in this way as the assumption is that the input solvent
 + * configuration is already equilibrated, and so does not contain any
 + * undesirable overlap.  The only overlap that should be removed is caused by
 + * cutting the box in half in replicateSolventBox() and leaving a margin of
 + * solvent outside those box edges; these atoms can then overlap with those on
 + * the opposite box edge in a way that is not part of the pre-equilibrated
 + * configuration.
 + */
 +static void removeSolventBoxOverlap(t_atoms *atoms, rvec *x, rvec *v, real *r,
 +                                    const t_pbc &pbc)
 +{
 +    gmx::AtomsRemover remover(*atoms);
 +
 +    // TODO: We could limit the amount of pairs searched significantly,
 +    // since we are only interested in pairs where the positions are on
 +    // opposite edges.
 +    const real maxRadius = *std::max_element(r, r + atoms->nr);
 +    gmx::AnalysisNeighborhood           nb;
 +    nb.setCutoff(2*maxRadius);
 +    gmx::AnalysisNeighborhoodPositions  pos(x, atoms->nr);
 +    gmx::AnalysisNeighborhoodSearch     search     = nb.initSearch(&pbc, pos);
 +    gmx::AnalysisNeighborhoodPairSearch pairSearch = search.startPairSearch(pos);
 +    gmx::AnalysisNeighborhoodPair       pair;
 +    while (pairSearch.findNextPair(&pair))
      {
 -        int offs    = i*atoms->nr;
 -        int offsres = i*atoms->nres;
 -        for (j = 0; (j < atoms->nr); j++)
 +        const int  i1 = pair.refIndex();
 +        const int  i2 = pair.testIndex();
 +        if (remover.isMarked(i2))
 +        {
 +            pairSearch.skipRemainingPairsForTestPosition();
 +            continue;
 +        }
 +        if (remover.isMarked(i1) || atoms->atom[i1].resind == atoms->atom[i2].resind)
          {
 -            atoms->atomname[offs+j]                    = atoms->atomname[j];
 -            atoms->atom[offs+j].resind                 = atoms->atom[j].resind + offsres;
 -            atoms->resinfo[atoms->atom[offs+j].resind] =
 -                atoms->resinfo[atoms->atom[j].resind];
 -            atoms->resinfo[atoms->atom[offs+j].resind].nr += offsres;
 +            continue;
          }
 +        if (pair.distance2() < sqr(r[i1] + r[i2]))
 +        {
 +            rvec dx;
 +            rvec_sub(x[i2], x[i1], dx);
 +            bool bCandidate1 = false, bCandidate2 = false;
 +            // To satisfy Clang static analyzer.
 +            GMX_ASSERT(pbc.ndim_ePBC <= DIM, "Too many periodic dimensions");
 +            for (int d = 0; d < pbc.ndim_ePBC; ++d)
 +            {
 +                // If the distance in some dimension is larger than the
 +                // cutoff, then it means that the distance has been computed
 +                // over the PBC.  Mark the position with a larger coordinate
 +                // for potential removal.
 +                if (dx[d] > maxRadius)
 +                {
 +                    bCandidate2 = true;
 +                }
 +                else if (dx[d] < -maxRadius)
 +                {
 +                    bCandidate1 = true;
 +                }
 +            }
 +            // Only mark one of the positions for removal if both were
 +            // candidates.
 +            if (bCandidate2 && (!bCandidate1 || i2 > i1))
 +            {
 +                remover.markResidue(*atoms, i2, true);
 +                pairSearch.skipRemainingPairsForTestPosition();
 +            }
 +            else if (bCandidate1)
 +            {
 +                remover.markResidue(*atoms, i1, true);
 +            }
 +        }
 +    }
 +
 +    remover.removeMarkedVectors(x);
 +    if (v != NULL)
 +    {
 +        remover.removeMarkedVectors(v);
 +    }
 +    remover.removeMarkedValues(r);
 +    const int originalAtomCount = atoms->nr;
 +    remover.removeMarkedAtoms(atoms);
 +    fprintf(stderr, "Removed %d solvent atoms due to solvent-solvent overlap\n",
 +            originalAtomCount - atoms->nr);
 +}
 +
 +/*! \brief
 + * Removes solvent molecules that overlap with the solute, and optionally also
 + * those that are outside a given shell radius from the solute.
 + *
 + * \param[in,out] atoms      Solvent atoms.
 + * \param[in,out] x          Solvent positions.
 + * \param[in,out] v          Solvent velocities (can be NULL).
 + * \param[in,out] r          Solvent exclusion radii.
 + * \param[in]     pbc        PBC information.
 + * \param[in]     soluteAtomCount Number of solute atoms.
 + * \param[in]     x_solute   Solute positions.
 + * \param[in]     r_solute   Solute exclusion radii.
 + * \param[in]     rshell     If >0, only keep solvent atoms within a shell of
 + *     this size from the solute.
 + */
 +static void removeSoluteOverlap(t_atoms *atoms, rvec *x, rvec *v, real *r,
 +                                const t_pbc &pbc, int soluteAtomCount,
 +                                const rvec *x_solute, const real *r_solute,
 +                                real rshell)
 +{
 +    const real                          maxRadius1
 +        = *std::max_element(r, r + atoms->nr);
 +    const real                          maxRadius2
 +        = *std::max_element(r_solute, r_solute + soluteAtomCount);
 +
 +    gmx::AtomsRemover                   remover(*atoms);
 +    // If rshell is >0, the neighborhood search looks at all pairs
 +    // within rshell, and unmarks those that are within the cutoff.
 +    // This line marks everything, so that solvent outside rshell remains
 +    // marked after the loop.
 +    // Without rshell, the neighborhood search only marks the overlapping
 +    // solvent atoms, and all others are left alone.
 +    if (rshell > 0.0)
 +    {
 +        remover.markAll();
 +    }
 +
 +    gmx::AnalysisNeighborhood           nb;
 +    nb.setCutoff(std::max(maxRadius1 + maxRadius2, rshell));
 +    gmx::AnalysisNeighborhoodPositions  posSolute(x_solute, soluteAtomCount);
 +    gmx::AnalysisNeighborhoodSearch     search     = nb.initSearch(&pbc, posSolute);
 +    gmx::AnalysisNeighborhoodPositions  pos(x, atoms->nr);
 +    gmx::AnalysisNeighborhoodPairSearch pairSearch = search.startPairSearch(pos);
 +    gmx::AnalysisNeighborhoodPair       pair;
 +    while (pairSearch.findNextPair(&pair))
 +    {
 +        if (remover.isMarked(pair.testIndex()))
 +        {
 +            pairSearch.skipRemainingPairsForTestPosition();
 +            continue;
 +        }
 +        const real r1      = r_solute[pair.refIndex()];
 +        const real r2      = r[pair.testIndex()];
 +        const bool bRemove = (pair.distance2() < sqr(r1 + r2));
 +        remover.markResidue(*atoms, pair.testIndex(), bRemove);
      }
 -    atoms->nr   *= nmol;
 -    atoms->nres *= nmol;
 -    for (i = 0; i < DIM; i++)
 +
 +    remover.removeMarkedVectors(x);
 +    if (v != NULL)
 +    {
 +        remover.removeMarkedVectors(v);
 +    }
 +    remover.removeMarkedValues(r);
 +    const int originalAtomCount = atoms->nr;
 +    remover.removeMarkedAtoms(atoms);
 +    fprintf(stderr, "Removed %d solvent atoms due to solute-solvent overlap\n",
 +            originalAtomCount - atoms->nr);
 +}
 +
 +/*! \brief
 + * Removes a given number of solvent residues.
 + *
 + * \param[in,out] atoms           Solvent atoms.
 + * \param[in,out] x               Solvent positions.
 + * \param[in,out] v               Solvent velocities (can be NULL).
 + * \param[in]     numberToRemove  Number of residues to remove.
 + *
 + * This function is called last in the process of creating the solvent box,
 + * so it does not operate on the exclusion radii, as no code after this needs
 + * them.
 + */
 +static void removeExtraSolventMolecules(t_atoms *atoms, rvec *x, rvec *v,
 +                                        int numberToRemove)
 +{
 +    gmx::AtomsRemover remover(*atoms);
 +    // TODO: It might be nicer to remove a random set of residues, but
 +    // in practice this should give a roughly uniform spatial distribution.
 +    const int stride = atoms->nr / numberToRemove;
 +    for (int i = 0; i < numberToRemove; ++i)
      {
 -        for (j = 0; j < DIM; j++)
 +        int atomIndex = (i+1)*stride - 1;
 +        while (remover.isMarked(atomIndex))
          {
 -            box[j][i] *= n_box[j];
 +            ++atomIndex;
 +            if (atomIndex == atoms->nr)
 +            {
 +                atomIndex = 0;
 +            }
          }
 +        remover.markResidue(*atoms, atomIndex, true);
 +    }
 +    remover.removeMarkedVectors(x);
 +    if (v != NULL)
 +    {
 +        remover.removeMarkedVectors(v);
      }
 +    remover.removeMarkedAtoms(atoms);
  }
  
 -static void add_solv(const char *fn, t_atoms *atoms, rvec **x, rvec **v, real **exclusionDistances,
 -                     int ePBC, matrix box,
 -                     gmx_atomprop_t aps,
 -                     real defaultDistance, real scaleFactor, int *atoms_added,
 -                     int *residues_added, real rshell, int max_sol,
 -                     const output_env_t oenv)
 +static void add_solv(const char *fn, t_atoms *atoms, rvec **x, rvec **v,
 +                     int ePBC, matrix box, gmx_atomprop_t aps,
 +                     real defaultDistance, real scaleFactor,
 +                     real rshell, int max_sol)
  {
 -    int      i, nmol;
 -    ivec     n_box;
 -    char     filename[STRLEN];
 -    char     title_solvt[STRLEN];
      t_atoms *atoms_solvt;
      rvec    *x_solvt, *v_solvt = NULL;
 -    real    *exclusionDistances_solvt;
      int      ePBC_solvt;
      matrix   box_solvt;
 -    int      onr, onres;
 -    char    *lfn;
  
 -    lfn = gmxlibfn(fn);
 -    strncpy(filename, lfn, STRLEN);
 -    sfree(lfn);
 +    char    *filename = gmxlibfn(fn);
 +    snew(atoms_solvt, 1);
 +    char    *title_solvt
 +        = readConformation(filename, atoms_solvt, &x_solvt, &v_solvt,
 +                           &ePBC_solvt, box_solvt, "solvent");
 +    sfree(title_solvt);
 +    if (0 == atoms_solvt->nr)
      {
 -        int natoms;
 -        get_stx_coordnum(filename, &natoms);
 -        if (0 == natoms)
 -        {
 -            gmx_fatal(FARGS, "No solvent in %s, please check your input\n", filename);
 -        }
 -        snew(atoms_solvt, 1);
 -        init_t_atoms(atoms_solvt, natoms, FALSE);
 -    }
 -    snew(x_solvt, atoms_solvt->nr);
 -    if (v)
 -    {
 -        snew(v_solvt, atoms_solvt->nr);
 -    }
 -    snew(exclusionDistances_solvt, atoms_solvt->nr);
 -    snew(atoms_solvt->resinfo, atoms_solvt->nr);
 -    snew(atoms_solvt->atomname, atoms_solvt->nr);
 -    snew(atoms_solvt->atom, atoms_solvt->nr);
 -    atoms_solvt->pdbinfo = NULL;
 -    fprintf(stderr, "Reading solvent configuration%s\n",
 -            v_solvt ? " and velocities" : "");
 -    read_stx_conf(filename, title_solvt, atoms_solvt, x_solvt, v_solvt,
 -                  &ePBC_solvt, box_solvt);
 -    fprintf(stderr, "\"%s\"\n", title_solvt);
 -    fprintf(stderr, "solvent configuration contains %d atoms in %d residues\n",
 -            atoms_solvt->nr, atoms_solvt->nres);
 +        gmx_fatal(FARGS, "No solvent in %s, please check your input\n", filename);
 +    }
 +    sfree(filename);
      fprintf(stderr, "\n");
  
      /* apply pbc for solvent configuration for whole molecules */
      rm_res_pbc(atoms_solvt, x_solvt, box_solvt);
  
      /* initialise distance arrays for solvent configuration */
 -    exclusionDistances_solvt = makeExclusionDistances(atoms_solvt, aps, defaultDistance, scaleFactor);
 +    fprintf(stderr, "Initialising inter-atomic distances...\n");
 +    real *exclusionDistances
 +        = makeExclusionDistances(atoms, aps, defaultDistance, scaleFactor);
 +    real *exclusionDistances_solvt
 +        = makeExclusionDistances(atoms_solvt, aps, defaultDistance, scaleFactor);
  
 -    /* calculate the box multiplication factors n_box[0...DIM] */
 -    nmol = 1;
 -    for (i = 0; (i < DIM); i++)
 +    /* generate a new solvent configuration */
 +    fprintf(stderr, "Generating solvent configuration\n");
 +    t_pbc pbc;
 +    set_pbc(&pbc, ePBC, box);
 +    replicateSolventBox(atoms_solvt, &x_solvt, &v_solvt, &exclusionDistances_solvt,
 +                        box_solvt, box);
 +    if (ePBC != epbcNONE)
      {
 -        n_box[i] = 1;
 -        while (n_box[i]*box_solvt[i][i] < box[i][i])
 -        {
 -            n_box[i]++;
 -        }
 -        nmol *= n_box[i];
 +        removeSolventBoxOverlap(atoms_solvt, x_solvt, v_solvt, exclusionDistances_solvt, pbc);
      }
 -    fprintf(stderr, "Will generate new solvent configuration of %dx%dx%d boxes\n",
 -            n_box[XX], n_box[YY], n_box[ZZ]);
 -
 -    /* realloc atoms_solvt for the new solvent configuration */
 -    srenew(atoms_solvt->resinfo, atoms_solvt->nres*nmol);
 -    srenew(atoms_solvt->atomname, atoms_solvt->nr*nmol);
 -    srenew(atoms_solvt->atom, atoms_solvt->nr*nmol);
 -    srenew(x_solvt, atoms_solvt->nr*nmol);
 -    if (v_solvt)
 +    if (atoms->nr > 0)
      {
 -        srenew(v_solvt, atoms_solvt->nr*nmol);
 +        removeSoluteOverlap(atoms_solvt, x_solvt, v_solvt, exclusionDistances_solvt, pbc,
 +                            atoms->nr, *x, exclusionDistances, rshell);
      }
 -    srenew(exclusionDistances_solvt, atoms_solvt->nr*nmol);
 -
 -    /* generate a new solvent configuration */
 -    make_new_conformation(atoms_solvt, x_solvt, v_solvt, exclusionDistances_solvt, box_solvt, n_box);
  
 -#ifdef DEBUG
 -    print_stat(x_solvt, atoms_solvt->nr, box_solvt);
 -#endif
 +    if (max_sol > 0 && atoms_solvt->nres > max_sol)
 +    {
 +        const int numberToRemove = atoms_solvt->nres - max_sol;
 +        removeExtraSolventMolecules(atoms_solvt, x_solvt, v_solvt, numberToRemove);
 +    }
  
 -#ifdef DEBUG
 -    print_stat(x_solvt, atoms_solvt->nr, box_solvt);
 -#endif
      /* Sort the solvent mixture, not the protein... */
      sort_molecule(&atoms_solvt, x_solvt, v_solvt, exclusionDistances_solvt);
  
 -    /* add the two configurations */
 -    onr   = atoms->nr;
 -    onres = atoms->nres;
 -    add_conf(atoms, x, v, exclusionDistances, TRUE, ePBC, box, FALSE,
 -             atoms_solvt, x_solvt, v_solvt, exclusionDistances_solvt, TRUE, rshell, max_sol, oenv);
 -    *atoms_added    = atoms->nr-onr;
 -    *residues_added = atoms->nres-onres;
 +    // Merge the two configurations.
 +    srenew(*x, atoms->nr + atoms_solvt->nr);
 +    if (v != NULL)
 +    {
 +        srenew(*v, atoms->nr + atoms_solvt->nr);
 +    }
 +    for (int i = 0; i < atoms_solvt->nr; ++i)
 +    {
 +        const int index = atoms->nr + i;
 +        copy_rvec(x_solvt[i], (*x)[index]);
 +        if (v != NULL)
 +        {
 +            copy_rvec(v_solvt[i], (*v)[index]);
 +        }
 +    }
 +    {
 +        gmx::AtomsBuilder builder(atoms);
 +        builder.mergeAtoms(*atoms_solvt);
 +    }
 +    fprintf(stderr, "Generated solvent containing %d atoms in %d residues\n",
 +            atoms_solvt->nr, atoms_solvt->nres);
  
      sfree(x_solvt);
 +    sfree(v_solvt);
 +    sfree(exclusionDistances);
      sfree(exclusionDistances_solvt);
      done_atom(atoms_solvt);
      sfree(atoms_solvt);
 -
 -    fprintf(stderr, "Generated solvent containing %d atoms in %d residues\n",
 -            *atoms_added, *residues_added);
  }
  
  static void update_top(t_atoms *atoms, matrix box, int NFILE, t_filenm fnm[],
                         gmx_atomprop_t aps)
  {
- #define TEMP_FILENM "temp.top"
      FILE       *fpin, *fpout;
      char        buf[STRLEN], buf2[STRLEN], *temp;
      const char *topinout;
      topinout  = ftp2fn(efTOP, NFILE, fnm);
      if (ftp2bSet(efTOP, NFILE, fnm) )
      {
+         char temporary_filename[STRLEN];
+         strncpy(temporary_filename, "temp.topXXXXXX", STRLEN);
          fprintf(stderr, "Processing topology\n");
          fpin    = gmx_ffopen(topinout, "r");
-         fpout   = gmx_ffopen(TEMP_FILENM, "w");
+         gmx_tmpnam(temporary_filename);
+         fpout   = gmx_ffopen(temporary_filename, "w");
          line    = 0;
          bSystem = bMolecules = FALSE;
          while (fgets(buf, STRLEN, fpin))
          /* use gmx_ffopen to generate backup of topinout */
          fpout = gmx_ffopen(topinout, "w");
          gmx_ffclose(fpout);
-         rename(TEMP_FILENM, topinout);
+         rename(temporary_filename, topinout);
      }
- #undef TEMP_FILENM
  }
  
  int gmx_solvate(int argc, char *argv[])
          "into the box. This can create a void that can cause problems later.",
          "Choose your volume wisely.[PAR]",
  
 -        "The program can optionally rotate the solute molecule to align the",
 -        "longest molecule axis along a box edge. This way the amount of solvent",
 -        "molecules necessary is reduced.",
 -        "It should be kept in mind that this only works for",
 -        "short simulations, as e.g. an alpha-helical peptide in solution can ",
 -        "rotate over 90 degrees, within 500 ps. In general it is therefore ",
 -        "better to make a more or less cubic box.[PAR]",
 -
          "Setting [TT]-shell[tt] larger than zero will place a layer of water of",
          "the specified thickness (nm) around the solute. Hint: it is a good",
          "idea to put the protein in the center of a box first (using [gmx-editconf]).",
      /* parameter data */
      gmx_bool       bProt, bBox;
      const char    *conf_prot, *confout;
 -    real          *exclusionDistances = NULL;
      gmx_atomprop_t aps;
  
      /* protein configuration data */
      int      ePBC = -1;
      matrix   box;
  
 -    /* other data types */
 -    int      atoms_added, residues_added;
 -
      t_filenm fnm[] = {
          { efSTX, "-cp", "protein", ffOPTRD },
          { efSTX, "-cs", "spc216",  ffLIBRD},
            "Keep velocities from input solute and solvent" },
      };
  
 -    if (!parse_common_args(&argc, argv, PCA_BE_NICE, NFILE, fnm, asize(pa), pa,
 +    if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa,
                             asize(desc), desc, asize(bugs), bugs, &oenv))
      {
          return 0;
          /* Generate a solute configuration */
          conf_prot = opt2fn("-cp", NFILE, fnm);
          title     = readConformation(conf_prot, atoms, &x,
 -                                     bReadV ? &v : NULL, &ePBC, box);
 -        exclusionDistances = makeExclusionDistances(atoms, aps, defaultDistance, scaleFactor);
 -
 +                                     bReadV ? &v : NULL, &ePBC, box, "solute");
          if (bReadV && !v)
          {
              fprintf(stderr, "Note: no velocities found\n");
                    "or give explicit -box command line option");
      }
  
 -    add_solv(solventFileName, atoms, &x, v ? &v : NULL, &exclusionDistances, ePBC, box,
 -             aps, defaultDistance, scaleFactor, &atoms_added, &residues_added, r_shell, max_sol,
 -             oenv);
 +    add_solv(solventFileName, atoms, &x, v ? &v : NULL, ePBC, box,
 +             aps, defaultDistance, scaleFactor, r_shell, max_sol);
  
      /* write new configuration 1 to file confout */
      confout = ftp2fn(efSTO, NFILE, fnm);
      if (bProt)
      {
          write_sto_conf(confout, title, atoms, x, v, ePBC, box);
 -        /* print box sizes and box type to stderr */
 -        fprintf(stderr, "%s\n", title);
      }
      else
      {
      update_top(atoms, box, NFILE, fnm, aps);
  
      gmx_atomprop_destroy(aps);
 -    sfree(exclusionDistances);
      sfree(x);
      sfree(v);
      done_atom(atoms);
      sfree(atoms);
 +    sfree(title);
 +    output_env_done(oenv);
 +    done_filenms(NFILE, fnm);
  
      return 0;
  }
index 6e68fdcf71dad90773f1c19a5421818b4a166e6e,326de5f9696aeb1010389bd1543067e9c2f8fc7f..fae5d4ddc3b9f919dc8df0b17c9821d91d387766
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "gmxpre.h"
 +
 +#include "membed.h"
  
  #include <signal.h>
  #include <stdlib.h>
 -#include "typedefs.h"
 -#include "types/commrec.h"
 -#include "gromacs/utility/smalloc.h"
 -#include "sysstuff.h"
 -#include "vec.h"
 -#include "macros.h"
 -#include "main.h"
 -#include "gromacs/fileio/futil.h"
 +
  #include "gromacs/essentialdynamics/edsam.h"
 -#include "index.h"
 -#include "physics.h"
 -#include "names.h"
 -#include "mtop_util.h"
  #include "gromacs/fileio/tpxio.h"
 +#include "gromacs/legacyheaders/macros.h"
 +#include "gromacs/legacyheaders/names.h"
 +#include "gromacs/legacyheaders/readinp.h"
 +#include "gromacs/legacyheaders/typedefs.h"
 +#include "gromacs/legacyheaders/types/commrec.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/pbcutil/pbc.h"
 +#include "gromacs/topology/index.h"
 +#include "gromacs/topology/mtop_util.h"
  #include "gromacs/utility/cstringutil.h"
 -#include "membed.h"
 -#include "pbc.h"
 -#include "readinp.h"
 -#include "gromacs/gmxpreprocess/readir.h"
 +#include "gromacs/utility/futil.h"
 +#include "gromacs/utility/smalloc.h"
  
  /* information about scaling center */
  typedef struct {
@@@ -886,14 -890,16 +886,16 @@@ int rm_bonded(t_block *ins_at, gmx_mtop
  /* Write a topology where the number of molecules is correct for the system after embedding */
  static void top_update(const char *topfile, rm_t *rm_p, gmx_mtop_t *mtop)
  {
- #define TEMP_FILENM "temp.top"
-     int        bMolecules = 0;
+     int        bMolecules         = 0;
      FILE      *fpin, *fpout;
      char       buf[STRLEN], buf2[STRLEN], *temp;
      int        i, *nmol_rm, nmol, line;
+     char       temporary_filename[STRLEN];
  
      fpin  = gmx_ffopen(topfile, "r");
-     fpout = gmx_ffopen(TEMP_FILENM, "w");
+     strncpy(temporary_filename, "temp.topXXXXXX", STRLEN);
+     gmx_tmpnam(temporary_filename);
+     fpout = gmx_ffopen(temporary_filename, "w");
  
      snew(nmol_rm, mtop->nmoltype);
      for (i = 0; i < rm_p->nr; i++)
      /* use gmx_ffopen to generate backup of topinout */
      fpout = gmx_ffopen(topfile, "w");
      gmx_ffclose(fpout);
-     rename(TEMP_FILENM, topfile);
- #undef TEMP_FILENM
+     rename(temporary_filename, topfile);
  }
  
  void rescale_membed(int step_rel, gmx_membed_t membed, rvec *x)
      resize(membed->r_ins, x, membed->pos_ins, membed->fac);
  }
  
 +/* We would like gn to be const as well, but C doesn't allow this */
 +/* TODO this is utility functionality (search for the index of a
 +   string in a collection), so should be refactored and located more
 +   centrally. Also, it nearly duplicates the same string in readir.c */
 +static int search_string(const char *s, int ng, char *gn[])
 +{
 +    int i;
 +
 +    for (i = 0; (i < ng); i++)
 +    {
 +        if (gmx_strcasecmp(s, gn[i]) == 0)
 +        {
 +            return i;
 +        }
 +    }
 +
 +    gmx_fatal(FARGS,
 +              "Group %s selected for embedding was not found in the index file.\n"
 +              "Group names must match either [moleculetype] names or custom index group\n"
 +              "names, in which case you must supply an index file to the '-n' option\n"
 +              "of grompp.",
 +              s);
 +
 +    return -1;
 +}
 +
  gmx_membed_t init_membed(FILE *fplog, int nfile, const t_filenm fnm[], gmx_mtop_t *mtop,
                           t_inputrec *inputrec, t_state *state, t_commrec *cr, real *cpt)
  {
          get_input(membed_input, &xy_fac, &xy_max, &z_fac, &z_max, &it_xy, &it_z, &probe_rad, &low_up_rm,
                    &maxwarn, &pieces, &bALLOW_ASYMMETRY);
  
 -        tpr_version = get_tpr_version(ftp2fn(efTPX, nfile, fnm));
 +        tpr_version = get_tpr_version(ftp2fn(efTPR, nfile, fnm));
          if (tpr_version < membed_version)
          {
              gmx_fatal(FARGS, "Version of *.tpr file to old (%d). "
index ffa7a8df29037f2f94685d88b20eefad9d78b77b,744e9f501e301cede3bcaac6434dccf8d4eb2e13..8956d4442bedc77b0a0e49e6181e0be132a26fef
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +
 +#include "gmxpre.h"
 +
 +#include "config.h"
 +
 +#include <assert.h>
  #include <signal.h>
  #include <stdlib.h>
 -#ifdef HAVE_UNISTD_H
 -#include <unistd.h>
 -#endif
  #include <string.h>
 -#include <assert.h>
  
 -#include "typedefs.h"
 -#include "gromacs/utility/smalloc.h"
 -#include "sysstuff.h"
 -#include "copyrite.h"
 -#include "force.h"
 -#include "mdrun.h"
 -#include "md_logging.h"
 -#include "md_support.h"
 -#include "network.h"
 -#include "names.h"
 -#include "disre.h"
 -#include "orires.h"
 -#include "pme.h"
 -#include "mdatoms.h"
 -#include "repl_ex.h"
 -#include "deform.h"
 -#include "qmmm.h"
 -#include "domdec.h"
 -#include "coulomb.h"
 -#include "constr.h"
 -#include "mvdata.h"
 -#include "checkpoint.h"
 -#include "mtop_util.h"
 -#include "sighandler.h"
 -#include "txtdump.h"
 -#include "gmx_detect_hardware.h"
 -#include "gmx_omp_nthreads.h"
 -#include "gromacs/gmxpreprocess/calc_verletbuf.h"
 -#include "gmx_fatal_collective.h"
 -#include "membed.h"
 -#include "macros.h"
 -#include "gmx_thread_affinity.h"
 -#include "inputrec.h"
 +#include <algorithm>
  
 +#include "gromacs/domdec/domdec.h"
 +#include "gromacs/essentialdynamics/edsam.h"
 +#include "gromacs/ewald/pme.h"
  #include "gromacs/fileio/tpxio.h"
 -#include "gromacs/mdlib/nbnxn_search.h"
 +#include "gromacs/gmxlib/gpu_utils/gpu_utils.h"
 +#include "gromacs/legacyheaders/checkpoint.h"
 +#include "gromacs/legacyheaders/constr.h"
 +#include "gromacs/legacyheaders/disre.h"
 +#include "gromacs/legacyheaders/force.h"
 +#include "gromacs/legacyheaders/gmx_detect_hardware.h"
 +#include "gromacs/legacyheaders/gmx_omp_nthreads.h"
 +#include "gromacs/legacyheaders/gmx_thread_affinity.h"
 +#include "gromacs/legacyheaders/inputrec.h"
 +#include "gromacs/legacyheaders/main.h"
 +#include "gromacs/legacyheaders/md_logging.h"
 +#include "gromacs/legacyheaders/md_support.h"
 +#include "gromacs/legacyheaders/mdatoms.h"
 +#include "gromacs/legacyheaders/mdrun.h"
 +#include "gromacs/legacyheaders/names.h"
 +#include "gromacs/legacyheaders/network.h"
 +#include "gromacs/legacyheaders/oenv.h"
 +#include "gromacs/legacyheaders/orires.h"
 +#include "gromacs/legacyheaders/qmmm.h"
 +#include "gromacs/legacyheaders/sighandler.h"
 +#include "gromacs/legacyheaders/txtdump.h"
 +#include "gromacs/legacyheaders/typedefs.h"
 +#include "gromacs/math/calculate-ewald-splitting-coefficient.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/mdlib/calc_verletbuf.h"
  #include "gromacs/mdlib/nbnxn_consts.h"
 -#include "gromacs/timing/wallcycle.h"
 -#include "gromacs/utility/gmxmpi.h"
 -#include "gromacs/utility/gmxomp.h"
 -#include "gromacs/swap/swapcoords.h"
 -#include "gromacs/essentialdynamics/edsam.h"
 +#include "gromacs/mdlib/nbnxn_search.h"
 +#include "gromacs/pbcutil/pbc.h"
  #include "gromacs/pulling/pull.h"
  #include "gromacs/pulling/pull_rotation.h"
 +#include "gromacs/swap/swapcoords.h"
 +#include "gromacs/timing/wallcycle.h"
 +#include "gromacs/topology/mtop_util.h"
 +#include "gromacs/utility/gmxassert.h"
 +#include "gromacs/utility/gmxmpi.h"
 +#include "gromacs/utility/smalloc.h"
 +
 +#include "deform.h"
 +#include "membed.h"
 +#include "repl_ex.h"
  
  #ifdef GMX_FAHCORE
  #include "corewrap.h"
  #endif
  
 -#include "gpu_utils.h"
 -#include "nbnxn_cuda_data_mgmt.h"
 -
  typedef struct {
      gmx_integrator_t *func;
  } gmx_intp_t;
@@@ -146,6 -148,7 +146,6 @@@ struct mdrunner_arglis
      real            pforce;
      real            cpt_period;
      real            max_hours;
 -    const char     *deviceOptions;
      int             imdport;
      unsigned long   Flags;
  };
@@@ -182,7 -185,7 +182,7 @@@ static void mdrunner_start_fn(void *arg
               mc.nbpu_opt, mc.nstlist_cmdline,
               mc.nsteps_cmdline, mc.nstepout, mc.resetstep,
               mc.nmultisim, mc.repl_ex_nst, mc.repl_ex_nex, mc.repl_ex_seed, mc.pforce,
 -             mc.cpt_period, mc.max_hours, mc.deviceOptions, mc.imdport, mc.Flags);
 +             mc.cpt_period, mc.max_hours, mc.imdport, mc.Flags);
  }
  
  /* called by mdrunner() to start a specific number of threads (including
@@@ -201,7 -204,7 +201,7 @@@ static t_commrec *mdrunner_start_thread
                                           int nstepout, int resetstep,
                                           int nmultisim, int repl_ex_nst, int repl_ex_nex, int repl_ex_seed,
                                           real pforce, real cpt_period, real max_hours,
 -                                         const char *deviceOptions, unsigned long Flags)
 +                                         unsigned long Flags)
  {
      int                      ret;
      struct mdrunner_arglist *mda;
      mda->pforce          = pforce;
      mda->cpt_period      = cpt_period;
      mda->max_hours       = max_hours;
 -    mda->deviceOptions   = deviceOptions;
      mda->Flags           = Flags;
  
      /* now spawn new threads that start mdrunner_start_fn(), while
@@@ -291,7 -295,7 +291,7 @@@ static int get_tmpi_omp_thread_division
      else if (hw_opt->nthreads_omp > 0)
      {
          /* Here we could oversubscribe, when we do, we issue a warning later */
 -        nthreads_tmpi = max(1, nthreads_tot/hw_opt->nthreads_omp);
 +        nthreads_tmpi = std::max(1, nthreads_tot/hw_opt->nthreads_omp);
      }
      else
      {
@@@ -352,6 -356,8 +352,6 @@@ static int get_nthreads_mpi(const gmx_h
  {
      int      nthreads_hw, nthreads_tot_max, nthreads_tmpi, nthreads_new, ngpu;
      int      min_atoms_per_mpi_thread;
 -    char    *env;
 -    char     sbuf[STRLEN];
      gmx_bool bCanUseGPU;
  
      if (hw_opt->nthreads_tmpi > 0)
      }
  
      bCanUseGPU = (inputrec->cutoff_scheme == ecutsVERLET &&
 -                  hwinfo->gpu_info.ncuda_dev_compatible > 0);
 +                  hwinfo->gpu_info.n_dev_compatible > 0);
 +
      if (bCanUseGPU)
      {
 -        ngpu = hwinfo->gpu_info.ncuda_dev_compatible;
 +        ngpu = hwinfo->gpu_info.n_dev_compatible;
      }
      else
      {
      {
          /* the thread number was chosen automatically, but there are too many
             threads (too few atoms per thread) */
 -        nthreads_new = max(1, mtop->natoms/min_atoms_per_mpi_thread);
 +        nthreads_new = std::max(1, mtop->natoms/min_atoms_per_mpi_thread);
  
          /* Avoid partial use of Hyper-Threading */
          if (gmx_cpuid_x86_smt(hwinfo->cpuid_info) == GMX_CPUID_X86_SMT_ENABLED &&
  /* We determine the extra cost of the non-bonded kernels compared to
   * a reference nstlist value of 10 (which is the default in grompp).
   */
 -static const int    nbnxn_reference_nstlist = 10;
 +static const int    nbnxnReferenceNstlist = 10;
  /* The values to try when switching  */
  const int           nstlist_try[] = { 20, 25, 40 };
  #define NNSTL  sizeof(nstlist_try)/sizeof(nstlist_try[0])
@@@ -504,9 -509,9 +504,9 @@@ static void increase_nstlist(FILE *fp, 
      float                  listfac_ok, listfac_max;
      int                    nstlist_orig, nstlist_prev;
      verletbuf_list_setup_t ls;
 -    real                   rlist_nstlist10, rlist_inc, rlist_ok, rlist_max;
 +    real                   rlistWithReferenceNstlist, rlist_inc, rlist_ok, rlist_max;
      real                   rlist_new, rlist_prev;
 -    int                    nstlist_ind = 0;
 +    size_t                 nstlist_ind = 0;
      t_state                state_tmp;
      gmx_bool               bBox, bDD, bCont;
      const char            *nstl_gpu = "\nFor optimal performance with a GPU nstlist (now %d) should be larger.\nThe optimum depends on your CPU and GPU resources.\nYou might want to try several nstlist values.\n";
      const char            *box_err  = "Can not increase nstlist because the box is too small";
      const char            *dd_err   = "Can not increase nstlist because of domain decomposition limitations";
      char                   buf[STRLEN];
 +    const float            oneThird = 1.0f / 3.0f;
  
      if (nstlist_cmdline <= 0)
      {
      verletbuf_get_list_setup(bGPU, &ls);
  
      /* Allow rlist to make the list a given factor larger than the list
 -     * would be with nstlist=10.
 +     * would be with the reference value for nstlist (10).
       */
      nstlist_prev = ir->nstlist;
 -    ir->nstlist  = 10;
 +    ir->nstlist  = nbnxnReferenceNstlist;
      calc_verlet_buffer_size(mtop, det(box), ir, -1, &ls, NULL,
 -                            &rlist_nstlist10);
 +                            &rlistWithReferenceNstlist);
      ir->nstlist  = nstlist_prev;
  
      /* Determine the pair list size increase due to zero interactions */
      rlist_inc = nbnxn_get_rlist_effective_inc(ls.cluster_size_j,
                                                mtop->natoms/det(box));
 -    rlist_ok  = (rlist_nstlist10 + rlist_inc)*pow(listfac_ok, 1.0/3.0) - rlist_inc;
 -    rlist_max = (rlist_nstlist10 + rlist_inc)*pow(listfac_max, 1.0/3.0) - rlist_inc;
 +    rlist_ok  = (rlistWithReferenceNstlist + rlist_inc)*pow(listfac_ok, oneThird) - rlist_inc;
 +    rlist_max = (rlistWithReferenceNstlist + rlist_inc)*pow(listfac_max, oneThird) - rlist_inc;
      if (debug)
      {
          fprintf(debug, "nstlist tuning: rlist_inc %.3f rlist_ok %.3f rlist_max %.3f\n",
@@@ -753,6 -757,99 +753,6 @@@ static void prepare_verlet_scheme(FIL
      }
  }
  
 -static void convert_to_verlet_scheme(FILE *fplog,
 -                                     t_inputrec *ir,
 -                                     gmx_mtop_t *mtop, real box_vol)
 -{
 -    char *conv_mesg = "Converting input file with group cut-off scheme to the Verlet cut-off scheme";
 -
 -    md_print_warn(NULL, fplog, "%s\n", conv_mesg);
 -
 -    ir->cutoff_scheme = ecutsVERLET;
 -    ir->verletbuf_tol = 0.005;
 -
 -    if (ir->rcoulomb != ir->rvdw)
 -    {
 -        gmx_fatal(FARGS, "The VdW and Coulomb cut-offs are different, whereas the Verlet scheme only supports equal cut-offs");
 -    }
 -
 -    if (ir->vdwtype == evdwUSER || EEL_USER(ir->coulombtype))
 -    {
 -        gmx_fatal(FARGS, "User non-bonded potentials are not (yet) supported with the Verlet scheme");
 -    }
 -    else if (ir_vdw_switched(ir) || ir_coulomb_switched(ir))
 -    {
 -        if (ir_vdw_switched(ir) && ir->vdw_modifier == eintmodNONE)
 -        {
 -            ir->vdwtype = evdwCUT;
 -
 -            switch (ir->vdwtype)
 -            {
 -                case evdwSHIFT:  ir->vdw_modifier = eintmodFORCESWITCH; break;
 -                case evdwSWITCH: ir->vdw_modifier = eintmodPOTSWITCH; break;
 -                default: gmx_fatal(FARGS, "The Verlet scheme does not support Van der Waals interactions of type '%s'", evdw_names[ir->vdwtype]);
 -            }
 -        }
 -        if (ir_coulomb_switched(ir) && ir->coulomb_modifier == eintmodNONE)
 -        {
 -            if (EEL_FULL(ir->coulombtype))
 -            {
 -                /* With full electrostatic only PME can be switched */
 -                ir->coulombtype      = eelPME;
 -                ir->coulomb_modifier = eintmodPOTSHIFT;
 -            }
 -            else
 -            {
 -                md_print_warn(NULL, fplog, "NOTE: Replacing %s electrostatics with reaction-field with epsilon-rf=inf\n", eel_names[ir->coulombtype]);
 -                ir->coulombtype      = eelRF;
 -                ir->epsilon_rf       = 0.0;
 -                ir->coulomb_modifier = eintmodPOTSHIFT;
 -            }
 -        }
 -
 -        /* We set the pair energy error tolerance to a small number.
 -         * Note that this is only for testing. For production the user
 -         * should think about this and set the mdp options.
 -         */
 -        ir->verletbuf_tol = 1e-4;
 -    }
 -
 -    if (inputrec2nboundeddim(ir) != 3)
 -    {
 -        gmx_fatal(FARGS, "Can only convert old tpr files to the Verlet cut-off scheme with 3D pbc");
 -    }
 -
 -    if (ir->efep != efepNO || ir->implicit_solvent != eisNO)
 -    {
 -        gmx_fatal(FARGS, "Will not convert old tpr files to the Verlet cut-off scheme with free-energy calculations or implicit solvent");
 -    }
 -
 -    if (EI_DYNAMICS(ir->eI) && !(EI_MD(ir->eI) && ir->etc == etcNO))
 -    {
 -        verletbuf_list_setup_t ls;
 -
 -        verletbuf_get_list_setup(FALSE, &ls);
 -        calc_verlet_buffer_size(mtop, box_vol, ir, -1, &ls, NULL, &ir->rlist);
 -    }
 -    else
 -    {
 -        real rlist_fac;
 -
 -        if (EI_MD(ir->eI))
 -        {
 -            rlist_fac       = 1 + verlet_buffer_ratio_NVE_T0;
 -        }
 -        else
 -        {
 -            rlist_fac       = 1 + verlet_buffer_ratio_nodynamics;
 -        }
 -        ir->verletbuf_tol   = -1;
 -        ir->rlist           = rlist_fac*max(ir->rvdw, ir->rcoulomb);
 -    }
 -
 -    gmx_mtop_remove_chargegroups(mtop);
 -}
 -
  static void print_hw_opt(FILE *fp, const gmx_hw_opt_t *hw_opt)
  {
      fprintf(fp, "hw_opt: nt %d ntmpi %d ntomp %d ntomp_pme %d gpu_id '%s'\n",
@@@ -772,18 -869,18 +772,18 @@@ static void check_and_update_hw_opt_1(g
  #ifndef GMX_THREAD_MPI
      if (hw_opt->nthreads_tot > 0)
      {
 -        gmx_fatal(FARGS, "Setting the total number of threads is only supported with thread-MPI and Gromacs was compiled without thread-MPI");
 +        gmx_fatal(FARGS, "Setting the total number of threads is only supported with thread-MPI and GROMACS was compiled without thread-MPI");
      }
      if (hw_opt->nthreads_tmpi > 0)
      {
 -        gmx_fatal(FARGS, "Setting the number of thread-MPI threads is only supported with thread-MPI and Gromacs was compiled without thread-MPI");
 +        gmx_fatal(FARGS, "Setting the number of thread-MPI threads is only supported with thread-MPI and GROMACS was compiled without thread-MPI");
      }
  #endif
  
  #ifndef GMX_OPENMP
      if (hw_opt->nthreads_omp > 1)
      {
 -        gmx_fatal(FARGS, "More than 1 OpenMP thread requested, but Gromacs was compiled without OpenMP support");
 +        gmx_fatal(FARGS, "More than 1 OpenMP thread requested, but GROMACS was compiled without OpenMP support");
      }
      hw_opt->nthreads_omp = 1;
  #endif
  #ifndef GMX_OPENMP
      if (hw_opt->nthreads_omp > 1)
      {
 -        gmx_fatal(FARGS, "OpenMP threads are requested, but Gromacs was compiled without OpenMP support");
 +        gmx_fatal(FARGS, "OpenMP threads are requested, but GROMACS was compiled without OpenMP support");
      }
  #endif
  
      gmx_parse_gpu_ids(&hw_opt->gpu_opt);
  
  #ifdef GMX_THREAD_MPI
 -    if (hw_opt->gpu_opt.ncuda_dev_use > 0 && hw_opt->nthreads_tmpi == 0)
 +    if (hw_opt->gpu_opt.n_dev_use > 0
 +        &&
 +        hw_opt->nthreads_tmpi == 0)
      {
          /* Set the number of MPI threads equal to the number of GPUs */
 -        hw_opt->nthreads_tmpi = hw_opt->gpu_opt.ncuda_dev_use;
 +        hw_opt->nthreads_tmpi = hw_opt->gpu_opt.n_dev_use;
  
          if (hw_opt->nthreads_tot > 0 &&
              hw_opt->nthreads_tmpi > hw_opt->nthreads_tot)
@@@ -916,36 -1011,72 +916,36 @@@ static void override_nsteps_cmdline(FIL
                                      t_inputrec      *ir,
                                      const t_commrec *cr)
  {
 -    char sbuf[STEPSTRSIZE];
 -
      assert(ir);
      assert(cr);
  
      /* override with anything else than the default -2 */
      if (nsteps_cmdline > -2)
      {
 -        char stmp[STRLEN];
 +        char sbuf_steps[STEPSTRSIZE];
 +        char sbuf_msg[STRLEN];
  
          ir->nsteps = nsteps_cmdline;
          if (EI_DYNAMICS(ir->eI) && nsteps_cmdline != -1)
          {
 -            sprintf(stmp, "Overriding nsteps with value passed on the command line: %s steps, %.3g ps",
 -                    gmx_step_str(nsteps_cmdline, sbuf),
 +            sprintf(sbuf_msg, "Overriding nsteps with value passed on the command line: %s steps, %.3g ps",
 +                    gmx_step_str(nsteps_cmdline, sbuf_steps),
                      fabs(nsteps_cmdline*ir->delta_t));
          }
          else
          {
 -            sprintf(stmp, "Overriding nsteps with value passed on the command line: %s steps",
 -                    gmx_step_str(nsteps_cmdline, sbuf));
 +            sprintf(sbuf_msg, "Overriding nsteps with value passed on the command line: %s steps",
 +                    gmx_step_str(nsteps_cmdline, sbuf_steps));
          }
  
 -        md_print_warn(cr, fplog, "%s\n", stmp);
 +        md_print_warn(cr, fplog, "%s\n", sbuf_msg);
      }
 -}
 -
 -/* Frees GPU memory and destroys the CUDA context.
 - *
 - * Note that this function needs to be called even if GPUs are not used
 - * in this run because the PME ranks have no knowledge of whether GPUs
 - * are used or not, but all ranks need to enter the barrier below.
 - */
 -static void free_gpu_resources(const t_forcerec *fr,
 -                               const t_commrec  *cr)
 -{
 -    gmx_bool bIsPPrankUsingGPU;
 -    char     gpu_err_str[STRLEN];
 -
 -    bIsPPrankUsingGPU = (cr->duty & DUTY_PP) && fr->nbv != NULL && fr->nbv->bUseGPU;
 -
 -    if (bIsPPrankUsingGPU)
 +    else if (nsteps_cmdline < -2)
      {
 -        /* free nbnxn data in GPU memory */
 -        nbnxn_cuda_free(fr->nbv->cu_nbv);
 -
 -        /* With tMPI we need to wait for all ranks to finish deallocation before
 -         * destroying the context in free_gpu() as some ranks may be sharing
 -         * GPU and context.
 -         * Note: as only PP ranks need to free GPU resources, so it is safe to
 -         * not call the barrier on PME ranks.
 -         */
 -#ifdef GMX_THREAD_MPI
 -        if (PAR(cr))
 -        {
 -            gmx_barrier(cr);
 -        }
 -#endif  /* GMX_THREAD_MPI */
 -
 -        /* uninitialize GPU (by destroying the context) */
 -        if (!free_gpu(gpu_err_str))
 -        {
 -            gmx_warning("On rank %d failed to free GPU #%d: %s",
 -                        cr->nodeid, get_current_gpu_device_id(), gpu_err_str);
 -        }
 +        gmx_fatal(FARGS, "Invalid nsteps value passed on the command line: %d",
 +                  nsteps_cmdline);
      }
 +    /* Do nothing if nsteps_cmdline == -2 */
  }
  
  int mdrunner(gmx_hw_opt_t *hw_opt,
               gmx_int64_t nsteps_cmdline, int nstepout, int resetstep,
               int gmx_unused nmultisim, int repl_ex_nst, int repl_ex_nex,
               int repl_ex_seed, real pforce, real cpt_period, real max_hours,
 -             const char *deviceOptions, int imdport, unsigned long Flags)
 +             int imdport, unsigned long Flags)
  {
      gmx_bool                  bForceUseGPU, bTryUseGPU, bRerunMD, bCantUseGPU;
 -    double                    nodetime = 0, realtime;
      t_inputrec               *inputrec;
      t_state                  *state = NULL;
      matrix                    box;
      gmx_ddbox_t               ddbox = {0};
      int                       npme_major, npme_minor;
 -    real                      tmpr1, tmpr2;
      t_nrnb                   *nrnb;
      gmx_mtop_t               *mtop          = NULL;
      t_mdatoms                *mdatoms       = NULL;
      t_fcdata                 *fcd           = NULL;
      real                      ewaldcoeff_q  = 0;
      real                      ewaldcoeff_lj = 0;
 -    gmx_pme_t                *pmedata       = NULL;
 +    struct gmx_pme_t        **pmedata       = NULL;
      gmx_vsite_t              *vsite         = NULL;
      gmx_constr_t              constr;
 -    int                       i, m, nChargePerturbed = -1, nTypePerturbed = 0, status, nalloc;
 -    char                     *gro;
 +    int                       nChargePerturbed = -1, nTypePerturbed = 0, status;
      gmx_wallcycle_t           wcycle;
      gmx_bool                  bReadEkin;
 -    int                       list;
      gmx_walltime_accounting_t walltime_accounting = NULL;
      int                       rc;
      gmx_int64_t               reset_counters;
      gmx_edsam_t               ed           = NULL;
 -    t_commrec                *cr_old       = cr;
      int                       nthreads_pme = 1;
      int                       nthreads_pp  = 1;
      gmx_membed_t              membed       = NULL;
      if (SIMMASTER(cr))
      {
          /* Read (nearly) all data required for the simulation */
 -        read_tpx_state(ftp2fn(efTPX, nfile, fnm), inputrec, state, NULL, mtop);
 -
 -        if (inputrec->cutoff_scheme != ecutsVERLET &&
 -            ((Flags & MD_TESTVERLET) || getenv("GMX_VERLET_SCHEME") != NULL))
 -        {
 -            convert_to_verlet_scheme(fplog, inputrec, mtop, det(state->box));
 -        }
 +        read_tpx_state(ftp2fn(efTPR, nfile, fnm), inputrec, state, NULL, mtop);
  
          if (inputrec->cutoff_scheme == ecutsVERLET)
          {
              /* Here the master rank decides if all ranks will use GPUs */
 -            bUseGPU = (hwinfo->gpu_info.ncuda_dev_compatible > 0 ||
 +            bUseGPU = (hwinfo->gpu_info.n_dev_compatible > 0 ||
                         getenv("GMX_EMULATE_GPU") != NULL);
  
              /* TODO add GPU kernels for this and replace this check by:
                  gmx_fatal(FARGS, "Can not set nstlist with the group cut-off scheme");
              }
  
 -            if (hwinfo->gpu_info.ncuda_dev_compatible > 0)
 +            if (hwinfo->gpu_info.n_dev_compatible > 0)
              {
                  md_print_warn(cr, fplog,
                                "NOTE: GPU(s) found, but the current simulation can not use GPUs\n"
 -                              "      To use a GPU, set the mdp option: cutoff-scheme = Verlet\n"
 -                              "      (for quick performance testing you can use the -testverlet option)\n");
 +                              "      To use a GPU, set the mdp option: cutoff-scheme = Verlet\n");
              }
  
              if (bForceUseGPU)
          }
      }
  
 -    /* Check for externally set OpenMP affinity and turn off internal
 -     * pinning if any is found. We need to do this check early to tell
 -     * thread-MPI whether it should do pinning when spawning threads.
 -     * TODO: the above no longer holds, we should move these checks down
 -     */
 -    gmx_omp_check_thread_affinity(fplog, cr, hw_opt);
 -
      /* Check and update the hardware options for internal consistency */
      check_and_update_hw_opt_1(hw_opt, SIMMASTER(cr));
  
 +    /* Early check for externally set process affinity. */
 +    gmx_check_thread_affinity_set(fplog, cr,
 +                                  hw_opt, hwinfo->nthreads_hw_avail, FALSE);
      if (SIMMASTER(cr))
      {
 -#ifdef GMX_THREAD_MPI
 -        /* Early check for externally set process affinity.
 -         * With thread-MPI this is needed as pinning might get turned off,
 -         * which needs to be known before starting thread-MPI.
 -         * With thread-MPI hw_opt is processed here on the master rank
 -         * and passed to the other ranks later, so we only do this on master.
 -         */
 -        gmx_check_thread_affinity_set(fplog,
 -                                      NULL,
 -                                      hw_opt, hwinfo->nthreads_hw_avail, FALSE);
 -#endif
  
  #ifdef GMX_THREAD_MPI
          if (cr->npmenodes > 0 && hw_opt->nthreads_tmpi <= 0)
  
          if (hw_opt->nthreads_tmpi > 1)
          {
 +            t_commrec *cr_old       = cr;
              /* now start the threads. */
              cr = mdrunner_start_threads(hw_opt, fplog, cr_old, nfile, fnm,
                                          oenv, bVerbose, bCompact, nstglobalcomm,
                                          nbpu_opt, nstlist_cmdline,
                                          nsteps_cmdline, nstepout, resetstep, nmultisim,
                                          repl_ex_nst, repl_ex_nex, repl_ex_seed, pforce,
 -                                        cpt_period, max_hours, deviceOptions,
 +                                        cpt_period, max_hours,
                                          Flags);
              /* the main thread continues here with a new cr. We don't deallocate
                 the old cr because other threads may still be reading it. */
      {
          /* now broadcast everything to the non-master nodes/threads: */
          init_parallel(cr, inputrec, mtop);
+         /* The master rank decided on the use of GPUs,
+          * broadcast this information to all ranks.
+          */
+         gmx_bcast_sim(sizeof(bUseGPU), &bUseGPU, cr);
      }
      if (fplog != NULL)
      {
          pr_inputrec(fplog, 0, "Input Parameters", inputrec, FALSE);
                    "but %s was not started through mpirun/mpiexec or only one rank was requested through mpirun/mpiexec"
  #endif
  #endif
 -                  , ShortProgram()
 +                  , output_env_get_program_display_name(oenv)
                    );
      }
  
          cr->npmenodes = 0;
      }
  
+     if (bUseGPU && cr->npmenodes < 0)
+     {
+         /* With GPUs we don't automatically use PME-only ranks. PME ranks can
+          * improve performance with many threads per GPU, since our OpenMP
+          * scaling is bad, but it's difficult to automate the setup.
+          */
+         cr->npmenodes = 0;
+     }
  #ifdef GMX_FAHCORE
      if (MASTER(cr))
      {
          }
      }
  
 -    if (((MASTER(cr) || (Flags & MD_SEPPOT)) && (Flags & MD_APPENDFILES))
 -#ifdef GMX_THREAD_MPI
 -        /* With thread MPI only the master node/thread exists in mdrun.c,
 -         * therefore non-master nodes need to open the "seppot" log file here.
 -         */
 -        || (!MASTER(cr) && (Flags & MD_SEPPOT))
 -#endif
 -        )
 +    if (MASTER(cr) && (Flags & MD_APPENDFILES))
      {
 -        gmx_log_open(ftp2fn(efLOG, nfile, fnm), cr, !(Flags & MD_SEPPOT),
 +        gmx_log_open(ftp2fn(efLOG, nfile, fnm), cr,
                       Flags, &fplog);
      }
  
      if (MULTISIM(cr))
      {
          md_print_info(cr, fplog,
 -                      "This is simulation %d out of %d running as a composite Gromacs\n"
 +                      "This is simulation %d out of %d running as a composite GROMACS\n"
                        "multi-simulation job. Setup for this simulation:\n\n",
                        cr->ms->sim, cr->ms->nsim);
      }
                            (cr->duty & DUTY_PP) == 0,
                            inputrec->cutoff_scheme == ecutsVERLET);
  
-     if (PAR(cr))
-     {
-         /* The master rank decided on the use of GPUs,
-          * broadcast this information to all ranks.
-          */
-         gmx_bcast_sim(sizeof(bUseGPU), &bUseGPU, cr);
-     }
 +#ifndef NDEBUG
 +    if (integrator[inputrec->eI].func != do_tpi &&
 +        inputrec->cutoff_scheme == ecutsVERLET)
 +    {
 +        gmx_feenableexcept();
 +    }
 +#endif
 +
      if (bUseGPU)
      {
-         if (cr->npmenodes == -1)
-         {
-             /* Don't automatically use PME-only nodes with GPUs */
-             cr->npmenodes = 0;
-         }
          /* Select GPU id's to use */
          gmx_select_gpu_ids(fplog, cr, &hwinfo->gpu_info, bForceUseGPU,
                             &hw_opt->gpu_opt);
      else
      {
          /* Ignore (potentially) manually selected GPUs */
 -        hw_opt->gpu_opt.ncuda_dev_use = 0;
 +        hw_opt->gpu_opt.n_dev_use = 0;
      }
  
      /* check consistency across ranks of things like SIMD
          /*init_forcerec(fplog,fr,fcd,inputrec,mtop,cr,box,FALSE,
             "nofile","nofile","nofile","nofile",FALSE,pforce);
           */
 -        fr->bSepDVDL = ((Flags & MD_SEPPOT) == MD_SEPPOT);
  
          /* Initialize QM-MM */
          if (fr->bQMMM)
          /* Assumes uniform use of the number of OpenMP threads */
          walltime_accounting = walltime_accounting_init(gmx_omp_nthreads_get(emntDefault));
  
 -        if (inputrec->ePull != epullNO)
 +        if (inputrec->bPull)
          {
              /* Initialize pull code */
              init_pull(fplog, inputrec, nfile, fnm, mtop, cr, oenv, inputrec->fepvals->init_lambda,
  
          if (DOMAINDECOMP(cr))
          {
 +            GMX_RELEASE_ASSERT(fr, "fr was NULL while cr->duty was DUTY_PP");
              dd_init_bondeds(fplog, cr->dd, mtop, vsite, inputrec,
                              Flags & MD_DDBONDCHECK, fr->cginfo_mb);
  
                                        repl_ex_nst, repl_ex_nex, repl_ex_seed,
                                        membed,
                                        cpt_period, max_hours,
 -                                      deviceOptions,
                                        imdport,
                                        Flags,
                                        walltime_accounting);
  
 -        if (inputrec->ePull != epullNO)
 +        if (inputrec->bPull)
          {
              finish_pull(inputrec->pull);
          }
      }
      else
      {
 +        GMX_RELEASE_ASSERT(pmedata, "pmedata was NULL while cr->duty was not DUTY_PP");
          /* do PME only */
          walltime_accounting = walltime_accounting_init(gmx_omp_nthreads_get(emntPME));
          gmx_pmeonly(*pmedata, cr, nrnb, wcycle, walltime_accounting, ewaldcoeff_q, ewaldcoeff_lj, inputrec);
       */
      finish_run(fplog, cr,
                 inputrec, nrnb, wcycle, walltime_accounting,
 -               fr != NULL && fr->nbv != NULL && fr->nbv->bUseGPU ?
 -               nbnxn_cuda_get_timings(fr->nbv->cu_nbv) : NULL,
 +               fr ? fr->nbv : NULL,
                 EI_DYNAMICS(inputrec->eI) && !MULTISIM(cr));
  
  
      /* Free GPU memory and context */
 -    free_gpu_resources(fr, cr);
 +    free_gpu_resources(fr, cr, &hwinfo->gpu_info, fr ? fr->gpu_opt : NULL);
  
      if (opt2bSet("-membed", nfile, fnm))
      {
  
      rc = (int)gmx_get_stop_condition();
  
 +    done_ed(&ed);
 +
  #ifdef GMX_THREAD_MPI
      /* we need to join all threads. The sub-threads join when they
         exit this function, but the master thread needs to be told to