Merge branch 'origin/release-2020' into merge-release-2020
authorPaul Bauer <paul.bauer.q@gmail.com>
Tue, 20 Oct 2020 14:50:31 +0000 (16:50 +0200)
committerPaul Bauer <paul.bauer.q@gmail.com>
Tue, 20 Oct 2020 14:50:31 +0000 (16:50 +0200)
1  2 
src/gromacs/gmxpreprocess/hackblock.cpp
src/gromacs/mdlib/shake.cpp

index bc00ea2c46f1396e4e569fd68738ff2632991791,73a144cef309de517833604c9c17827a849a4f75..2459141ae6e9744a00c7369cd8429236ae179b79
@@@ -3,8 -3,7 +3,8 @@@
   *
   * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
   * Copyright (c) 2001-2004, The GROMACS development team.
 - * Copyright (c) 2011,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
 + * Copyright (c) 2011,2014,2015,2017,2018 by the GROMACS development team.
 + * Copyright (c) 2019,2020, 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.
  #include "gromacs/mdtypes/md_enums.h"
  #include "gromacs/topology/atoms.h"
  #include "gromacs/topology/symtab.h"
 +#include "gromacs/utility/arrayref.h"
  #include "gromacs/utility/cstringutil.h"
  #include "gromacs/utility/exceptions.h"
  #include "gromacs/utility/fatalerror.h"
  #include "gromacs/utility/gmxassert.h"
  #include "gromacs/utility/smalloc.h"
+ #include "gromacs/utility/stringcompare.h"
  
  /* these MUST correspond to the enum in hackblock.h */
  const char* btsNames[ebtsNR] = { "bonds", "angles", "dihedrals", "impropers", "exclusions", "cmap" };
@@@ -136,7 -135,7 +137,7 @@@ static int rbonded_find_atoms_in_list(c
               * Since we only have the unparsed string here we can only detect
               * EXACT matches (including identical whitespace).
               */
-             if (b.s != it->s)
+             if (b.s == it->s)
              {
                  gmx_warning("Duplicate line found in or between hackblock and rtp entries");
              }
index 30c965214bd1e2a244f493f7c9006440587e2a3e,99cc542300ef72ec8460cc1f695671711ba73222..ee5dee5f4f82ea38d7e3e045ba3582c2d1bb270e
@@@ -3,8 -3,7 +3,8 @@@
   *
   * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
   * Copyright (c) 2001-2004, The GROMACS development team.
 - * Copyright (c) 2013,2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
 + * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
 + * Copyright (c) 2019,2020, 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.
  #include "shake.h"
  
  #include <cmath>
+ #include <cstdlib>
  
  #include <algorithm>
  
 -#include "gromacs/domdec/domdec_struct.h"
  #include "gromacs/gmxlib/nrnb.h"
  #include "gromacs/math/functions.h"
  #include "gromacs/math/vec.h"
@@@ -58,7 -59,7 +59,7 @@@
  #include "gromacs/mdlib/splitter.h"
  #include "gromacs/mdtypes/inputrec.h"
  #include "gromacs/mdtypes/md_enums.h"
 -#include "gromacs/mdtypes/mdatom.h"
 +#include "gromacs/pbcutil/pbc.h"
  #include "gromacs/topology/invblock.h"
  #include "gromacs/utility/fatalerror.h"
  #include "gromacs/utility/smalloc.h"
  namespace gmx
  {
  
 -struct shakedata
 -{
 -    rvec* rij;
 -    real* half_of_reduced_mass;
 -    real* distance_squared_tolerance;
 -    real* constraint_distance_squared;
 -    int   nalloc;
 -    /* SOR stuff */
 -    real delta;
 -    real omega;
 -    real gamma;
 -    int  nblocks;       /* The number of SHAKE blocks         */
 -    int* sblock;        /* The SHAKE blocks                   */
 -    int  sblock_nalloc; /* The allocation size of sblock      */
 -    /*! \brief Scaled Lagrange multiplier for each constraint.
 -     *
 -     * Value is -2 * eta from p. 336 of the paper, divided by the
 -     * constraint distance. */
 -    real* scaled_lagrange_multiplier;
 -    int   lagr_nalloc; /* The allocation size of scaled_lagrange_multiplier */
 -};
 -
 -shakedata* shake_init()
 -{
 -    shakedata* d;
 -
 -    snew(d, 1);
 -
 -    d->nalloc                      = 0;
 -    d->rij                         = nullptr;
 -    d->half_of_reduced_mass        = nullptr;
 -    d->distance_squared_tolerance  = nullptr;
 -    d->constraint_distance_squared = nullptr;
 -
 -    /* SOR initialization */
 -    d->delta = 0.1;
 -    d->omega = 1.0;
 -    d->gamma = 1000000;
 -
 -    return d;
 -}
 -
 -void done_shake(shakedata* d)
 -{
 -    sfree(d->rij);
 -    sfree(d->half_of_reduced_mass);
 -    sfree(d->distance_squared_tolerance);
 -    sfree(d->constraint_distance_squared);
 -    sfree(d->sblock);
 -    sfree(d->scaled_lagrange_multiplier);
 -    sfree(d);
 -}
 -
  typedef struct
  {
      int iatom[3];
@@@ -118,38 -172,44 +119,38 @@@ static void pr_sortblock(FILE* fp, cons
  //! Reallocates a vector.
  static void resizeLagrangianData(shakedata* shaked, int ncons)
  {
 -    if (ncons > shaked->lagr_nalloc)
 -    {
 -        shaked->lagr_nalloc = over_alloc_dd(ncons);
 -        srenew(shaked->scaled_lagrange_multiplier, shaked->lagr_nalloc);
 -    }
 +    shaked->scaled_lagrange_multiplier.resize(ncons);
  }
  
 -void make_shake_sblock_serial(shakedata* shaked, const t_idef* idef, const t_mdatoms& md)
 +void make_shake_sblock_serial(shakedata* shaked, InteractionDefinitions* idef, const int numAtoms)
  {
 -    int          i, j, m, ncons;
 +    int          i, m, ncons;
      int          bstart, bnr;
      t_blocka     sblocks;
      t_sortblock* sb;
 -    t_iatom*     iatom;
      int*         inv_sblock;
  
      /* Since we are processing the local topology,
       * the F_CONSTRNC ilist has been concatenated to the F_CONSTR ilist.
       */
 -    ncons = idef->il[F_CONSTR].nr / 3;
 +    ncons = idef->il[F_CONSTR].size() / 3;
  
      init_blocka(&sblocks);
      sfree(sblocks.index); // To solve memory leak
 -    gen_sblocks(nullptr, 0, md.homenr, idef, &sblocks, FALSE);
 +    gen_sblocks(nullptr, numAtoms, *idef, &sblocks, FALSE);
  
      /*
         bstart=(idef->nodeid > 0) ? blocks->multinr[idef->nodeid-1] : 0;
         nblocks=blocks->multinr[idef->nodeid] - bstart;
       */
 -    bstart          = 0;
 -    shaked->nblocks = sblocks.nr;
 +    bstart = 0;
      if (debug)
      {
 -        fprintf(debug, "ncons: %d, bstart: %d, nblocks: %d\n", ncons, bstart, shaked->nblocks);
 +        fprintf(debug, "ncons: %d, bstart: %d, nblocks: %d\n", ncons, bstart, sblocks.nr);
      }
  
      /* Calculate block number for each atom */
 -    inv_sblock = make_invblocka(&sblocks, md.nr);
 +    inv_sblock = make_invblocka(&sblocks, numAtoms);
  
      done_blocka(&sblocks);
  
       * sort the constraints in order of the sblock number
       * and the atom numbers, really sorting a segment of the array!
       */
 -    iatom = idef->il[F_CONSTR].iatoms;
 +    int* iatom = idef->il[F_CONSTR].iatoms.data();
      snew(sb, ncons);
      for (i = 0; (i < ncons); i++, iatom += 3)
      {
          pr_sortblock(debug, "After sorting", ncons, sb);
      }
  
 -    iatom = idef->il[F_CONSTR].iatoms;
 +    iatom = idef->il[F_CONSTR].iatoms.data();
      for (i = 0; (i < ncons); i++, iatom += 3)
      {
          for (m = 0; (m < 3); m++)
          }
      }
  
 -    j = 0;
 -    snew(shaked->sblock, shaked->nblocks + 1);
 +    shaked->sblock.clear();
      bnr = -2;
      for (i = 0; (i < ncons); i++)
      {
          if (sb[i].blocknr != bnr)
          {
 -            bnr                 = sb[i].blocknr;
 -            shaked->sblock[j++] = 3 * i;
 +            bnr = sb[i].blocknr;
 +            shaked->sblock.push_back(3 * i);
          }
      }
      /* Last block... */
 -    shaked->sblock[j++] = 3 * ncons;
 +    shaked->sblock.push_back(3 * ncons);
  
 -    if (j != (shaked->nblocks + 1))
 -    {
 -        fprintf(stderr, "bstart: %d\n", bstart);
 -        fprintf(stderr, "j: %d, nblocks: %d, ncons: %d\n", j, shaked->nblocks, ncons);
 -        for (i = 0; (i < ncons); i++)
 -        {
 -            fprintf(stderr, "i: %5d  sb[i].blocknr: %5d\n", i, sb[i].blocknr);
 -        }
 -        for (j = 0; (j <= shaked->nblocks); j++)
 -        {
 -            fprintf(stderr, "sblock[%3d]=%5d\n", j, shaked->sblock[j]);
 -        }
 -        gmx_fatal(FARGS,
 -                  "DEATH HORROR: "
 -                  "sblocks does not match idef->il[F_CONSTR]");
 -    }
      sfree(sb);
      sfree(inv_sblock);
      resizeLagrangianData(shaked, ncons);
  }
  
 -// TODO: Check if this code is useful. It might never be called.
 -void make_shake_sblock_dd(shakedata* shaked, const t_ilist* ilcon, const gmx_domdec_t* dd)
 +void make_shake_sblock_dd(shakedata* shaked, const InteractionList& ilcon)
  {
 -    int      ncons, c, cg;
 -    t_iatom* iatom;
 +    int ncons, c, cg;
  
 -    if (dd->ncg_home + 1 > shaked->sblock_nalloc)
 -    {
 -        shaked->sblock_nalloc = over_alloc_dd(dd->ncg_home + 1);
 -        srenew(shaked->sblock, shaked->sblock_nalloc);
 -    }
 -
 -    ncons           = ilcon->nr / 3;
 -    iatom           = ilcon->iatoms;
 -    shaked->nblocks = 0;
 -    cg              = 0;
 +    ncons            = ilcon.size() / 3;
 +    const int* iatom = ilcon.iatoms.data();
 +    shaked->sblock.clear();
 +    cg = 0;
      for (c = 0; c < ncons; c++)
      {
          if (c == 0 || iatom[1] >= cg + 1)
          {
 -            shaked->sblock[shaked->nblocks++] = 3 * c;
 +            shaked->sblock.push_back(3 * c);
              while (iatom[1] >= cg + 1)
              {
                  cg++;
          }
          iatom += 3;
      }
 -    shaked->sblock[shaked->nblocks] = 3 * ncons;
 +    shaked->sblock.push_back(3 * ncons);
      resizeLagrangianData(shaked, ncons);
  }
  
   * The algorithm here is based section five of Ryckaert, Ciccotti and
   * Berendsen, J Comp Phys, 23, 327, 1977.
   *
 - * \param[in]    iatom                         Mini-topology of triples of constraint type (unused in this
 - *                                             function) and indices of the two atoms involved
 + * \param[in]    iatom                         Mini-topology of triplets of constraint type (unused
 + *                                             in this function) and indices of two atoms involved
   * \param[in]    ncon                          Number of constraints
   * \param[out]   nnit                          Number of iterations performed
   * \param[in]    maxnit                        Maximum number of iterations permitted
   * \param[in]    constraint_distance_squared   The objective value for each constraint
 - * \param[inout] positions                     The initial (and final) values of the positions of all atoms
 + * \param[inout] positions                     The initial (and final) values of the positions
 + *                                             of all atoms
 + * \param[in]    pbc                           PBC information
   * \param[in]    initial_displacements         The initial displacements of each constraint
   * \param[in]    half_of_reduced_mass          Half of the reduced mass for each constraint
   * \param[in]    omega                         SHAKE over-relaxation factor (set non-1.0 by
 - *                                             using shake-sor=yes in the .mdp, but there is no documentation anywhere)
 + *                                             using shake-sor=yes in the .mdp,
 + *                                             but there is no documentation anywhere)
   * \param[in]    invmass                       Inverse mass of each atom
   * \param[in]    distance_squared_tolerance    Multiplicative tolerance on the difference in the
   *                                             square of the constrained distance (see code)
 - * \param[out]   scaled_lagrange_multiplier    Scaled Lagrange multiplier for each constraint (-2 * eta from p. 336
 - *                                             of the paper, divided by the constraint distance)
 - * \param[out]   nerror                        Zero upon success, returns one more than the index of the
 - *                                             problematic constraint if the input was malformed
 + * \param[out]   scaled_lagrange_multiplier    Scaled Lagrange multiplier for each constraint
 + *                                             (-2 * eta from p. 336 of the paper, divided by
 + *                                             the constraint distance)
 + * \param[out]   nerror                        Zero upon success, returns one more than the index of
 + *                                             the problematic constraint if the input was malformed
   *
   * \todo Make SHAKE use better data structures, in particular for iatom. */
 -void cshake(const int  iatom[],
 -            int        ncon,
 -            int*       nnit,
 -            int        maxnit,
 -            const real constraint_distance_squared[],
 -            real       positions[],
 -            const real initial_displacements[],
 -            const real half_of_reduced_mass[],
 -            real       omega,
 -            const real invmass[],
 -            const real distance_squared_tolerance[],
 -            real       scaled_lagrange_multiplier[],
 -            int*       nerror)
 +void cshake(const int            iatom[],
 +            int                  ncon,
 +            int*                 nnit,
 +            int                  maxnit,
 +            ArrayRef<const real> constraint_distance_squared,
 +            ArrayRef<RVec>       positions,
 +            const t_pbc*         pbc,
 +            ArrayRef<const RVec> initial_displacements,
 +            ArrayRef<const real> half_of_reduced_mass,
 +            real                 omega,
 +            const real           invmass[],
 +            ArrayRef<const real> distance_squared_tolerance,
 +            ArrayRef<real>       scaled_lagrange_multiplier,
 +            int*                 nerror)
  {
      /* default should be increased! MRS 8/4/2009 */
      const real mytol = 1e-10;
  
 -    int  ll, i, j, i3, j3, l3;
 -    int  ix, iy, iz, jx, jy, jz;
 -    real r_dot_r_prime;
 -    real constraint_distance_squared_ll;
 -    real r_prime_squared;
 -    real scaled_lagrange_multiplier_ll;
 -    real r_prime_x, r_prime_y, r_prime_z, diff, im, jm;
 -    real xh, yh, zh, rijx, rijy, rijz;
 -    int  nit, error, nconv;
 -    real iconvf;
 -
      // TODO nconv is used solely as a boolean, so we should write the
      // code like that
 -    error = 0;
 -    nconv = 1;
 +    int error = 0;
 +    int nconv = 1;
 +    int nit;
      for (nit = 0; (nit < maxnit) && (nconv != 0) && (error == 0); nit++)
      {
          nconv = 0;
 -        for (ll = 0; (ll < ncon) && (error == 0); ll++)
 +        for (int ll = 0; (ll < ncon) && (error == 0); ll++)
          {
 -            l3   = 3 * ll;
 -            rijx = initial_displacements[l3 + XX];
 -            rijy = initial_displacements[l3 + YY];
 -            rijz = initial_displacements[l3 + ZZ];
 -            i    = iatom[l3 + 1];
 -            j    = iatom[l3 + 2];
 -            i3   = 3 * i;
 -            j3   = 3 * j;
 -            ix   = i3 + XX;
 -            iy   = i3 + YY;
 -            iz   = i3 + ZZ;
 -            jx   = j3 + XX;
 -            jy   = j3 + YY;
 -            jz   = j3 + ZZ;
 +            const int  l3   = 3 * ll;
 +            const real rijx = initial_displacements[ll][XX];
 +            const real rijy = initial_displacements[ll][YY];
 +            const real rijz = initial_displacements[ll][ZZ];
 +            const int  i    = iatom[l3 + 1];
 +            const int  j    = iatom[l3 + 2];
  
              /* Compute r prime between atoms i and j, which is the
                 displacement *before* this update stage */
 -            r_prime_x       = positions[ix] - positions[jx];
 -            r_prime_y       = positions[iy] - positions[jy];
 -            r_prime_z       = positions[iz] - positions[jz];
 -            r_prime_squared = (r_prime_x * r_prime_x + r_prime_y * r_prime_y + r_prime_z * r_prime_z);
 -            constraint_distance_squared_ll = constraint_distance_squared[ll];
 -            diff                           = constraint_distance_squared_ll - r_prime_squared;
 +            rvec r_prime;
 +            if (pbc)
 +            {
 +                pbc_dx(pbc, positions[i], positions[j], r_prime);
 +            }
 +            else
 +            {
 +                rvec_sub(positions[i], positions[j], r_prime);
 +            }
 +            const real r_prime_squared                = norm2(r_prime);
 +            const real constraint_distance_squared_ll = constraint_distance_squared[ll];
 +            const real diff = constraint_distance_squared_ll - r_prime_squared;
  
              /* iconvf is less than 1 when the error is smaller than a bound */
 -            iconvf = fabs(diff) * distance_squared_tolerance[ll];
 +            const real iconvf = std::abs(diff) * distance_squared_tolerance[ll];
  
 -            if (iconvf > 1.0)
 +            if (iconvf > 1.0_real)
              {
 -                nconv         = static_cast<int>(iconvf);
 -                r_dot_r_prime = (rijx * r_prime_x + rijy * r_prime_y + rijz * r_prime_z);
 +                nconv = static_cast<int>(iconvf);
 +                const real r_dot_r_prime =
 +                        (rijx * r_prime[XX] + rijy * r_prime[YY] + rijz * r_prime[ZZ]);
  
                  if (r_dot_r_prime < constraint_distance_squared_ll * mytol)
                  {
                  {
                      /* The next line solves equation 5.6 (neglecting
                         the term in g^2), for g */
 -                    scaled_lagrange_multiplier_ll = omega * diff * half_of_reduced_mass[ll] / r_dot_r_prime;
 +                    real scaled_lagrange_multiplier_ll =
 +                            omega * diff * half_of_reduced_mass[ll] / r_dot_r_prime;
                      scaled_lagrange_multiplier[ll] += scaled_lagrange_multiplier_ll;
 -                    xh = rijx * scaled_lagrange_multiplier_ll;
 -                    yh = rijy * scaled_lagrange_multiplier_ll;
 -                    zh = rijz * scaled_lagrange_multiplier_ll;
 -                    im = invmass[i];
 -                    jm = invmass[j];
 -                    positions[ix] += xh * im;
 -                    positions[iy] += yh * im;
 -                    positions[iz] += zh * im;
 -                    positions[jx] -= xh * jm;
 -                    positions[jy] -= yh * jm;
 -                    positions[jz] -= zh * jm;
 +                    const real xh = rijx * scaled_lagrange_multiplier_ll;
 +                    const real yh = rijy * scaled_lagrange_multiplier_ll;
 +                    const real zh = rijz * scaled_lagrange_multiplier_ll;
 +                    const real im = invmass[i];
 +                    const real jm = invmass[j];
 +                    positions[i][XX] += xh * im;
 +                    positions[i][YY] += yh * im;
 +                    positions[i][ZZ] += zh * im;
 +                    positions[j][XX] -= xh * jm;
 +                    positions[j][YY] -= yh * jm;
 +                    positions[j][ZZ] -= zh * jm;
                  }
              }
          }
  }
  
  //! Implements RATTLE (ie. SHAKE for velocity verlet integrators)
 -static void crattle(const int  iatom[],
 -                    int        ncon,
 -                    int*       nnit,
 -                    int        maxnit,
 -                    const real constraint_distance_squared[],
 -                    real       vp[],
 -                    const real rij[],
 -                    const real m2[],
 -                    real       omega,
 -                    const real invmass[],
 -                    const real distance_squared_tolerance[],
 -                    real       scaled_lagrange_multiplier[],
 -                    int*       nerror,
 -                    real       invdt)
 +static void crattle(const int            iatom[],
 +                    int                  ncon,
 +                    int*                 nnit,
 +                    int                  maxnit,
 +                    ArrayRef<const real> constraint_distance_squared,
 +                    ArrayRef<RVec>       vp,
 +                    ArrayRef<const RVec> rij,
 +                    ArrayRef<const real> m2,
 +                    real                 omega,
 +                    const real           invmass[],
 +                    ArrayRef<const real> distance_squared_tolerance,
 +                    ArrayRef<real>       scaled_lagrange_multiplier,
 +                    int*                 nerror,
 +                    real                 invdt)
  {
      /*
       *     r.c. van schaik and w.f. van gunsteren
       *     second part of rattle algorithm
       */
  
 -    int  ll, i, j, i3, j3, l3;
 -    int  ix, iy, iz, jx, jy, jz;
 -    real constraint_distance_squared_ll;
 -    real vpijd, vx, vy, vz, acor, fac, im, jm;
 -    real xh, yh, zh, rijx, rijy, rijz;
 -    int  nit, error, nconv;
 -    real iconvf;
 -
      // TODO nconv is used solely as a boolean, so we should write the
      // code like that
 -    error = 0;
 -    nconv = 1;
 +    int error = 0;
 +    int nconv = 1;
 +    int nit;
      for (nit = 0; (nit < maxnit) && (nconv != 0) && (error == 0); nit++)
      {
          nconv = 0;
 -        for (ll = 0; (ll < ncon) && (error == 0); ll++)
 +        for (int ll = 0; (ll < ncon) && (error == 0); ll++)
          {
 -            l3   = 3 * ll;
 -            rijx = rij[l3 + XX];
 -            rijy = rij[l3 + YY];
 -            rijz = rij[l3 + ZZ];
 -            i    = iatom[l3 + 1];
 -            j    = iatom[l3 + 2];
 -            i3   = 3 * i;
 -            j3   = 3 * j;
 -            ix   = i3 + XX;
 -            iy   = i3 + YY;
 -            iz   = i3 + ZZ;
 -            jx   = j3 + XX;
 -            jy   = j3 + YY;
 -            jz   = j3 + ZZ;
 -            vx   = vp[ix] - vp[jx];
 -            vy   = vp[iy] - vp[jy];
 -            vz   = vp[iz] - vp[jz];
 -
 -            vpijd                          = vx * rijx + vy * rijy + vz * rijz;
 -            constraint_distance_squared_ll = constraint_distance_squared[ll];
 +            const int  l3   = 3 * ll;
 +            const real rijx = rij[ll][XX];
 +            const real rijy = rij[ll][YY];
 +            const real rijz = rij[ll][ZZ];
 +            const int  i    = iatom[l3 + 1];
 +            const int  j    = iatom[l3 + 2];
 +            rvec       v;
 +            rvec_sub(vp[i], vp[j], v);
 +
 +            const real vpijd                          = v[XX] * rijx + v[YY] * rijy + v[ZZ] * rijz;
 +            const real constraint_distance_squared_ll = constraint_distance_squared[ll];
  
              /* iconv is zero when the error is smaller than a bound */
 -            iconvf = fabs(vpijd) * (distance_squared_tolerance[ll] / invdt);
 +            const real iconvf = fabs(vpijd) * (distance_squared_tolerance[ll] / invdt);
  
 -            if (iconvf > 1)
 +            if (iconvf > 1.0_real)
              {
 -                nconv = static_cast<int>(iconvf);
 -                fac   = omega * 2.0 * m2[ll] / constraint_distance_squared_ll;
 -                acor  = -fac * vpijd;
 +                nconv           = static_cast<int>(iconvf);
 +                const real fac  = omega * 2.0_real * m2[ll] / constraint_distance_squared_ll;
 +                const real acor = -fac * vpijd;
                  scaled_lagrange_multiplier[ll] += acor;
 -                xh = rijx * acor;
 -                yh = rijy * acor;
 -                zh = rijz * acor;
 -
 -                im = invmass[i];
 -                jm = invmass[j];
 -
 -                vp[ix] += xh * im;
 -                vp[iy] += yh * im;
 -                vp[iz] += zh * im;
 -                vp[jx] -= xh * jm;
 -                vp[jy] -= yh * jm;
 -                vp[jz] -= zh * jm;
 +                const real xh = rijx * acor;
 +                const real yh = rijy * acor;
 +                const real zh = rijz * acor;
 +
 +                const real im = invmass[i];
 +                const real jm = invmass[j];
 +
 +                vp[i][XX] += xh * im;
 +                vp[i][YY] += yh * im;
 +                vp[i][ZZ] += zh * im;
 +                vp[j][XX] -= xh * jm;
 +                vp[j][YY] -= yh * jm;
 +                vp[j][ZZ] -= zh * jm;
              }
          }
      }
  }
  
  //! Applies SHAKE
 -static int vec_shakef(FILE*              fplog,
 -                      shakedata*         shaked,
 -                      const real         invmass[],
 -                      int                ncon,
 -                      t_iparams          ip[],
 -                      t_iatom*           iatom,
 -                      real               tol,
 -                      const rvec         x[],
 -                      rvec               prime[],
 -                      real               omega,
 -                      bool               bFEP,
 -                      real               lambda,
 -                      real               scaled_lagrange_multiplier[],
 -                      real               invdt,
 -                      rvec*              v,
 -                      bool               bCalcVir,
 -                      tensor             vir_r_m_dr,
 -                      ConstraintVariable econq)
 +static int vec_shakef(FILE*                     fplog,
 +                      shakedata*                shaked,
 +                      const real                invmass[],
 +                      int                       ncon,
 +                      ArrayRef<const t_iparams> ip,
 +                      const int*                iatom,
 +                      real                      tol,
 +                      ArrayRef<const RVec>      x,
 +                      ArrayRef<RVec>            prime,
 +                      const t_pbc*              pbc,
 +                      real                      omega,
 +                      bool                      bFEP,
 +                      real                      lambda,
 +                      ArrayRef<real>            scaled_lagrange_multiplier,
 +                      real                      invdt,
 +                      ArrayRef<RVec>            v,
 +                      bool                      bCalcVir,
 +                      tensor                    vir_r_m_dr,
 +                      ConstraintVariable        econq)
  {
 -    rvec*    rij;
 -    real *   half_of_reduced_mass, *distance_squared_tolerance, *constraint_distance_squared;
 -    int      maxnit = 1000;
 -    int      nit    = 0, ll, i, j, d, d2, type;
 -    t_iatom* ia;
 -    real     L1;
 -    real     mm    = 0., tmp;
 -    int      error = 0;
 -    real     constraint_distance;
 -
 -    if (ncon > shaked->nalloc)
 -    {
 -        shaked->nalloc = over_alloc_dd(ncon);
 -        srenew(shaked->rij, shaked->nalloc);
 -        srenew(shaked->half_of_reduced_mass, shaked->nalloc);
 -        srenew(shaked->distance_squared_tolerance, shaked->nalloc);
 -        srenew(shaked->constraint_distance_squared, shaked->nalloc);
 -    }
 -    rij                         = shaked->rij;
 -    half_of_reduced_mass        = shaked->half_of_reduced_mass;
 -    distance_squared_tolerance  = shaked->distance_squared_tolerance;
 -    constraint_distance_squared = shaked->constraint_distance_squared;
 -
 -    L1 = 1.0 - lambda;
 -    ia = iatom;
 +    int  maxnit = 1000;
 +    int  nit    = 0, ll, i, j, d, d2, type;
 +    real L1;
 +    int  error = 0;
 +    real constraint_distance;
 +
 +    shaked->rij.resize(ncon);
 +    shaked->half_of_reduced_mass.resize(ncon);
 +    shaked->distance_squared_tolerance.resize(ncon);
 +    shaked->constraint_distance_squared.resize(ncon);
 +
 +    ArrayRef<RVec> rij                         = shaked->rij;
 +    ArrayRef<real> half_of_reduced_mass        = shaked->half_of_reduced_mass;
 +    ArrayRef<real> distance_squared_tolerance  = shaked->distance_squared_tolerance;
 +    ArrayRef<real> constraint_distance_squared = shaked->constraint_distance_squared;
 +
 +    L1            = 1.0_real - lambda;
 +    const int* ia = iatom;
      for (ll = 0; (ll < ncon); ll++, ia += 3)
      {
          type = ia[0];
          i    = ia[1];
          j    = ia[2];
  
 -        mm                       = 2.0 * (invmass[i] + invmass[j]);
 -        rij[ll][XX]              = x[i][XX] - x[j][XX];
 -        rij[ll][YY]              = x[i][YY] - x[j][YY];
 -        rij[ll][ZZ]              = x[i][ZZ] - x[j][ZZ];
 -        half_of_reduced_mass[ll] = 1.0 / mm;
 +        if (pbc)
 +        {
 +            pbc_dx(pbc, x[i], x[j], rij[ll]);
 +        }
 +        else
 +        {
 +            rvec_sub(x[i], x[j], rij[ll]);
 +        }
 +        const real mm            = 2.0_real * (invmass[i] + invmass[j]);
 +        half_of_reduced_mass[ll] = 1.0_real / mm;
          if (bFEP)
          {
              constraint_distance = L1 * ip[type].constr.dA + lambda * ip[type].constr.dB;
      switch (econq)
      {
          case ConstraintVariable::Positions:
 -            cshake(iatom, ncon, &nit, maxnit, constraint_distance_squared, prime[0], rij[0],
 +            cshake(iatom, ncon, &nit, maxnit, constraint_distance_squared, prime, pbc, rij,
                     half_of_reduced_mass, omega, invmass, distance_squared_tolerance,
                     scaled_lagrange_multiplier, &error);
              break;
          case ConstraintVariable::Velocities:
 -            crattle(iatom, ncon, &nit, maxnit, constraint_distance_squared, prime[0], rij[0],
 +            crattle(iatom, ncon, &nit, maxnit, constraint_distance_squared, prime, rij,
                      half_of_reduced_mass, omega, invmass, distance_squared_tolerance,
                      scaled_lagrange_multiplier, &error, invdt);
              break;
          i    = ia[1];
          j    = ia[2];
  
 -        if ((econq == ConstraintVariable::Positions) && v != nullptr)
 +        if ((econq == ConstraintVariable::Positions) && !v.empty())
          {
              /* Correct the velocities */
 -            mm = scaled_lagrange_multiplier[ll] * invmass[i] * invdt;
 +            real mm = scaled_lagrange_multiplier[ll] * invmass[i] * invdt;
              for (d = 0; d < DIM; d++)
              {
                  v[ia[1]][d] += mm * rij[ll][d];
          /* constraint virial */
          if (bCalcVir)
          {
 -            mm = scaled_lagrange_multiplier[ll];
 +            const real mm = scaled_lagrange_multiplier[ll];
              for (d = 0; d < DIM; d++)
              {
 -                tmp = mm * rij[ll][d];
 +                const real tmp = mm * rij[ll][d];
                  for (d2 = 0; d2 < DIM; d2++)
                  {
                      vir_r_m_dr[d][d2] -= tmp * rij[ll][d2];
  }
  
  //! Check that constraints are satisfied.
 -static void check_cons(FILE*              log,
 -                       int                nc,
 -                       const rvec         x[],
 -                       rvec               prime[],
 -                       rvec               v[],
 -                       t_iparams          ip[],
 -                       t_iatom*           iatom,
 -                       const real         invmass[],
 -                       ConstraintVariable econq)
 +static void check_cons(FILE*                     log,
 +                       int                       nc,
 +                       ArrayRef<const RVec>      x,
 +                       ArrayRef<const RVec>      prime,
 +                       ArrayRef<const RVec>      v,
 +                       const t_pbc*              pbc,
 +                       ArrayRef<const t_iparams> ip,
 +                       const int*                iatom,
 +                       const real                invmass[],
 +                       ConstraintVariable        econq)
  {
 -    t_iatom* ia;
 -    int      ai, aj;
 -    int      i;
 -    real     d, dp;
 -    rvec     dx, dv;
 +    int  ai, aj;
 +    int  i;
 +    real d, dp;
 +    rvec dx, dv;
  
 -    GMX_ASSERT(v, "Input has to be non-null");
 +    GMX_ASSERT(!v.empty(), "Input has to be non-null");
      fprintf(log, "    i     mi      j     mj      before       after   should be\n");
 -    ia = iatom;
 +    const int* ia = iatom;
      for (i = 0; (i < nc); i++, ia += 3)
      {
          ai = ia[1];
          switch (econq)
          {
              case ConstraintVariable::Positions:
 -                rvec_sub(prime[ai], prime[aj], dx);
 +                if (pbc)
 +                {
 +                    pbc_dx(pbc, prime[ai], prime[aj], dx);
 +                }
 +                else
 +                {
 +                    rvec_sub(prime[ai], prime[aj], dx);
 +                }
                  dp = norm(dx);
                  fprintf(log, "%5d  %5.2f  %5d  %5.2f  %10.5f  %10.5f  %10.5f\n", ai + 1,
                          1.0 / invmass[ai], aj + 1, 1.0 / invmass[aj], d, dp, ip[ia[0]].constr.dA);
  }
  
  //! Applies SHAKE.
 -static bool bshakef(FILE*              log,
 -                    shakedata*         shaked,
 -                    const real         invmass[],
 -                    const t_idef&      idef,
 -                    const t_inputrec&  ir,
 -                    const rvec         x_s[],
 -                    rvec               prime[],
 -                    t_nrnb*            nrnb,
 -                    real               lambda,
 -                    real*              dvdlambda,
 -                    real               invdt,
 -                    rvec*              v,
 -                    bool               bCalcVir,
 -                    tensor             vir_r_m_dr,
 -                    bool               bDumpOnError,
 -                    ConstraintVariable econq)
 +static bool bshakef(FILE*                         log,
 +                    shakedata*                    shaked,
 +                    const real                    invmass[],
 +                    const InteractionDefinitions& idef,
 +                    const t_inputrec&             ir,
 +                    ArrayRef<const RVec>          x_s,
 +                    ArrayRef<RVec>                prime,
 +                    const t_pbc*                  pbc,
 +                    t_nrnb*                       nrnb,
 +                    real                          lambda,
 +                    real*                         dvdlambda,
 +                    real                          invdt,
 +                    ArrayRef<RVec>                v,
 +                    bool                          bCalcVir,
 +                    tensor                        vir_r_m_dr,
 +                    bool                          bDumpOnError,
 +                    ConstraintVariable            econq)
  {
 -    t_iatom* iatoms;
 -    real *   lam, dt_2, dvdl;
 -    int      i, n0, ncon, blen, type, ll;
 -    int      tnit = 0, trij = 0;
 +    real dt_2, dvdl;
 +    int  i, n0, ncon, blen, type, ll;
 +    int  tnit = 0, trij = 0;
  
 -    ncon = idef.il[F_CONSTR].nr / 3;
 +    ncon = idef.il[F_CONSTR].size() / 3;
  
      for (ll = 0; ll < ncon; ll++)
      {
      // TODO Rewrite this block so that it is obvious that i, iatoms
      // and lam are all iteration variables. Is this easier if the
      // sblock data structure is organized differently?
 -    iatoms = &(idef.il[F_CONSTR].iatoms[shaked->sblock[0]]);
 -    lam    = shaked->scaled_lagrange_multiplier;
 -    for (i = 0; (i < shaked->nblocks);)
 +    const int*     iatoms = &(idef.il[F_CONSTR].iatoms[shaked->sblock[0]]);
 +    ArrayRef<real> lam    = shaked->scaled_lagrange_multiplier;
 +    for (i = 0; (i < shaked->numShakeBlocks());)
      {
          blen = (shaked->sblock[i + 1] - shaked->sblock[i]);
          blen /= 3;
          n0 = vec_shakef(log, shaked, invmass, blen, idef.iparams, iatoms, ir.shake_tol, x_s, prime,
 -                        shaked->omega, ir.efep != efepNO, lambda, lam, invdt, v, bCalcVir,
 +                        pbc, shaked->omega, ir.efep != efepNO, lambda, lam, invdt, v, bCalcVir,
                          vir_r_m_dr, econq);
  
          if (n0 == 0)
              if (bDumpOnError && log)
              {
                  {
 -                    check_cons(log, blen, x_s, prime, v, idef.iparams, iatoms, invmass, econq);
 +                    check_cons(log, blen, x_s, prime, v, pbc, idef.iparams, iatoms, invmass, econq);
                  }
              }
              return FALSE;
          tnit += n0 * blen;
          trij += blen;
          iatoms += 3 * blen; /* Increment pointer! */
 -        lam += blen;
 +        lam = lam.subArray(blen, lam.ssize() - blen);
          i++;
      }
      /* only for position part? */
      {
          if (ir.efep != efepNO)
          {
 -            real bondA, bondB;
 +            ArrayRef<const t_iparams> iparams = idef.iparams;
 +
              /* TODO This should probably use invdt, so that sd integrator scaling works properly */
              dt_2 = 1 / gmx::square(ir.delta_t);
              dvdl = 0;
                  /* The vector scaled_lagrange_multiplier[ll] contains the value -2 r_ll eta_ll
                     (eta_ll is the estimate of the Langrangian, definition on page 336 of Ryckaert et
                     al 1977), so the pre-factors are already present. */
 -                bondA = idef.iparams[type].constr.dA;
 -                bondB = idef.iparams[type].constr.dB;
 +                const real bondA = iparams[type].constr.dA;
 +                const real bondB = iparams[type].constr.dB;
                  dvdl += shaked->scaled_lagrange_multiplier[ll] * dt_2 * (bondB - bondA);
              }
              *dvdlambda += dvdl;
      }
      inc_nrnb(nrnb, eNR_SHAKE, tnit);
      inc_nrnb(nrnb, eNR_SHAKE_RIJ, trij);
 -    if (v)
 +    if (!v.empty())
      {
          inc_nrnb(nrnb, eNR_CONSTR_V, trij * 2);
      }
      return TRUE;
  }
  
 -bool constrain_shake(FILE*              log,
 -                     shakedata*         shaked,
 -                     const real         invmass[],
 -                     const t_idef&      idef,
 -                     const t_inputrec&  ir,
 -                     const rvec         x_s[],
 -                     rvec               xprime[],
 -                     rvec               vprime[],
 -                     t_nrnb*            nrnb,
 -                     real               lambda,
 -                     real*              dvdlambda,
 -                     real               invdt,
 -                     rvec*              v,
 -                     bool               bCalcVir,
 -                     tensor             vir_r_m_dr,
 -                     bool               bDumpOnError,
 -                     ConstraintVariable econq)
 +bool constrain_shake(FILE*                         log,
 +                     shakedata*                    shaked,
 +                     const real                    invmass[],
 +                     const InteractionDefinitions& idef,
 +                     const t_inputrec&             ir,
 +                     ArrayRef<const RVec>          x_s,
 +                     ArrayRef<RVec>                xprime,
 +                     ArrayRef<RVec>                vprime,
 +                     const t_pbc*                  pbc,
 +                     t_nrnb*                       nrnb,
 +                     real                          lambda,
 +                     real*                         dvdlambda,
 +                     real                          invdt,
 +                     ArrayRef<RVec>                v,
 +                     bool                          bCalcVir,
 +                     tensor                        vir_r_m_dr,
 +                     bool                          bDumpOnError,
 +                     ConstraintVariable            econq)
  {
 -    if (shaked->nblocks == 0)
 +    if (shaked->numShakeBlocks() == 0)
      {
          return true;
      }
      switch (econq)
      {
          case (ConstraintVariable::Positions):
 -            bOK = bshakef(log, shaked, invmass, idef, ir, x_s, xprime, nrnb, lambda, dvdlambda,
 +            bOK = bshakef(log, shaked, invmass, idef, ir, x_s, xprime, pbc, nrnb, lambda, dvdlambda,
                            invdt, v, bCalcVir, vir_r_m_dr, bDumpOnError, econq);
              break;
          case (ConstraintVariable::Velocities):
 -            bOK = bshakef(log, shaked, invmass, idef, ir, x_s, vprime, nrnb, lambda, dvdlambda,
 -                          invdt, nullptr, bCalcVir, vir_r_m_dr, bDumpOnError, econq);
 +            bOK = bshakef(log, shaked, invmass, idef, ir, x_s, vprime, pbc, nrnb, lambda, dvdlambda,
 +                          invdt, {}, bCalcVir, vir_r_m_dr, bDumpOnError, econq);
              break;
          default:
              gmx_fatal(FARGS,