Merge branch release-2019 into release-2020
authorMark Abraham <mark.j.abraham@gmail.com>
Fri, 29 Nov 2019 12:52:13 +0000 (13:52 +0100)
committerMark Abraham <mark.j.abraham@gmail.com>
Fri, 29 Nov 2019 12:56:44 +0000 (13:56 +0100)
Change-Id: I138d910b0d5cb8555460f87345c4e312e528a3ce

1  2 
docs/user-guide/mdp-options.rst
src/gromacs/awh/biasparams.h
src/gromacs/awh/read_params.cpp
src/gromacs/fileio/pdbio.cpp
src/gromacs/pulling/pull.cpp

index 35d391bffa722c1376324991f845e29d4131706e,e1af80503e2bb93c3b73494cb6f530ca8718c606..b9901f7b70b1eb1c670721119422b0759ce157bd
@@@ -64,7 -64,7 +64,7 @@@ Run contro
        of motion.  For constant NVE simulations started from
        corresponding points in the same trajectory, the trajectories
        are analytically, but not binary, identical to the
 -      :mdp-value:`integrator=md` leap-frog integrator. The the kinetic
 +      :mdp-value:`integrator=md` leap-frog integrator. The kinetic
        energy, which is determined from the whole step velocities and
        is therefore slightly too high. The advantage of this integrator
        is more accurate, reversible Nose-Hoover and Parrinello-Rahman
@@@ -94,8 -94,8 +94,8 @@@
        significant part of the simulation time. The temperature for one
        or more groups of atoms (:mdp:`tc-grps`) is set with
        :mdp:`ref-t`, the inverse friction constant for each group is
 -      set with :mdp:`tau-t`.  The parameter :mdp:`tcoupl` is
 -      ignored. The random generator is initialized with
 +      set with :mdp:`tau-t`.  The parameters :mdp:`tcoupl` and :mdp:`nsttcouple`
 +      are ignored. The random generator is initialized with
        :mdp:`ld-seed`. When used as a thermostat, an appropriate value
        for :mdp:`tau-t` is 2 ps, since this results in a friction that
        is lower than the internal friction of water, while it is high
@@@ -428,13 -428,29 +428,13 @@@ Neighbor searchin
        Generate a pair list with buffering. The buffer size is
        automatically set based on :mdp:`verlet-buffer-tolerance`,
        unless this is set to -1, in which case :mdp:`rlist` will be
 -      used. This option has an explicit, exact cut-off at :mdp:`rvdw`
 -      equal to :mdp:`rcoulomb`, unless PME or Ewald is used, in which
 -      case :mdp:`rcoulomb` > :mdp:`rvdw` is allowed. Currently only
 -      cut-off, reaction-field, PME or Ewald electrostatics and plain
 -      LJ are supported. Some :ref:`gmx mdrun` functionality is not yet
 -      supported with the :mdp-value:`cutoff-scheme=Verlet` scheme, but :ref:`gmx grompp`
 -      checks for this. Native GPU acceleration is only supported with
 -      :mdp-value:`cutoff-scheme=Verlet`. With GPU-accelerated PME or with separate PME
 -      ranks, :ref:`gmx mdrun` will automatically tune the CPU/GPU load
 -      balance by scaling :mdp:`rcoulomb` and the grid spacing. This
 -      can be turned off with ``mdrun -notunepme``. :mdp-value:`cutoff-scheme=Verlet` is
 -      faster than :mdp-value:`cutoff-scheme=group` when there is no water, or if
 -      :mdp-value:`cutoff-scheme=group` would use a pair-list buffer to conserve energy.
 +      used.
  
     .. mdp-value:: group
  
 -      Generate a pair list for groups of atoms. These groups
 -      correspond to the charge groups in the topology. This was the
 -      only cut-off treatment scheme before version 4.6, and is
 -      **deprecated since 5.1**. There is no explicit buffering of
 -      the pair list. This enables efficient force calculations for
 -      water, but energy is only conserved when a buffer is explicitly
 -      added.
 +      Generate a pair list for groups of atoms, corresponding
 +      to the charge groups in the topology. This option is no longer
 +      supported.
  
  .. mdp:: nstlist
  
  
     .. mdp-value:: >0
  
 -      Frequency to update the neighbor list. When this is 0, the
 -      neighbor list is made only once. With energy minimization the
 -      pair list will be updated for every energy evaluation when
 -      :mdp:`nstlist` is greater than 0. With :mdp-value:`cutoff-scheme=Verlet` and
 +      Frequency to update the neighbor list. When dynamics and
        :mdp:`verlet-buffer-tolerance` set, :mdp:`nstlist` is actually
        a minimum value and :ref:`gmx mdrun` might increase it, unless
        it is set to 1. With parallel simulations and/or non-bonded
        force calculation on the GPU, a value of 20 or 40 often gives
 -      the best performance. With :mdp-value:`cutoff-scheme=group` and non-exact
 -      cut-off's, :mdp:`nstlist` will affect the accuracy of your
 -      simulation and it can not be chosen freely.
 +      the best performance.
  
     .. mdp-value:: 0
  
        The neighbor list is only constructed once and never
        updated. This is mainly useful for vacuum simulations in which
 -      all particles see each other.
 +      all particles see each other. But vacuum simulations are
 +      (temporarily) not supported.
  
     .. mdp-value:: <0
  
        Unused.
  
 -.. mdp:: ns-type
 -
 -   .. mdp-value:: grid
 -
 -      Make a grid in the box and only check atoms in neighboring grid
 -      cells when constructing a new neighbor list every
 -      :mdp:`nstlist` steps. In large systems grid search is much
 -      faster than simple search.
 -
 -   .. mdp-value:: simple
 -
 -      Check every atom in the box when constructing a new neighbor
 -      list every :mdp:`nstlist` steps (only with :mdp-value:`cutoff-scheme=group`
 -      cut-off scheme).
 -
  .. mdp:: pbc
  
     .. mdp-value:: xyz
  
     (0.005) [kJ mol\ :sup:`-1` ps\ :sup:`-1`]
  
 -   Useful only with the :mdp-value:`cutoff-scheme=Verlet` :mdp:`cutoff-scheme`. This sets
 +   Used when performing a simulation with dynamics. This sets
     the maximum allowed error for pair interactions per particle caused
     by the Verlet buffer, which indirectly sets :mdp:`rlist`. As both
     :mdp:`nstlist` and the Verlet buffer size are fixed (for
  .. mdp:: rlist
  
     (1) [nm]
 -   Cut-off distance for the short-range neighbor list. With the
 -   :mdp-value:`cutoff-scheme=Verlet` :mdp:`cutoff-scheme`, this is by default set by the
 -   :mdp:`verlet-buffer-tolerance` option and the value of
 -   :mdp:`rlist` is ignored.
 +   Cut-off distance for the short-range neighbor list. With dynamics,
 +   this is by default set by the :mdp:`verlet-buffer-tolerance` option
 +   and the value of :mdp:`rlist` is ignored. Without dynamics, this
 +   is by default set to the maximum cut-off plus 5% buffer, except
 +   for test particle insertion, where the buffer is managed exactly
 +   and automatically. For NVE simulations, where the automated
 +   setting is not possible, the advised procedure is to run :ref:`gmx grompp`
 +   with an NVT setup with the expected temperature and copy the resulting
 +   value of :mdp:`rlist` to the NVE setup.
  
  
  Electrostatics
        :mdp:`epsilon-rf`. The dielectric constant can be set to
        infinity by setting :mdp:`epsilon-rf` =0.
  
 -   .. mdp-value:: Generalized-Reaction-Field
 -
 -      Generalized reaction field with Coulomb cut-off
 -      :mdp:`rcoulomb`, where :mdp:`rlist` >= :mdp:`rcoulomb`. The
 -      dielectric constant beyond the cut-off is
 -      :mdp:`epsilon-rf`. The ionic strength is computed from the
 -      number of charged (*i.e.* with non zero charge) charge
 -      groups. The temperature for the GRF potential is set with
 -      :mdp:`ref-t`.
 -
 -   .. mdp-value:: Reaction-Field-zero
 -
 -      In |Gromacs|, normal reaction-field electrostatics with
 -      :mdp-value:`cutoff-scheme=group` leads to bad energy
 -      conservation. :mdp-value:`coulombtype=Reaction-Field-zero` solves this by making
 -      the potential zero beyond the cut-off. It can only be used with
 -      an infinite dielectric constant (:mdp:`epsilon-rf` =0), because
 -      only for that value the force vanishes at the
 -      cut-off. :mdp:`rlist` should be 0.1 to 0.3 nm larger than
 -      :mdp:`rcoulomb` to accommodate the size of charge groups
 -      and diffusion between neighbor list updates. This, and the fact
 -      that table lookups are used instead of analytical functions make
 -      reaction-field-zero computationally more expensive than
 -      normal reaction-field.
 -
 -   .. mdp-value:: Shift
 -
 -      Analogous to :mdp-value:`vdwtype=Shift` for :mdp:`vdwtype`. You
 -      might want to use :mdp-value:`coulombtype=Reaction-Field-zero` instead, which has
 -      a similar potential shape, but has a physical interpretation and
 -      has better energies due to the exclusion correction terms.
 -
 -   .. mdp-value:: Encad-Shift
 -
 -      The Coulomb potential is decreased over the whole range, using
 -      the definition from the Encad simulation package.
 -
 -   .. mdp-value:: Switch
 -
 -      Analogous to :mdp-value:`vdwtype=Switch` for
 -      :mdp:`vdwtype`. Switching the Coulomb potential can lead to
 -      serious artifacts, advice: use :mdp-value:`coulombtype=Reaction-Field-zero`
 -      instead.
 -
     .. mdp-value:: User
  
 +      Currently unsupported.
        :ref:`gmx mdrun` will now expect to find a file ``table.xvg``
        with user-defined potential functions for repulsion, dispersion
        and Coulomb. When pair interactions are present, :ref:`gmx
  
     .. mdp-value:: PME-Switch
  
 +      Currently unsupported.
        A combination of PME and a switch function for the direct-space
        part (see above). :mdp:`rcoulomb` is allowed to be smaller than
 -      :mdp:`rlist`. This is mainly useful constant energy simulations
 -      (note that using PME with :mdp-value:`cutoff-scheme=Verlet`
 -      will be more efficient).
 +      :mdp:`rlist`.
  
     .. mdp-value:: PME-User
  
 +      Currently unsupported.
        A combination of PME and user tables (see
        above). :mdp:`rcoulomb` is allowed to be smaller than
        :mdp:`rlist`. The PME mesh contribution is subtracted from the
  
     .. mdp-value:: PME-User-Switch
  
 +      Currently unsupported.
        A combination of PME-User and a switching function (see
        above). The switching function is applied to final
        particle-particle interaction, *i.e.* both to the user supplied
  
  .. mdp:: coulomb-modifier
  
 -   .. mdp-value:: Potential-shift-Verlet
 -
 -      Selects Potential-shift with the Verlet cutoff-scheme, as it is
 -      (nearly) free; selects None with the group cutoff-scheme.
 -
     .. mdp-value:: Potential-shift
  
        Shift the Coulomb potential by a constant such that it is zero
  
     .. mdp-value:: None
  
 -      Use an unmodified Coulomb potential. With the group scheme this
 -      means no exact cut-off is used, energies and forces are
 -      calculated for all pairs in the pair list.
 +      Use an unmodified Coulomb potential. This can be useful
 +      when comparing energies with those computed with other software.
  
  .. mdp:: rcoulomb-switch
  
@@@ -706,7 -784,10 +706,7 @@@ Van der Waal
        :mdp-value:`vdwtype=Cut-off` with :mdp-value:`vdw-modifier=Force-switch`.
        The LJ (not Buckingham) potential is decreased over the whole range and
        the forces decay smoothly to zero between :mdp:`rvdw-switch` and
 -      :mdp:`rvdw`. The neighbor search cut-off :mdp:`rlist` should
 -      be 0.1 to 0.3 nm larger than :mdp:`rvdw` to accommodate the
 -      size of charge groups and diffusion between neighbor list
 -      updates.
 +      :mdp:`rvdw`.
  
     .. mdp-value:: Switch
  
        potential and force functions are continuously smooth, but be
        aware that all switch functions will give rise to a bulge
        (increase) in the force (since we are switching the
 -      potential). The neighbor search cut-off :mdp:`rlist` should be
 -      0.1 to 0.3 nm larger than :mdp:`rvdw` to accommodate the
 -      size of charge groups and diffusion between neighbor list
 -      updates.
 -
 -   .. mdp-value:: Encad-Shift
 -
 -      The LJ (not Buckingham) potential is decreased over the whole
 -      range, using the definition from the Encad simulation package.
 +      potential).
  
     .. mdp-value:: User
  
 +      Currently unsupported.
        See user for :mdp:`coulombtype`. The function value at zero is
        not important. When you want to use LJ correction, make sure
        that :mdp:`rvdw` corresponds to the cut-off in the user-defined
  
  .. mdp:: vdw-modifier
  
 -   .. mdp-value:: Potential-shift-Verlet
 -
 -      Selects Potential-shift with the Verlet cutoff-scheme, as it is
 -      (nearly) free; selects None with the group cutoff-scheme.
 -
     .. mdp-value:: Potential-shift
  
        Shift the Van der Waals potential by a constant such that it is
  
     .. mdp-value:: None
  
 -      Use an unmodified Van der Waals potential. With the group scheme
 -      this means no exact cut-off is used, energies and forces are
 -      calculated for all pairs in the pair list.
 +      Use an unmodified Van der Waals potential. This can be useful
 +      when comparing energies with those computed with other software.
  
     .. mdp-value:: Force-switch
  
@@@ -792,15 -886,17 +792,15 @@@ Table
  
     (1) [nm]
     Extension of the non-bonded potential lookup tables beyond the
 -   largest cut-off distance. The value should be large enough to
 -   account for charge group sizes and the diffusion between
 -   neighbor-list updates. Without user defined potential the same
 -   table length is used for the lookup tables for the 1-4
 -   interactions, which are always tabulated irrespective of the use of
 -   tables for the non-bonded interactions. The value of
 -   :mdp:`table-extension` in no way affects the values of
 -   :mdp:`rlist`, :mdp:`rcoulomb`, or :mdp:`rvdw`.
 +   largest cut-off distance. With actual non-bonded interactions
 +   the tables are never accessed beyond the cut-off. But a longer
 +   table length might be needed for the 1-4 interactions, which
 +   are always tabulated irrespective of the use of tables for
 +   the non-bonded interactions.
  
  .. mdp:: energygrp-table
  
 +   Currently unsupported.
     When user tables are used for electrostatics and/or VdW, here one
     can give pairs of energy groups for which seperate user tables
     should be used. The two energy groups will be appended to the table
@@@ -979,7 -1075,7 +979,7 @@@ Temperature couplin
     integrators, the leap-frog :mdp-value:`integrator=md` integrator
     only supports 1. Data for the NH chain variables is not printed
     to the :ref:`edr` file by default, but can be turned on with the
 -   :mdp:`print-nose-hoover-chains` option.
 +   :mdp:`print-nose-hoover-chain-variables` option.
  
  .. mdp:: print-nose-hoover-chain-variables
  
@@@ -1308,11 -1404,10 +1308,11 @@@ Bond
        SHAKE is slightly slower and less stable than LINCS, but does
        work with angle constraints. The relative tolerance is set with
        :mdp:`shake-tol`, 0.0001 is a good value for "normal" MD. SHAKE
 -      does not support constraints between atoms on different nodes,
 -      thus it can not be used with domain decompositon when inter
 -      charge-group constraints are present. SHAKE can not be used with
 -      energy minimization.
 +      does not support constraints between atoms on different
 +      decomposition domains, so it can only be used with domain
 +      decomposition when so-called update-groups are used, which is
 +      usally the case when only bonds involving hydrogens are
 +      constrained. SHAKE can not be used with energy minimization.
  
  .. mdp:: continuation
  
@@@ -1672,10 -1767,13 +1672,13 @@@ pull-coord2-vec, pull-coord2-k, and so 
  
     .. mdp-value:: direction-periodic
  
-       As :mdp-value:`pull-coord1-geometry=direction`, but allows the distance to be larger
-       than half the box size. With this geometry the box should not be
+       As :mdp-value:`pull-coord1-geometry=direction`, but does not apply
+       periodic box vector corrections to keep the distance within half
+       the box length. This is (only) useful for pushing groups apart
+       by more than half the box length by continuously changing the reference
+       location using a pull rate. With this geometry the box should not be
        dynamic (*e.g.* no pressure scaling) in the pull dimensions and
-       the pull force is not added to virial.
+       the pull force is not added to the virial.
  
     .. mdp-value:: direction-relative
  
@@@ -2444,11 -2542,10 +2447,11 @@@ Free energy calculation
  .. mdp:: sc-r-power
  
     (6)
 -   the power of the radial term in the soft-core equation. Possible
 -   values are 6 and 48. 6 is more standard, and is the default. When
 -   48 is used, then sc-alpha should generally be much lower (between
 -   0.001 and 0.003).
 +   power 6 for the radial term in the soft-core equation.
 +
 +   (48)
 +   (deprecated) power 48 for the radial term in the soft-core equation. 
 +   Note that sc-alpha should generally be much lower (between 0.001 and 0.003).
  
  .. mdp:: sc-coul
  
@@@ -3198,103 -3295,6 +3201,103 @@@ Electrophysiology" simulation setups. (
  
     (1.0) [nm] Lower extension of the split cylinder #1.
  
 +Density-guided simulations
 +^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +These options enable and control the calculation and application of additional
 +forces that are derived from three-dimensional densities, e.g., from cryo
 +electron-microscopy experiments. (See the `reference manual`_ for details)
 +
 +.. mdp:: density-guided-simulation-active
 +
 +   (no) Activate density-guided simulations.
 +
 +.. mdp:: density-guided-simulation-group
 +
 +   (protein) The atoms that are subject to the forces from the density-guided
 +   simulation and contribute to the simulated density.
 +
 +.. mdp:: density-guided-simulation-similarity-measure
 +
 +   (inner-product) Similarity measure between the density that is calculated
 +   from the atom positions and the reference density.
 +
 +   .. mdp-value:: inner-product
 +
 +      Takes the sum of the product of reference density and simulated density
 +      voxel values.
 +
 +   .. mdp-value:: relative-entropy
 +
 +      Uses the negative relative entropy (or Kullback-Leibler divergence)
 +      between reference density and simulated density as similarity measure.
 +      Negative density values are ignored.
 +
 +.. mdp:: density-guided-simulation-atom-spreading-weight
 +
 +   (unity) Determines the multiplication factor for the Gaussian kernel when
 +   spreading atoms on the grid.
 +
 +   .. mdp-value:: unity
 +
 +      Every atom in the density fitting group is assigned the same unit factor.
 +
 +   .. mdp-value:: mass
 +
 +      Atoms contribute to the simulated density proportional to their mass.
 +
 +   .. mdp-value:: charge
 +
 +      Atoms contribute to the simulated density proportional to their charge.
 +
 +.. mdp:: density-guided-simulation-force-constant
 +
 +   (1e+09) [kJ mol\ :sup:`-1`] The scaling factor for density-guided simulation
 +   forces. May also be negative.
 +
 +.. mdp:: density-guided-simulation-gaussian-transform-spreading-width
 +
 +   (0.2) [nm] The Gaussian RMS width for the spread kernel for the simulated
 +   density.
 +
 +.. mdp:: density-guided-simulation-gaussian-transform-spreading-range-in-multiples-of-width
 +
 +   (4) The range after which the gaussian is cut off in multiples of the Gaussian
 +   RMS width described above.
 +
 +.. mdp:: density-guided-simulation-reference-density-filename
 +
 +   (reference.mrc) Reference density file name using an absolute path or a path
 +   relative to the to the folder from which :ref:`gmx mdrun` is called.
 +
 +.. mdp:: density-guided-simulation-nst
 +
 +   (1) Interval in steps at which the density fitting forces are evaluated
 +   and applied. The forces are scaled by this number when applied (See the
 +   `reference manual`_ for details).
 +
 +.. mdp:: density-guided-simulation-normalize-densities
 +
 +   (true) Normalize the sum of density voxel values to one for the reference
 +   density as well as the simulated density.
 +
 +.. mdp:: density-guided-simulation-adaptive-force-scaling
 +
 +   (false) Adapt the force constant to ensure a steady increase in similarity
 +   between simulated and reference density.
 +
 +   .. mdp-value: false
 +
 +      Do not use adaptive force scaling.
 +
 +   .. mdp-value:: true
 +
 +      Use adaptive force scaling.
 +
 +.. mdp:: density-guided-simulation-adaptive-force-scaling-time-constant
 +
 +   (4) [ps] Couple force constant to increase in similarity with reference density
 +   with this time constant. Larger times result in looser coupling.
  
  User defined thingies
  ^^^^^^^^^^^^^^^^^^^^^
index 938eefce1eb8a542432b220176a4e504007ece69,2cf134d224182d76aecc5038780c156af195c19f..812963c27a508386a9cc4dd1713d223adcd4c0ac
@@@ -69,160 -69,175 +69,163 @@@ class GridAxis
   */
  class BiasParams
  {
 -    public:
 -        /*! \brief Switch to turn off update skips, useful for testing.
 -         */
 -        enum class DisableUpdateSkips
 -        {
 -            no,  /**< Allow update skips (when supported by the method) */
 -            yes  /**< Disable update skips */
 -        };
 -
 -        /*! \brief
 -         * Check if the parameters permit skipping updates.
 -         *
 -         * Generally, we can skip updates of points that are non-local
 -         * at the time of the update if we for later times, when the points
 -         * with skipped updates have become local, know exactly how to apply
 -         * the previous updates. The free energy updates only depend
 -         * on local sampling, but the histogram rescaling factors
 -         * generally depend on the histogram size (all samples).
 -         * If the histogram size is kept constant or the scaling factors
 -         * are trivial, this is not a problem. However, if the histogram growth
 -         * is scaled down by some factor the size at the time of the update
 -         * needs to be known. It would be fairly simple to, for a deterministically
 -         * growing histogram, backtrack and calculate this value, but currently
 -         * we just disallow this case. This is not a restriction because it
 -         * only affects the local Boltzmann target type for which every update
 -         * is currently anyway global because the target is always updated globally.
 -         *
 -         * \returns true when we can skip updates.
 -         */
 -        inline bool skipUpdates() const
 -        {
 -            return (!disableUpdateSkips_ && localWeightScaling == 1);
 -        }
 -
 -        /*! \brief
 -         * Returns the the radius that needs to be sampled around a point before it is considered covered.
 -         */
 -        inline const awh_ivec &coverRadius() const
 -        {
 -            return coverRadius_;
 -        }
 -
 -        /*! \brief
 -         * Returns whether we should sample the coordinate.
 -         *
 -         * \param[in] step  The MD step number.
 -         */
 -        inline bool isSampleCoordStep(int64_t step) const
 -        {
 -            return (step > 0 && step % numStepsSampleCoord_ == 0);
 -        }
 -
 -        /*! \brief
 -         * Returns whether we should update the free energy.
 -         *
 -         * \param[in] step  The MD step number.
 -         */
 -        inline bool isUpdateFreeEnergyStep(int64_t step) const
 -        {
 -            int stepIntervalUpdateFreeEnergy = numSamplesUpdateFreeEnergy_*numStepsSampleCoord_;
 -            return (step > 0 && step % stepIntervalUpdateFreeEnergy == 0);
 -        }
 -
 -        /*! \brief
 -         * Returns whether we should update the target distribution.
 -         *
 -         * \param[in] step  The MD step number.
 -         */
 -        inline bool isUpdateTargetStep(int64_t step) const
 -        {
 -            return step % numStepsUpdateTarget_ == 0;
 -        }
 -
 -        /*! \brief
 -         * Returns if to do checks for covering in the initial stage.
 -         *
 -         * To avoid overhead due to expensive checks, we do not check
 -         * at every free energy update. However, if checks are
 -         * performed too rarely the detection of coverings will be
 -         * delayed, ultimately affecting free energy convergence.
 -         *
 -         * \param[in] step                  Time step.
 -         * \returns true at steps where checks should be performed.
 -         * \note  Only returns true at free energy update steps.
 -         */
 -        bool isCheckCoveringStep(int64_t step) const
 -        {
 -            return step > 0 && (step % numStepsCheckCovering_ == 0);
 -        }
 -
 -        /*! \brief
 -         * Returns if to perform checks for anomalies in the histogram.
 -         *
 -         * To avoid overhead due to expensive checks, we do not check
 -         * at every free energy update. These checks are only used for
 -         * warning the user and can be made as infrequently as
 -         * neccessary without affecting the algorithm itself.
 -         *
 -         * \param[in] step                  Time step.
 -         * \returns true at steps where checks should be performed.
 -         * \note Only returns true at free energy update steps.
 -         * \todo Currently this function just calls isCheckCoveringStep but the checks could be done less frequently.
 -         */
 -        bool isCheckHistogramForAnomaliesStep(int64_t step) const
 -        {
 -            return isCheckCoveringStep(step);
 -        }
 -
 -        /*! \brief Constructor.
 -         *
 -         * The local Boltzmann target distibution is defined by
 -         * 1) Adding the sampled weights instead of the target weights to the reference weight histogram.
 -         * 2) Scaling the weights of these samples by the beta scaling factor.
 -         * 3) Setting the target distribution equal the reference weight histogram.
 -         * This requires the following special update settings:
 -         *   localWeightScaling      = targetParam
 -         *   idealWeighthistUpdate   = false
 -         * Note: these variables could in principle be set to something else also for other target distribution types.
 -         * However, localWeightScaling < 1  is in general expected to give lower efficiency and, except for local Boltzmann,
 -         * idealWeightHistUpdate = false gives (in my experience) unstable, non-converging results.
 -         *
 -         * \param[in] awhParams              AWH parameters.
 -         * \param[in] awhBiasParams          Bias parameters.
 -         * \param[in] dimParams              Bias dimension parameters.
 -         * \param[in] beta                   1/(k_B T) in units of 1/(kJ/mol), should be > 0.
 -         * \param[in] mdTimeStep             The MD time step.
 -         * \param[in] numSharingSimulations  The number of simulations to share the bias across.
 -         * \param[in] gridAxis               The grid axes.
 -         * \param[in] disableUpdateSkips     If to disable update skips, useful for testing.
 -         * \param[in] biasIndex              Index of the bias.
 -         */
 -        BiasParams(const AwhParams              &awhParams,
 -                   const AwhBiasParams          &awhBiasParams,
 -                   const std::vector<DimParams> &dimParams,
 -                   double                        beta,
 -                   double                        mdTimeStep,
 -                   DisableUpdateSkips            disableUpdateSkips,
 -                   int                           numSharingSimulations,
 -                   const std::vector<GridAxis>  &gridAxis,
 -                   int                           biasIndex);
 -
 -        /* Data members */
 -        const double      invBeta;                     /**< 1/beta = kT in kJ/mol */
 -    private:
 -        const int64_t     numStepsSampleCoord_;        /**< Number of steps per coordinate value sample. */
 -    public:
 -        const int         numSamplesUpdateFreeEnergy_; /**< Number of samples per free energy update. */
 -    private:
 -        const int64_t     numStepsUpdateTarget_;       /**< Number of steps per updating the target distribution. */
 -        const int64_t     numStepsCheckCovering_;      /**< Number of steps per checking for covering. */
 -    public:
 -        const int         eTarget;                     /**< Type of target distribution. */
 -        const double      freeEnergyCutoffInKT;        /**< Free energy cut-off in kT for cut-off target distribution. */
 -        const double      temperatureScaleFactor;      /**< Temperature scaling factor for temperature scaled targed distributions. */
 -        const bool        idealWeighthistUpdate;       /**< Update reference weighthistogram using the target distribution? Otherwise use the realized distribution. */
 -        const int         numSharedUpdate;             /**< The number of (multi-)simulations sharing the bias update */
 -        const double      updateWeight;                /**< The probability weight accumulated for each update. */
 -        const double      localWeightScaling;          /**< Scaling factor applied to a sample before adding it to the reference weight histogram (= 1, usually). */
 -        const double      initialErrorInKT;            /**< Estimated initial free energy error in kT. */
 -        const double      initialHistogramSize;        /**< Initial reference weight histogram size. */
 -    private:
 -        awh_ivec          coverRadius_;                /**< The radius (in points) that needs to be sampled around a point before it is considered covered. */
 -    public:
 -        const bool        convolveForce;               /**< True if we convolve the force, false means use MC between umbrellas. */
 -        const int         biasIndex;                   /**< Index of the bias, used as a second random seed and for priting. */
 -    private:
 -        const bool        disableUpdateSkips_;         /**< If true, we disallow update skips, even when the method supports it. */
 +public:
 +    /*! \brief Switch to turn off update skips, useful for testing.
 +     */
 +    enum class DisableUpdateSkips
 +    {
 +        no, /**< Allow update skips (when supported by the method) */
 +        yes /**< Disable update skips */
 +    };
 +
 +    /*! \brief
 +     * Check if the parameters permit skipping updates.
 +     *
 +     * Generally, we can skip updates of points that are non-local
 +     * at the time of the update if we for later times, when the points
 +     * with skipped updates have become local, know exactly how to apply
 +     * the previous updates. The free energy updates only depend
 +     * on local sampling, but the histogram rescaling factors
 +     * generally depend on the histogram size (all samples).
 +     * If the histogram size is kept constant or the scaling factors
 +     * are trivial, this is not a problem. However, if the histogram growth
 +     * is scaled down by some factor the size at the time of the update
 +     * needs to be known. It would be fairly simple to, for a deterministically
 +     * growing histogram, backtrack and calculate this value, but currently
 +     * we just disallow this case. This is not a restriction because it
 +     * only affects the local Boltzmann target type for which every update
 +     * is currently anyway global because the target is always updated globally.
 +     *
 +     * \returns true when we can skip updates.
 +     */
 +    inline bool skipUpdates() const { return (!disableUpdateSkips_ && localWeightScaling == 1); }
 +
 +    /*! \brief
 +     * Returns the radius that needs to be sampled around a point before it is considered covered.
 +     */
 +    inline const awh_ivec& coverRadius() const { return coverRadius_; }
 +
 +    /*! \brief
 +     * Returns whether we should sample the coordinate.
 +     *
 +     * \param[in] step  The MD step number.
 +     */
 +    inline bool isSampleCoordStep(int64_t step) const
 +    {
 +        return (step > 0 && step % numStepsSampleCoord_ == 0);
 +    }
 +
 +    /*! \brief
 +     * Returns whether we should update the free energy.
 +     *
 +     * \param[in] step  The MD step number.
 +     */
 +    inline bool isUpdateFreeEnergyStep(int64_t step) const
 +    {
 +        int stepIntervalUpdateFreeEnergy = numSamplesUpdateFreeEnergy_ * numStepsSampleCoord_;
 +        return (step > 0 && step % stepIntervalUpdateFreeEnergy == 0);
 +    }
 +
 +    /*! \brief
 +     * Returns whether we should update the target distribution.
 +     *
 +     * \param[in] step  The MD step number.
 +     */
 +    inline bool isUpdateTargetStep(int64_t step) const { return step % numStepsUpdateTarget_ == 0; }
 +
 +    /*! \brief
 +     * Returns if to do checks for covering in the initial stage.
 +     *
 +     * To avoid overhead due to expensive checks, we do not check
 +     * at every free energy update. However, if checks are
 +     * performed too rarely the detection of coverings will be
 +     * delayed, ultimately affecting free energy convergence.
 +     *
 +     * \param[in] step                  Time step.
 +     * \returns true at steps where checks should be performed.
 +     * \note  Only returns true at free energy update steps.
 +     */
-     bool isCheckCoveringStep(int64_t step) const { return step % numStepsCheckCovering_ == 0; }
++    bool isCheckCoveringStep(int64_t step) const
++    {
++        return step > 0 && (step % numStepsCheckCovering_ == 0);
++    }
 +
 +    /*! \brief
 +     * Returns if to perform checks for anomalies in the histogram.
 +     *
 +     * To avoid overhead due to expensive checks, we do not check
 +     * at every free energy update. These checks are only used for
 +     * warning the user and can be made as infrequently as
 +     * neccessary without affecting the algorithm itself.
 +     *
 +     * \param[in] step                  Time step.
 +     * \returns true at steps where checks should be performed.
 +     * \note Only returns true at free energy update steps.
 +     * \todo Currently this function just calls isCheckCoveringStep but the checks could be done less frequently.
 +     */
 +    bool isCheckHistogramForAnomaliesStep(int64_t step) const { return isCheckCoveringStep(step); }
 +
 +    /*! \brief Constructor.
 +     *
 +     * The local Boltzmann target distibution is defined by
 +     * 1) Adding the sampled weights instead of the target weights to the reference weight histogram.
 +     * 2) Scaling the weights of these samples by the beta scaling factor.
 +     * 3) Setting the target distribution equal the reference weight histogram.
 +     * This requires the following special update settings:
 +     *   localWeightScaling      = targetParam
 +     *   idealWeighthistUpdate   = false
 +     * Note: these variables could in principle be set to something else also for other target distribution types.
 +     * However, localWeightScaling < 1  is in general expected to give lower efficiency and, except for local Boltzmann,
 +     * idealWeightHistUpdate = false gives (in my experience) unstable, non-converging results.
 +     *
 +     * \param[in] awhParams              AWH parameters.
 +     * \param[in] awhBiasParams          Bias parameters.
 +     * \param[in] dimParams              Bias dimension parameters.
 +     * \param[in] beta                   1/(k_B T) in units of 1/(kJ/mol), should be > 0.
 +     * \param[in] mdTimeStep             The MD time step.
 +     * \param[in] numSharingSimulations  The number of simulations to share the bias across.
 +     * \param[in] gridAxis               The grid axes.
 +     * \param[in] disableUpdateSkips     If to disable update skips, useful for testing.
 +     * \param[in] biasIndex              Index of the bias.
 +     */
 +    BiasParams(const AwhParams&              awhParams,
 +               const AwhBiasParams&          awhBiasParams,
 +               const std::vector<DimParams>& dimParams,
 +               double                        beta,
 +               double                        mdTimeStep,
 +               DisableUpdateSkips            disableUpdateSkips,
 +               int                           numSharingSimulations,
 +               const std::vector<GridAxis>&  gridAxis,
 +               int                           biasIndex);
 +
 +    /* Data members */
 +    const double invBeta; /**< 1/beta = kT in kJ/mol */
 +private:
 +    const int64_t numStepsSampleCoord_; /**< Number of steps per coordinate value sample. */
 +public:
 +    const int numSamplesUpdateFreeEnergy_; /**< Number of samples per free energy update. */
 +private:
 +    const int64_t numStepsUpdateTarget_; /**< Number of steps per updating the target distribution. */
 +    const int64_t numStepsCheckCovering_; /**< Number of steps per checking for covering. */
 +public:
 +    const int    eTarget;              /**< Type of target distribution. */
 +    const double freeEnergyCutoffInKT; /**< Free energy cut-off in kT for cut-off target distribution. */
 +    const double temperatureScaleFactor; /**< Temperature scaling factor for temperature scaled targed distributions. */
 +    const bool   idealWeighthistUpdate; /**< Update reference weighthistogram using the target distribution? Otherwise use the realized distribution. */
 +    const int    numSharedUpdate; /**< The number of (multi-)simulations sharing the bias update */
 +    const double updateWeight;    /**< The probability weight accumulated for each update. */
 +    const double localWeightScaling; /**< Scaling factor applied to a sample before adding it to the reference weight histogram (= 1, usually). */
 +    const double initialErrorInKT;   /**< Estimated initial free energy error in kT. */
 +    const double initialHistogramSize; /**< Initial reference weight histogram size. */
 +private:
 +    awh_ivec coverRadius_; /**< The radius (in points) that needs to be sampled around a point before it is considered covered. */
 +public:
 +    const bool convolveForce; /**< True if we convolve the force, false means use MC between umbrellas. */
 +    const int  biasIndex; /**< Index of the bias, used as a second random seed and for priting. */
 +private:
 +    const bool disableUpdateSkips_; /**< If true, we disallow update skips, even when the method supports it. */
  };
  
 -}      // namespace gmx
 +} // namespace gmx
  
  #endif /* GMX_AWH_BIASPARAMS_H */
index 55837e1f88bc2b17960323059d355a19a56354e5,0000000000000000000000000000000000000000..2b277177316ea97859389c93c38f3608f505a144
mode 100644,000000..100644
--- /dev/null
@@@ -1,837 -1,0 +1,843 @@@
-                 GMX_RELEASE_ASSERT(intervalLength < (1 + margin) * boxLength,
-                                    "We have checked before that interval <= period");
 +/*
 + * This file is part of the GROMACS molecular simulation package.
 + *
 + * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
 + * Copyright (c) 2001-2004, The GROMACS development team.
 + * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
 + * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 + * and including many others, as listed in the AUTHORS file in the
 + * top-level source directory and at http://www.gromacs.org.
 + *
 + * GROMACS is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public License
 + * as published by the Free Software Foundation; either version 2.1
 + * of the License, or (at your option) any later version.
 + *
 + * GROMACS is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + *
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with GROMACS; if not, see
 + * http://www.gnu.org/licenses, or write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
 + *
 + * If you want to redistribute modifications to GROMACS, please
 + * consider that scientific software is very special. Version
 + * control is crucial - bugs must be traceable. We will be happy to
 + * consider code for inclusion in the official distribution, but
 + * derived work must not be called official GROMACS. Details are found
 + * in the README & COPYING files - if they are missing, get the
 + * official version at http://www.gromacs.org.
 + *
 + * To help us fund GROMACS development, we humbly ask that you cite
 + * the research papers on the package. Check out http://www.gromacs.org.
 + */
 +#include "gmxpre.h"
 +
 +#include "read_params.h"
 +
 +#include "gromacs/awh/awh.h"
 +#include "gromacs/fileio/readinp.h"
 +#include "gromacs/fileio/warninp.h"
 +#include "gromacs/math/units.h"
 +#include "gromacs/math/utilities.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/mdtypes/awh_params.h"
 +#include "gromacs/mdtypes/inputrec.h"
 +#include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/mdtypes/pull_params.h"
 +#include "gromacs/pbcutil/pbc.h"
 +#include "gromacs/pulling/pull.h"
 +#include "gromacs/random/seed.h"
 +#include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/smalloc.h"
 +#include "gromacs/utility/stringutil.h"
 +
 +#include "biasparams.h"
 +#include "biassharing.h"
 +
 +namespace gmx
 +{
 +
 +const char* eawhtarget_names[eawhtargetNR + 1] = { "constant", "cutoff", "boltzmann",
 +                                                   "local-boltzmann", nullptr };
 +
 +const char* eawhgrowth_names[eawhgrowthNR + 1] = { "exp-linear", "linear", nullptr };
 +
 +const char* eawhpotential_names[eawhpotentialNR + 1] = { "convolved", "umbrella", nullptr };
 +
 +const char* eawhcoordprovider_names[eawhcoordproviderNR + 1] = { "pull", nullptr };
 +
 +/*! \brief
 + * Read parameters of an AWH bias dimension.
 + *
 + * \param[in,out] inp        Input file entries.
 + * \param[in] prefix         Prefix for dimension parameters.
 + * \param[in,out] dimParams  AWH dimensional parameters.
 + * \param[in] pull_params    Pull parameters.
 + * \param[in,out] wi         Struct for bookeeping warnings.
 + * \param[in] bComment       True if comments should be printed.
 + */
 +static void readDimParams(std::vector<t_inpfile>* inp,
 +                          const std::string&      prefix,
 +                          AwhDimParams*           dimParams,
 +                          const pull_params_t*    pull_params,
 +                          warninp_t               wi,
 +                          bool                    bComment)
 +{
 +    std::string opt;
 +    if (bComment)
 +    {
 +        printStringNoNewline(
 +                inp, "The provider of the reaction coordinate, currently only pull is supported");
 +    }
 +
 +    opt                       = prefix + "-coord-provider";
 +    dimParams->eCoordProvider = get_eeenum(inp, opt, eawhcoordprovider_names, wi);
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp, "The coordinate index for this dimension");
 +    }
 +    opt = prefix + "-coord-index";
 +    int coordIndexInput;
 +    coordIndexInput = get_eint(inp, opt, 1, wi);
 +    if (coordIndexInput < 1)
 +    {
 +        gmx_fatal(FARGS,
 +                  "Failed to read a valid coordinate index for %s. "
 +                  "Note that the pull coordinate indexing starts at 1.",
 +                  opt.c_str());
 +    }
 +
 +    /* The pull coordinate indices start at 1 in the input file, at 0 internally */
 +    dimParams->coordIndex = coordIndexInput - 1;
 +
 +    /* The pull settings need to be consistent with the AWH settings */
 +    if (!(pull_params->coord[dimParams->coordIndex].eType == epullEXTERNAL))
 +    {
 +        gmx_fatal(FARGS, "AWH biasing can only be  applied to pull type %s", EPULLTYPE(epullEXTERNAL));
 +    }
 +
 +    if (dimParams->coordIndex >= pull_params->ncoord)
 +    {
 +        gmx_fatal(FARGS,
 +                  "The given AWH coordinate index (%d) is larger than the number of pull "
 +                  "coordinates (%d)",
 +                  coordIndexInput, pull_params->ncoord);
 +    }
 +    if (pull_params->coord[dimParams->coordIndex].rate != 0)
 +    {
 +        auto message = formatString(
 +                "Setting pull-coord%d-rate (%g) is incompatible with AWH biasing this coordinate",
 +                coordIndexInput, pull_params->coord[dimParams->coordIndex].rate);
 +        warning_error(wi, message);
 +    }
 +
 +    /* Grid params for each axis */
 +    int eGeom = pull_params->coord[dimParams->coordIndex].eGeom;
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp, "Start and end values for each coordinate dimension");
 +    }
 +
 +    opt               = prefix + "-start";
 +    dimParams->origin = get_ereal(inp, opt, 0., wi);
 +
 +    opt            = prefix + "-end";
 +    dimParams->end = get_ereal(inp, opt, 0., wi);
 +
 +    if (gmx_within_tol(dimParams->end - dimParams->origin, 0, GMX_REAL_EPS))
 +    {
 +        auto message = formatString(
 +                "The given interval length given by %s-start (%g) and %s-end (%g) is zero. "
 +                "This will result in only one point along this axis in the coordinate value grid.",
 +                prefix.c_str(), dimParams->origin, prefix.c_str(), dimParams->end);
 +        warning(wi, message);
 +    }
 +    /* Check that the requested interval is in allowed range */
 +    if (eGeom == epullgDIST)
 +    {
 +        if (dimParams->origin < 0 || dimParams->end < 0)
 +        {
 +            gmx_fatal(FARGS,
 +                      "%s-start (%g) or %s-end (%g) set to a negative value. With pull geometry "
 +                      "distance coordinate values are non-negative. "
 +                      "Perhaps you want to use geometry %s instead?",
 +                      prefix.c_str(), dimParams->origin, prefix.c_str(), dimParams->end,
 +                      EPULLGEOM(epullgDIR));
 +        }
 +    }
 +    else if (eGeom == epullgANGLE || eGeom == epullgANGLEAXIS)
 +    {
 +        if (dimParams->origin < 0 || dimParams->end > 180)
 +        {
 +            gmx_fatal(FARGS,
 +                      "%s-start (%g) and %s-end (%g) are outside of the allowed range 0 to 180 deg "
 +                      "for pull geometries %s and %s ",
 +                      prefix.c_str(), dimParams->origin, prefix.c_str(), dimParams->end,
 +                      EPULLGEOM(epullgANGLE), EPULLGEOM(epullgANGLEAXIS));
 +        }
 +    }
 +    else if (eGeom == epullgDIHEDRAL)
 +    {
 +        if (dimParams->origin < -180 || dimParams->end > 180)
 +        {
 +            gmx_fatal(FARGS,
 +                      "%s-start (%g) and %s-end (%g) are outside of the allowed range -180 to 180 "
 +                      "deg for pull geometry %s. ",
 +                      prefix.c_str(), dimParams->origin, prefix.c_str(), dimParams->end,
 +                      EPULLGEOM(epullgDIHEDRAL));
 +        }
 +    }
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(
 +                inp, "The force constant for this coordinate (kJ/mol/nm^2 or kJ/mol/rad^2)");
 +    }
 +    opt                      = prefix + "-force-constant";
 +    dimParams->forceConstant = get_ereal(inp, opt, 0, wi);
 +    if (dimParams->forceConstant <= 0)
 +    {
 +        warning_error(wi, "The force AWH bias force constant should be > 0");
 +    }
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp, "Estimated diffusion constant (nm^2/ps or rad^2/ps)");
 +    }
 +    opt                  = prefix + "-diffusion";
 +    dimParams->diffusion = get_ereal(inp, opt, 0, wi);
 +
 +    if (dimParams->diffusion <= 0)
 +    {
 +        const double diffusion_default = 1e-5;
 +        auto         message           = formatString(
 +                "%s not explicitly set by user. You can choose to use a default "
 +                "value (%g nm^2/ps or rad^2/ps) but this may very well be "
 +                "non-optimal for your system!",
 +                opt.c_str(), diffusion_default);
 +        warning(wi, message);
 +        dimParams->diffusion = diffusion_default;
 +    }
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp,
 +                             "Diameter that needs to be sampled around a point before it is "
 +                             "considered covered.");
 +    }
 +    opt                      = prefix + "-cover-diameter";
 +    dimParams->coverDiameter = get_ereal(inp, opt, 0, wi);
 +
 +    if (dimParams->coverDiameter < 0)
 +    {
 +        gmx_fatal(FARGS, "%s (%g) cannot be negative.", opt.c_str(), dimParams->coverDiameter);
 +    }
 +}
 +
 +/*! \brief
 + * Check consistency of input at the AWH bias level.
 + *
 + * \param[in]     awhBiasParams  AWH bias parameters.
 + * \param[in,out] wi             Struct for bookkeeping warnings.
 + */
 +static void checkInputConsistencyAwhBias(const AwhBiasParams& awhBiasParams, warninp_t wi)
 +{
 +    /* Covering diameter and sharing warning. */
 +    for (int d = 0; d < awhBiasParams.ndim; d++)
 +    {
 +        double coverDiameter = awhBiasParams.dimParams[d].coverDiameter;
 +        if (awhBiasParams.shareGroup <= 0 && coverDiameter > 0)
 +        {
 +            warning(wi,
 +                    "The covering diameter is only relevant to set for bias sharing simulations.");
 +        }
 +    }
 +}
 +
 +/*! \brief
 + * Read parameters of an AWH bias.
 + *
 + * \param[in,out] inp            Input file entries.
 + * \param[in,out] awhBiasParams  AWH dimensional parameters.
 + * \param[in]     prefix         Prefix for bias parameters.
 + * \param[in]     ir             Input parameter struct.
 + * \param[in,out] wi             Struct for bookeeping warnings.
 + * \param[in]     bComment       True if comments should be printed.
 + */
 +static void read_bias_params(std::vector<t_inpfile>* inp,
 +                             AwhBiasParams*          awhBiasParams,
 +                             const std::string&      prefix,
 +                             const t_inputrec*       ir,
 +                             warninp_t               wi,
 +                             bool                    bComment)
 +{
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp, "Estimated initial PMF error (kJ/mol)");
 +    }
 +
 +    std::string opt = prefix + "-error-init";
 +    /* We allow using a default value here without warning (but warn the user if the diffusion constant is not set). */
 +    awhBiasParams->errorInitial = get_ereal(inp, opt, 10, wi);
 +    if (awhBiasParams->errorInitial <= 0)
 +    {
 +        gmx_fatal(FARGS, "%s needs to be > 0.", opt.c_str());
 +    }
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp,
 +                             "Growth rate of the reference histogram determining the bias update "
 +                             "size: exp-linear or linear");
 +    }
 +    opt                    = prefix + "-growth";
 +    awhBiasParams->eGrowth = get_eeenum(inp, opt, eawhgrowth_names, wi);
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp,
 +                             "Start the simulation by equilibrating histogram towards the target "
 +                             "distribution: no or yes");
 +    }
 +    opt                                 = prefix + "-equilibrate-histogram";
 +    awhBiasParams->equilibrateHistogram = (get_eeenum(inp, opt, yesno_names, wi) != 0);
 +    if (awhBiasParams->equilibrateHistogram && awhBiasParams->eGrowth != eawhgrowthEXP_LINEAR)
 +    {
 +        auto message =
 +                formatString("Option %s will only have an effect for histogram growth type '%s'.",
 +                             opt.c_str(), EAWHGROWTH(eawhgrowthEXP_LINEAR));
 +        warning(wi, message);
 +    }
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(
 +                inp, "Target distribution type: constant, cutoff, boltzmann or local-boltzmann");
 +    }
 +    opt                    = prefix + "-target";
 +    awhBiasParams->eTarget = get_eeenum(inp, opt, eawhtarget_names, wi);
 +
 +    if ((awhBiasParams->eTarget == eawhtargetLOCALBOLTZMANN)
 +        && (awhBiasParams->eGrowth == eawhgrowthEXP_LINEAR))
 +    {
 +        auto message = formatString(
 +                "Target type '%s' combined with histogram growth type '%s' is not "
 +                "expected to give stable bias updates. You probably want to use growth type "
 +                "'%s' instead.",
 +                EAWHTARGET(eawhtargetLOCALBOLTZMANN), EAWHGROWTH(eawhgrowthEXP_LINEAR),
 +                EAWHGROWTH(eawhgrowthLINEAR));
 +        warning(wi, message);
 +    }
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp,
 +                             "Boltzmann beta scaling factor for target distribution types "
 +                             "'boltzmann' and 'boltzmann-local'");
 +    }
 +    opt                              = prefix + "-target-beta-scaling";
 +    awhBiasParams->targetBetaScaling = get_ereal(inp, opt, 0, wi);
 +
 +    switch (awhBiasParams->eTarget)
 +    {
 +        case eawhtargetBOLTZMANN:
 +        case eawhtargetLOCALBOLTZMANN:
 +            if (awhBiasParams->targetBetaScaling < 0 || awhBiasParams->targetBetaScaling > 1)
 +            {
 +                gmx_fatal(FARGS, "%s = %g is not useful for target type %s.", opt.c_str(),
 +                          awhBiasParams->targetBetaScaling, EAWHTARGET(awhBiasParams->eTarget));
 +            }
 +            break;
 +        default:
 +            if (awhBiasParams->targetBetaScaling != 0)
 +            {
 +                gmx_fatal(
 +                        FARGS,
 +                        "Value for %s (%g) set explicitly but will not be used for target type %s.",
 +                        opt.c_str(), awhBiasParams->targetBetaScaling,
 +                        EAWHTARGET(awhBiasParams->eTarget));
 +            }
 +            break;
 +    }
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp, "Free energy cutoff value for target distribution type 'cutoff'");
 +    }
 +    opt                         = prefix + "-target-cutoff";
 +    awhBiasParams->targetCutoff = get_ereal(inp, opt, 0, wi);
 +
 +    switch (awhBiasParams->eTarget)
 +    {
 +        case eawhtargetCUTOFF:
 +            if (awhBiasParams->targetCutoff <= 0)
 +            {
 +                gmx_fatal(FARGS, "%s = %g is not useful for target type %s.", opt.c_str(),
 +                          awhBiasParams->targetCutoff, EAWHTARGET(awhBiasParams->eTarget));
 +            }
 +            break;
 +        default:
 +            if (awhBiasParams->targetCutoff != 0)
 +            {
 +                gmx_fatal(
 +                        FARGS,
 +                        "Value for %s (%g) set explicitly but will not be used for target type %s.",
 +                        opt.c_str(), awhBiasParams->targetCutoff, EAWHTARGET(awhBiasParams->eTarget));
 +            }
 +            break;
 +    }
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp, "Initialize PMF and target with user data: no or yes");
 +    }
 +    opt                      = prefix + "-user-data";
 +    awhBiasParams->bUserData = get_eeenum(inp, opt, yesno_names, wi);
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp, "Group index to share the bias with, 0 means not shared");
 +    }
 +    opt                       = prefix + "-share-group";
 +    awhBiasParams->shareGroup = get_eint(inp, opt, 0, wi);
 +    if (awhBiasParams->shareGroup < 0)
 +    {
 +        warning_error(wi, "AWH bias share-group should be >= 0");
 +    }
 +
 +    if (bComment)
 +    {
 +        printStringNoNewline(inp, "Dimensionality of the coordinate");
 +    }
 +    opt                 = prefix + "-ndim";
 +    awhBiasParams->ndim = get_eint(inp, opt, 0, wi);
 +
 +    if (awhBiasParams->ndim <= 0 || awhBiasParams->ndim > c_biasMaxNumDim)
 +    {
 +        gmx_fatal(FARGS, "%s (%d) needs to be > 0 and at most %d\n", opt.c_str(),
 +                  awhBiasParams->ndim, c_biasMaxNumDim);
 +    }
 +    if (awhBiasParams->ndim > 2)
 +    {
 +        warning_note(wi,
 +                     "For awh-dim > 2 the estimate based on the diffusion and the initial error is "
 +                     "currently only a rough guideline."
 +                     " You should verify its usefulness for your system before production runs!");
 +    }
 +    snew(awhBiasParams->dimParams, awhBiasParams->ndim);
 +    for (int d = 0; d < awhBiasParams->ndim; d++)
 +    {
 +        bComment              = bComment && d == 0;
 +        std::string prefixdim = prefix + formatString("-dim%d", d + 1);
 +        readDimParams(inp, prefixdim, &awhBiasParams->dimParams[d], ir->pull, wi, bComment);
 +    }
 +
 +    /* Check consistencies here that cannot be checked at read time at a lower level. */
 +    checkInputConsistencyAwhBias(*awhBiasParams, wi);
 +}
 +
 +/*! \brief
 + * Check consistency of input at the AWH level.
 + *
 + * \param[in]     awhParams  AWH parameters.
 + * \param[in,out] wi         Struct for bookkeeping warnings.
 + */
 +static void checkInputConsistencyAwh(const AwhParams& awhParams, warninp_t wi)
 +{
 +    /* Each pull coord can map to at most 1 AWH coord.
 +     * Check that we have a shared bias when requesting multisim sharing.
 +     */
 +    bool haveSharedBias = false;
 +    for (int k1 = 0; k1 < awhParams.numBias; k1++)
 +    {
 +        const AwhBiasParams& awhBiasParams1 = awhParams.awhBiasParams[k1];
 +
 +        if (awhBiasParams1.shareGroup > 0)
 +        {
 +            haveSharedBias = true;
 +        }
 +
 +        /* k1 is the reference AWH, k2 is the AWH we compare with (can be equal to k1) */
 +        for (int k2 = k1; k2 < awhParams.numBias; k2++)
 +        {
 +            for (int d1 = 0; d1 < awhBiasParams1.ndim; d1++)
 +            {
 +                const AwhBiasParams& awhBiasParams2 = awhParams.awhBiasParams[k2];
 +
 +                /* d1 is the reference dimension of the reference AWH. d2 is the dim index of the AWH to compare with. */
 +                for (int d2 = 0; d2 < awhBiasParams2.ndim; d2++)
 +                {
 +                    /* Give an error if (d1, k1) is different from (d2, k2) but the pull coordinate is the same */
 +                    if ((d1 != d2 || k1 != k2)
 +                        && (awhBiasParams1.dimParams[d1].coordIndex == awhBiasParams2.dimParams[d2].coordIndex))
 +                    {
 +                        char errormsg[STRLEN];
 +                        sprintf(errormsg,
 +                                "One pull coordinate (%d) cannot be mapped to two separate AWH "
 +                                "dimensions (awh%d-dim%d and awh%d-dim%d). "
 +                                "If this is really what you want to do you will have to duplicate "
 +                                "this pull coordinate.",
 +                                awhBiasParams1.dimParams[d1].coordIndex + 1, k1 + 1, d1 + 1, k2 + 1,
 +                                d2 + 1);
 +                        gmx_fatal(FARGS, "%s", errormsg);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    if (awhParams.shareBiasMultisim && !haveSharedBias)
 +    {
 +        warning(wi,
 +                "Sharing of biases over multiple simulations is requested, but no bias is marked "
 +                "as shared (share-group > 0)");
 +    }
 +
 +    /* mdrun does not support this (yet), but will check again */
 +    if (haveBiasSharingWithinSimulation(awhParams))
 +    {
 +        warning(wi,
 +                "You have shared biases within a single simulation, but mdrun does not support "
 +                "this (yet)");
 +    }
 +}
 +
 +AwhParams* readAndCheckAwhParams(std::vector<t_inpfile>* inp, const t_inputrec* ir, warninp_t wi)
 +{
 +    AwhParams* awhParams;
 +    snew(awhParams, 1);
 +    std::string opt;
 +
 +    /* Parameters common for all biases */
 +
 +    printStringNoNewline(inp, "The way to apply the biasing potential: convolved or umbrella");
 +    opt                   = "awh-potential";
 +    awhParams->ePotential = get_eeenum(inp, opt, eawhpotential_names, wi);
 +
 +    printStringNoNewline(inp,
 +                         "The random seed used for sampling the umbrella center in the case of "
 +                         "umbrella type potential");
 +    opt             = "awh-seed";
 +    awhParams->seed = get_eint(inp, opt, -1, wi);
 +    if (awhParams->seed == -1)
 +    {
 +        awhParams->seed = static_cast<int>(gmx::makeRandomSeed());
 +        fprintf(stderr, "Setting the AWH bias MC random seed to %" PRId64 "\n", awhParams->seed);
 +    }
 +
 +    printStringNoNewline(inp, "Data output interval in number of steps");
 +    opt               = "awh-nstout";
 +    awhParams->nstOut = get_eint(inp, opt, 100000, wi);
 +    if (awhParams->nstOut <= 0)
 +    {
 +        auto message = formatString("Not writing AWH output with AWH (%s = %d) does not make sense",
 +                                    opt.c_str(), awhParams->nstOut);
 +        warning_error(wi, message);
 +    }
 +    /* This restriction can be removed by changing a flag of print_ebin() */
 +    if (ir->nstenergy == 0 || awhParams->nstOut % ir->nstenergy != 0)
 +    {
 +        auto message = formatString("%s (%d) should be a multiple of nstenergy (%d)", opt.c_str(),
 +                                    awhParams->nstOut, ir->nstenergy);
 +        warning_error(wi, message);
 +    }
 +
 +    printStringNoNewline(inp, "Coordinate sampling interval in number of steps");
 +    opt                       = "awh-nstsample";
 +    awhParams->nstSampleCoord = get_eint(inp, opt, 10, wi);
 +
 +    printStringNoNewline(inp, "Free energy and bias update interval in number of samples");
 +    opt                                   = "awh-nsamples-update";
 +    awhParams->numSamplesUpdateFreeEnergy = get_eint(inp, opt, 10, wi);
 +    if (awhParams->numSamplesUpdateFreeEnergy <= 0)
 +    {
 +        warning_error(wi, opt + " needs to be an integer > 0");
 +    }
 +
 +    printStringNoNewline(
 +            inp, "When true, biases with share-group>0 are shared between multiple simulations");
 +    opt                          = "awh-share-multisim";
 +    awhParams->shareBiasMultisim = (get_eeenum(inp, opt, yesno_names, wi) != 0);
 +
 +    printStringNoNewline(inp, "The number of independent AWH biases");
 +    opt                = "awh-nbias";
 +    awhParams->numBias = get_eint(inp, opt, 1, wi);
 +    if (awhParams->numBias <= 0)
 +    {
 +        gmx_fatal(FARGS, "%s needs to be an integer > 0", opt.c_str());
 +    }
 +
 +    /* Read the parameters specific to each AWH bias */
 +    snew(awhParams->awhBiasParams, awhParams->numBias);
 +
 +    for (int k = 0; k < awhParams->numBias; k++)
 +    {
 +        bool        bComment  = (k == 0);
 +        std::string prefixawh = formatString("awh%d", k + 1);
 +        read_bias_params(inp, &awhParams->awhBiasParams[k], prefixawh, ir, wi, bComment);
 +    }
 +
 +    /* Do a final consistency check before returning */
 +    checkInputConsistencyAwh(*awhParams, wi);
 +
 +    if (ir->init_step != 0)
 +    {
 +        warning_error(wi, "With AWH init-step should be 0");
 +    }
 +
 +    return awhParams;
 +}
 +
 +/*! \brief
 + * Gets the period of a pull coordinate.
 + *
 + * \param[in] pullCoordParams   The parameters for the pull coordinate.
 + * \param[in] pbc               The PBC setup
 + * \param[in] intervalLength    The length of the AWH interval for this pull coordinate
 + * \returns the period (or 0 if not periodic).
 + */
 +static double get_pull_coord_period(const t_pull_coord& pullCoordParams, const t_pbc& pbc, const real intervalLength)
 +{
 +    double period = 0;
 +
 +    if (pullCoordParams.eGeom == epullgDIR)
 +    {
 +        const real margin = 0.001;
 +        // Make dims periodic when the interval covers > 95%
 +        const real periodicFraction = 0.95;
 +
 +        // Check if the pull direction is along a box vector
 +        for (int dim = 0; dim < pbc.ndim_ePBC; dim++)
 +        {
 +            const real boxLength    = norm(pbc.box[dim]);
 +            const real innerProduct = iprod(pullCoordParams.vec, pbc.box[dim]);
 +            if (innerProduct >= (1 - margin) * boxLength && innerProduct <= (1 + margin) * boxLength)
 +            {
++                if (intervalLength > (1 + margin) * boxLength)
++                {
++                    gmx_fatal(FARGS,
++                              "The AWH interval (%f nm) for a pull coordinate is larger than the "
++                              "box size (%f nm)",
++                              intervalLength, boxLength);
++                }
++
 +                if (intervalLength > periodicFraction * boxLength)
 +                {
 +                    period = boxLength;
 +                }
 +            }
 +        }
 +    }
 +    else if (pullCoordParams.eGeom == epullgDIHEDRAL)
 +    {
 +        /* The dihedral angle is periodic in -180 to 180 deg */
 +        period = 360;
 +    }
 +
 +    return period;
 +}
 +
 +/*! \brief
 + * Checks if the given interval is defined in the correct periodic interval.
 + *
 + * \param[in] origin      Start value of interval.
 + * \param[in] end         End value of interval.
 + * \param[in] period      Period (or 0 if not periodic).
 + * \returns true if the end point values are in the correct periodic interval.
 + */
 +static bool intervalIsInPeriodicInterval(double origin, double end, double period)
 +{
 +    return (period == 0) || (std::fabs(origin) <= 0.5 * period && std::fabs(end) <= 0.5 * period);
 +}
 +
 +/*! \brief
 + * Checks if a value is within an interval.
 + *
 + * \param[in] origin      Start value of interval.
 + * \param[in] end         End value of interval.
 + * \param[in] period      Period (or 0 if not periodic).
 + * \param[in] value       Value to check.
 + * \returns true if the value is within the interval.
 + */
 +static bool valueIsInInterval(double origin, double end, double period, double value)
 +{
 +    bool bIn_interval;
 +
 +    if (period > 0)
 +    {
 +        if (origin < end)
 +        {
 +            /* The interval closes within the periodic interval */
 +            bIn_interval = (value >= origin) && (value <= end);
 +        }
 +        else
 +        {
 +            /* The interval wraps around the periodic boundary */
 +            bIn_interval = ((value >= origin) && (value <= 0.5 * period))
 +                           || ((value >= -0.5 * period) && (value <= end));
 +        }
 +    }
 +    else
 +    {
 +        bIn_interval = (value >= origin) && (value <= end);
 +    }
 +
 +    return bIn_interval;
 +}
 +
 +/*! \brief
 + * Check if the starting configuration is consistent with the given interval.
 + *
 + * \param[in]     awhParams  AWH parameters.
 + * \param[in,out] wi         Struct for bookeeping warnings.
 + */
 +static void checkInputConsistencyInterval(const AwhParams* awhParams, warninp_t wi)
 +{
 +    for (int k = 0; k < awhParams->numBias; k++)
 +    {
 +        AwhBiasParams* awhBiasParams = &awhParams->awhBiasParams[k];
 +        for (int d = 0; d < awhBiasParams->ndim; d++)
 +        {
 +            AwhDimParams* dimParams  = &awhBiasParams->dimParams[d];
 +            int           coordIndex = dimParams->coordIndex;
 +            double origin = dimParams->origin, end = dimParams->end, period = dimParams->period;
 +            double coordValueInit = dimParams->coordValueInit;
 +
 +            if ((period == 0) && (origin > end))
 +            {
 +                gmx_fatal(FARGS,
 +                          "For the non-periodic pull coordinates awh%d-dim%d-start (%f) cannot be "
 +                          "larger than awh%d-dim%d-end (%f)",
 +                          k + 1, d + 1, origin, k + 1, d + 1, end);
 +            }
 +
 +            /* Currently we assume symmetric periodic intervals, meaning we use [-period/2, period/2] as the reference interval.
 +               Make sure the AWH interval is within this reference interval.
 +
 +               Note: we could fairly simply allow using a  more general interval (e.g. [x, x + period]) but it complicates
 +               things slightly and I don't see that there is a great need for it. It would also mean that the interval would
 +               depend on AWH input. Also, for dihedral angles you would always want the reference interval to be -180, +180,
 +               independent of AWH parameters.
 +             */
 +            if (!intervalIsInPeriodicInterval(origin, end, period))
 +            {
 +                gmx_fatal(FARGS,
 +                          "When using AWH with periodic pull coordinate geometries "
 +                          "awh%d-dim%d-start (%.8g) and "
 +                          "awh%d-dim%d-end (%.8g) should cover at most one period (%.8g) and take "
 +                          "values in between "
 +                          "minus half a period and plus half a period, i.e. in the interval [%.8g, "
 +                          "%.8g].",
 +                          k + 1, d + 1, origin, k + 1, d + 1, end, period, -0.5 * period, 0.5 * period);
 +            }
 +
 +            /* Warn if the pull initial coordinate value is not in the grid */
 +            if (!valueIsInInterval(origin, end, period, coordValueInit))
 +            {
 +                auto message = formatString(
 +                        "The initial coordinate value (%.8g) for pull coordinate index %d falls "
 +                        "outside "
 +                        "of the sampling nterval awh%d-dim%d-start (%.8g) to awh%d-dim%d-end "
 +                        "(%.8g). "
 +                        "This can lead to large initial forces pulling the coordinate towards the "
 +                        "sampling interval.",
 +                        coordValueInit, coordIndex + 1, k + 1, d + 1, origin, k + 1, d + 1, end);
 +                warning(wi, message);
 +            }
 +        }
 +    }
 +}
 +
 +void setStateDependentAwhParams(AwhParams*           awhParams,
 +                                const pull_params_t* pull_params,
 +                                pull_t*              pull_work,
 +                                const matrix         box,
 +                                int                  ePBC,
 +                                const tensor&        compressibility,
 +                                const t_grpopts*     inputrecGroupOptions,
 +                                warninp_t            wi)
 +{
 +    /* The temperature is not really state depenendent but is not known
 +     * when read_awhParams is called (in get ir).
 +     * It is known first after do_index has been called in grompp.cpp.
 +     */
 +    if (inputrecGroupOptions->ref_t == nullptr || inputrecGroupOptions->ref_t[0] <= 0)
 +    {
 +        gmx_fatal(FARGS, "AWH biasing is only supported for temperatures > 0");
 +    }
 +    for (int i = 1; i < inputrecGroupOptions->ngtc; i++)
 +    {
 +        if (inputrecGroupOptions->ref_t[i] != inputrecGroupOptions->ref_t[0])
 +        {
 +            gmx_fatal(FARGS,
 +                      "AWH biasing is currently only supported for identical temperatures for all "
 +                      "temperature coupling groups");
 +        }
 +    }
 +
 +    t_pbc pbc;
 +    set_pbc(&pbc, ePBC, box);
 +
 +    for (int k = 0; k < awhParams->numBias; k++)
 +    {
 +        AwhBiasParams* awhBiasParams = &awhParams->awhBiasParams[k];
 +        for (int d = 0; d < awhBiasParams->ndim; d++)
 +        {
 +            AwhDimParams*       dimParams       = &awhBiasParams->dimParams[d];
 +            const t_pull_coord& pullCoordParams = pull_params->coord[dimParams->coordIndex];
 +
 +            if (pullCoordParams.eGeom == epullgDIRPBC)
 +            {
 +                gmx_fatal(FARGS,
 +                          "AWH does not support pull geometry '%s'. "
 +                          "If the maximum distance between the groups is always less than half the "
 +                          "box size, "
 +                          "you can use geometry '%s' instead.",
 +                          EPULLGEOM(epullgDIRPBC), EPULLGEOM(epullgDIR));
 +            }
 +
 +            dimParams->period =
 +                    get_pull_coord_period(pullCoordParams, pbc, dimParams->end - dimParams->origin);
 +            // We would like to check for scaling, but we don't have the full inputrec available here
 +            if (dimParams->period > 0
 +                && !(pullCoordParams.eGeom == epullgANGLE || pullCoordParams.eGeom == epullgDIHEDRAL))
 +            {
 +                bool coordIsScaled = false;
 +                for (int d2 = 0; d2 < DIM; d2++)
 +                {
 +                    if (pullCoordParams.vec[d2] != 0 && norm2(compressibility[d2]) != 0)
 +                    {
 +                        coordIsScaled = true;
 +                    }
 +                }
 +                if (coordIsScaled)
 +                {
 +                    std::string mesg = gmx::formatString(
 +                            "AWH dimension %d of bias %d is periodic with pull geometry '%s', "
 +                            "while you should are applying pressure scaling to the corresponding "
 +                            "box vector, this is not supported.",
 +                            d + 1, k + 1, EPULLGEOM(pullCoordParams.eGeom));
 +                    warning(wi, mesg.c_str());
 +                }
 +            }
 +
 +            /* The initial coordinate value, converted to external user units. */
 +            dimParams->coordValueInit = get_pull_coord_value(pull_work, dimParams->coordIndex, &pbc);
 +
 +            dimParams->coordValueInit *= pull_conversion_factor_internal2userinput(&pullCoordParams);
 +        }
 +    }
 +    checkInputConsistencyInterval(awhParams, wi);
 +
 +    /* Register AWH as external potential with pull to check consistency. */
 +    Awh::registerAwhWithPull(*awhParams, pull_work);
 +}
 +
 +} // namespace gmx
index fa4be6e34b02ec4b5c147b66021151052b5bd6fd,e475e66a2c440dbbf37f238bea894808d56b0e8d..3466d8d4f1114644918dbe3c9fba228062ccc701
  #include "gromacs/utility/smalloc.h"
  #include "gromacs/utility/snprintf.h"
  
 -typedef struct {
 +typedef struct
 +{
      int ai, aj;
  } gmx_conection_t;
  
 -typedef struct gmx_conect_t {
 +typedef struct gmx_conect_t
 +{
      int              nconect;
      gmx_bool         bSorted;
 -    gmx_conection_t *conect;
 +    gmx_conection_tconect;
  } gmx_conect_t;
  
 -static const char *pdbtp[epdbNR] = {
 -    "ATOM  ", "HETATM", "ANISOU", "CRYST1",
 -    "COMPND", "MODEL", "ENDMDL", "TER", "HEADER", "TITLE", "REMARK",
 -    "CONECT"
 -};
 -
 +static const char* pdbtp[epdbNR] = { "ATOM  ", "HETATM", "ANISOU", "CRYST1", "COMPND", "MODEL",
 +                                     "ENDMDL", "TER",    "HEADER", "TITLE",  "REMARK", "CONECT" };
  
  #define REMARK_SIM_BOX "REMARK    THIS IS A SIMULATION BOX"
  
 -static void xlate_atomname_pdb2gmx(char *name)
 +static void xlate_atomname_pdb2gmx(charname)
  {
      int  i, length;
      char temp;
@@@ -89,9 -91,9 +89,9 @@@
          temp = name[0];
          for (i = 1; i < length; i++)
          {
 -            name[i-1] = name[i];
 +            name[i - 1] = name[i];
          }
 -        name[length-1] = temp;
 +        name[length - 1] = temp;
      }
  }
  
  static std::string xlate_atomname_gmx2pdb(std::string name)
  {
      size_t length = name.size();
 -    if (length > 3 && std::isdigit(name[length-1]))
 +    if (length > 3 && std::isdigit(name[length - 1]))
      {
 -        char temp = name[length-1];
 -        for (size_t i = length-1; i > 0; --i)
 +        char temp = name[length - 1];
 +        for (size_t i = length - 1; i > 0; --i)
          {
 -            name[i] = name[i-1];
 +            name[i] = name[i - 1];
          }
          name[0] = temp;
      }
  }
  
  
 -void gmx_write_pdb_box(FILE *out, int ePBC, const matrix box)
 +void gmx_write_pdb_box(FILEout, int ePBC, const matrix box)
  {
      real alpha, beta, gamma;
  
          return;
      }
  
 -    if (norm2(box[YY])*norm2(box[ZZ]) != 0)
 +    if (norm2(box[YY]) * norm2(box[ZZ]) != 0)
      {
 -        alpha = RAD2DEG*gmx_angle(box[YY], box[ZZ]);
 +        alpha = RAD2DEG * gmx_angle(box[YY], box[ZZ]);
      }
      else
      {
          alpha = 90;
      }
 -    if (norm2(box[XX])*norm2(box[ZZ]) != 0)
 +    if (norm2(box[XX]) * norm2(box[ZZ]) != 0)
      {
 -        beta  = RAD2DEG*gmx_angle(box[XX], box[ZZ]);
 +        beta = RAD2DEG * gmx_angle(box[XX], box[ZZ]);
      }
      else
      {
 -        beta  = 90;
 +        beta = 90;
      }
 -    if (norm2(box[XX])*norm2(box[YY]) != 0)
 +    if (norm2(box[XX]) * norm2(box[YY]) != 0)
      {
 -        gamma = RAD2DEG*gmx_angle(box[XX], box[YY]);
 +        gamma = RAD2DEG * gmx_angle(box[XX], box[YY]);
      }
      else
      {
      fprintf(out, "REMARK    THIS IS A SIMULATION BOX\n");
      if (ePBC != epbcSCREW)
      {
 -        fprintf(out, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n",
 -                10*norm(box[XX]), 10*norm(box[YY]), 10*norm(box[ZZ]),
 -                alpha, beta, gamma, "P 1", 1);
 +        fprintf(out, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n", 10 * norm(box[XX]),
 +                10 * norm(box[YY]), 10 * norm(box[ZZ]), alpha, beta, gamma, "P 1", 1);
      }
      else
      {
          /* Double the a-vector length and write the correct space group */
 -        fprintf(out, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n",
 -                20*norm(box[XX]), 10*norm(box[YY]), 10*norm(box[ZZ]),
 -                alpha, beta, gamma, "P 21 1 1", 1);
 -
 +        fprintf(out, "CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f %-11s%4d\n", 20 * norm(box[XX]),
 +                10 * norm(box[YY]), 10 * norm(box[ZZ]), alpha, beta, gamma, "P 21 1 1", 1);
      }
  }
  
 -static void read_cryst1(char *line, int *ePBC, matrix box)
 +static void read_cryst1(char* line, int* ePBC, matrix box)
  {
  #define SG_SIZE 11
 -    char   sa[12], sb[12], sc[12], sg[SG_SIZE+1], ident;
 +    char   sa[12], sb[12], sc[12], sg[SG_SIZE + 1], ident;
      double fa, fb, fc, alpha, beta, gamma, cosa, cosb, cosg, sing;
      int    syma, symb, symc;
      int    ePBC_file;
      ePBC_file = -1;
      if (strlen(line) >= 55)
      {
 -        strncpy(sg, line+55, SG_SIZE);
 +        strncpy(sg, line + 55, SG_SIZE);
          sg[SG_SIZE] = '\0';
          ident       = ' ';
          syma        = 0;
          symb        = 0;
          symc        = 0;
          sscanf(sg, "%c %d %d %d", &ident, &syma, &symb, &symc);
 -        if (ident == 'P' && syma ==  1 && symb <= 1 && symc <= 1)
 +        if (ident == 'P' && syma == 1 && symb <= 1 && symc <= 1)
          {
 -            fc        = strtod(sc, nullptr)*0.1;
 +            fc        = strtod(sc, nullptr) * 0.1;
              ePBC_file = (fc > 0 ? epbcXYZ : epbcXY);
          }
          if (ident == 'P' && syma == 21 && symb == 1 && symc == 1)
  
      if (box)
      {
 -        fa = strtod(sa, nullptr)*0.1;
 -        fb = strtod(sb, nullptr)*0.1;
 -        fc = strtod(sc, nullptr)*0.1;
 +        fa = strtod(sa, nullptr) * 0.1;
 +        fb = strtod(sb, nullptr) * 0.1;
 +        fc = strtod(sc, nullptr) * 0.1;
          if (ePBC_file == epbcSCREW)
          {
              fa *= 0.5;
          {
              if (alpha != 90.0)
              {
 -                cosa = std::cos(alpha*DEG2RAD);
 +                cosa = std::cos(alpha * DEG2RAD);
              }
              else
              {
              }
              if (beta != 90.0)
              {
 -                cosb = std::cos(beta*DEG2RAD);
 +                cosb = std::cos(beta * DEG2RAD);
              }
              else
              {
              }
              if (gamma != 90.0)
              {
 -                cosg = std::cos(gamma*DEG2RAD);
 -                sing = std::sin(gamma*DEG2RAD);
 +                cosg = std::cos(gamma * DEG2RAD);
 +                sing = std::sin(gamma * DEG2RAD);
              }
              else
              {
                  cosg = 0;
                  sing = 1;
              }
 -            box[YY][XX] = fb*cosg;
 -            box[YY][YY] = fb*sing;
 -            box[ZZ][XX] = fc*cosb;
 -            box[ZZ][YY] = fc*(cosa - cosb*cosg)/sing;
 -            box[ZZ][ZZ] = std::sqrt(fc*fc
 -                                    - box[ZZ][XX]*box[ZZ][XX] - box[ZZ][YY]*box[ZZ][YY]);
 +            box[YY][XX] = fb * cosg;
 +            box[YY][YY] = fb * sing;
 +            box[ZZ][XX] = fc * cosb;
 +            box[ZZ][YY] = fc * (cosa - cosb * cosg) / sing;
 +            box[ZZ][ZZ] = std::sqrt(fc * fc - box[ZZ][XX] * box[ZZ][XX] - box[ZZ][YY] * box[ZZ][YY]);
          }
          else
          {
      }
  }
  
 -static int
 -gmx_fprintf_pqr_atomline(FILE *            fp,
 -                         enum PDB_record   record,
 -                         int               atom_seq_number,
 -                         const char *      atom_name,
 -                         const char *      res_name,
 -                         char              chain_id,
 -                         int               res_seq_number,
 -                         real              x,
 -                         real              y,
 -                         real              z,
 -                         real              occupancy,
 -                         real              b_factor)
 +static int gmx_fprintf_pqr_atomline(FILE*           fp,
 +                                    enum PDB_record record,
 +                                    int             atom_seq_number,
 +                                    const char*     atom_name,
 +                                    const char*     res_name,
 +                                    char            chain_id,
 +                                    int             res_seq_number,
 +                                    real            x,
 +                                    real            y,
 +                                    real            z,
 +                                    real            occupancy,
 +                                    real            b_factor)
  {
      GMX_RELEASE_ASSERT(record == epdbATOM || record == epdbHETATM,
                         "Can only print PQR atom lines as ATOM or HETATM records");
  
      /* Check atom name */
 -    GMX_RELEASE_ASSERT(atom_name != nullptr,
 -                       "Need atom information to print pqr");
 +    GMX_RELEASE_ASSERT(atom_name != nullptr, "Need atom information to print pqr");
  
      /* Check residue name */
 -    GMX_RELEASE_ASSERT(res_name != nullptr,
 -                       "Need residue information to print pqr");
 +    GMX_RELEASE_ASSERT(res_name != nullptr, "Need residue information to print pqr");
  
      /* Truncate integers so they fit */
      atom_seq_number = atom_seq_number % 100000;
      res_seq_number  = res_seq_number % 10000;
  
 -    int n = fprintf(fp,
 -                    "%-6s%5d %-4.4s%4.4s%c%4d %8.3f %8.3f %8.3f %6.2f %6.2f\n",
 -                    pdbtp[record],
 -                    atom_seq_number,
 -                    atom_name,
 -                    res_name,
 -                    chain_id,
 -                    res_seq_number,
 -                    x, y, z,
 -                    occupancy,
 -                    b_factor);
 +    int n = fprintf(fp, "%-6s%5d %-4.4s%4.4s%c%4d %8.3f %8.3f %8.3f %6.2f %6.2f\n", pdbtp[record],
 +                    atom_seq_number, atom_name, res_name, chain_id, res_seq_number, x, y, z,
 +                    occupancy, b_factor);
  
      return n;
  }
  
 -void write_pdbfile_indexed(FILE *out, const char *title,
 -                           const t_atoms *atoms, const rvec x[],
 -                           int ePBC, const matrix box, char chainid,
 -                           int model_nr, int nindex, const int index[],
 -                           gmx_conect conect, gmx_bool bTerSepChains,
 -                           bool usePqrFormat)
 +void write_pdbfile_indexed(FILE*          out,
 +                           const char*    title,
 +                           const t_atoms* atoms,
 +                           const rvec     x[],
 +                           int            ePBC,
 +                           const matrix   box,
 +                           char           chainid,
 +                           int            model_nr,
 +                           int            nindex,
 +                           const int      index[],
 +                           gmx_conect     conect,
 +                           bool           usePqrFormat)
  {
 -    gmx_conect_t     *gc = static_cast<gmx_conect_t *>(conect);
 -    int               i, ii;
 -    int               resind, resnr;
 -    enum PDB_record   type;
 -    unsigned char     resic, ch;
 -    char              altloc;
 -    real              occup, bfac;
 -    gmx_bool          bOccup;
 -    int               chainnum, lastchainnum;
 -    gmx_residuetype_t*rt;
 -    const char       *p_restype;
 -    const char       *p_lastrestype;
 -
 -    gmx_residuetype_init(&rt);
 +    gmx_conect_t*   gc = static_cast<gmx_conect_t*>(conect);
 +    enum PDB_record type;
 +    char            altloc;
 +    real            occup, bfac;
 +    gmx_bool        bOccup;
 +
  
      fprintf(out, "TITLE     %s\n", (title && title[0]) ? title : gmx::bromacs().c_str());
 -    if (box && ( (norm2(box[XX]) != 0.0f) || (norm2(box[YY]) != 0.0f) || (norm2(box[ZZ]) != 0.0f) ) )
 +    if (box && ((norm2(box[XX]) != 0.0F) || (norm2(box[YY]) != 0.0F) || (norm2(box[ZZ]) != 0.0F)))
      {
          gmx_write_pdb_box(out, ePBC, box);
      }
           * otherwise set them all to one
           */
          bOccup = TRUE;
 -        for (ii = 0; (ii < nindex) && bOccup; ii++)
 +        for (int ii = 0; (ii < nindex) && bOccup; ii++)
          {
 -            i      = index[ii];
 +            int i  = index[ii];
              bOccup = bOccup && (atoms->pdbinfo[i].occup == 0.0);
          }
      }
  
      fprintf(out, "MODEL %8d\n", model_nr > 0 ? model_nr : 1);
  
 -    lastchainnum      = -1;
 -    p_restype         = nullptr;
 -
 -    for (ii = 0; ii < nindex; ii++)
 +    ResidueType rt;
 +    for (int ii = 0; ii < nindex; ii++)
      {
 -        i             = index[ii];
 -        resind        = atoms->atom[i].resind;
 -        chainnum      = atoms->resinfo[resind].chainnum;
 -        p_lastrestype = p_restype;
 -        gmx_residuetype_get_type(rt, *atoms->resinfo[resind].name, &p_restype);
 -
 -        /* Add a TER record if we changed chain, and if either the previous or this chain is protein/DNA/RNA. */
 -        if (bTerSepChains && ii > 0 && chainnum != lastchainnum)
 -        {
 -            /* Only add TER if the previous chain contained protein/DNA/RNA. */
 -            if (gmx_residuetype_is_protein(rt, p_lastrestype) || gmx_residuetype_is_dna(rt, p_lastrestype) || gmx_residuetype_is_rna(rt, p_lastrestype))
 -            {
 -                fprintf(out, "TER\n");
 -            }
 -            lastchainnum    = chainnum;
 -        }
 -
 -        std::string resnm = *atoms->resinfo[resind].name;
 -        std::string nm    = *atoms->atomname[i];
 +        int         i      = index[ii];
 +        int         resind = atoms->atom[i].resind;
 +        std::string resnm  = *atoms->resinfo[resind].name;
 +        std::string nm     = *atoms->atomname[i];
  
          /* rename HG12 to 2HG1, etc. */
 -        nm    = xlate_atomname_gmx2pdb(nm);
 -        resnr = atoms->resinfo[resind].nr;
 -        resic = atoms->resinfo[resind].ic;
 +        nm                  = xlate_atomname_gmx2pdb(nm);
 +        int           resnr = atoms->resinfo[resind].nr;
 +        unsigned char resic = atoms->resinfo[resind].ic;
 +        unsigned char ch;
          if (chainid != ' ')
          {
              ch = chainid;
          bfac  = pdbinfo.bfac;
          if (!usePqrFormat)
          {
 -            gmx_fprintf_pdb_atomline(out,
 -                                     type,
 -                                     i+1,
 -                                     nm.c_str(),
 -                                     altloc,
 -                                     resnm.c_str(),
 -                                     ch,
 -                                     resnr,
 -                                     resic,
 -                                     10*x[i][XX], 10*x[i][YY], 10*x[i][ZZ],
 -                                     occup,
 -                                     bfac,
 -                                     atoms->atom[i].elem);
 +            gmx_fprintf_pdb_atomline(out, type, i + 1, nm.c_str(), altloc, resnm.c_str(), ch, resnr,
 +                                     resic, 10 * x[i][XX], 10 * x[i][YY], 10 * x[i][ZZ], occup,
 +                                     bfac, atoms->atom[i].elem);
  
              if (atoms->pdbinfo && atoms->pdbinfo[i].bAnisotropic)
              {
 -                fprintf(out, "ANISOU%5d  %-4.4s%4.4s%c%4d%c %7d%7d%7d%7d%7d%7d\n",
 -                        (i+1)%100000, nm.c_str(), resnm.c_str(), ch, resnr,
 -                        (resic == '\0') ? ' ' : resic,
 -                        atoms->pdbinfo[i].uij[0], atoms->pdbinfo[i].uij[1],
 -                        atoms->pdbinfo[i].uij[2], atoms->pdbinfo[i].uij[3],
 -                        atoms->pdbinfo[i].uij[4], atoms->pdbinfo[i].uij[5]);
 +                fprintf(out, "ANISOU%5d  %-4.4s%4.4s%c%4d%c %7d%7d%7d%7d%7d%7d\n", (i + 1) % 100000,
 +                        nm.c_str(), resnm.c_str(), ch, resnr, (resic == '\0') ? ' ' : resic,
 +                        atoms->pdbinfo[i].uij[0], atoms->pdbinfo[i].uij[1], atoms->pdbinfo[i].uij[2],
 +                        atoms->pdbinfo[i].uij[3], atoms->pdbinfo[i].uij[4], atoms->pdbinfo[i].uij[5]);
              }
          }
          else
          {
 -            gmx_fprintf_pqr_atomline(out,
 -                                     type,
 -                                     i+1,
 -                                     nm.c_str(),
 -                                     resnm.c_str(),
 -                                     ch,
 -                                     resnr,
 -                                     10*x[i][XX], 10*x[i][YY], 10*x[i][ZZ],
 -                                     occup,
 -                                     bfac);
 +            gmx_fprintf_pqr_atomline(out, type, i + 1, nm.c_str(), resnm.c_str(), ch, resnr,
 +                                     10 * x[i][XX], 10 * x[i][YY], 10 * x[i][ZZ], occup, bfac);
          }
      }
  
      if (nullptr != gc)
      {
          /* Write conect records */
 -        for (i = 0; (i < gc->nconect); i++)
 +        for (int i = 0; (i < gc->nconect); i++)
          {
 -            fprintf(out, "CONECT%5d%5d\n", gc->conect[i].ai+1, gc->conect[i].aj+1);
 +            fprintf(out, "CONECT%5d%5d\n", gc->conect[i].ai + 1, gc->conect[i].aj + 1);
          }
      }
 -
 -    gmx_residuetype_destroy(rt);
  }
  
 -void write_pdbfile(FILE *out, const char *title, const t_atoms *atoms, const rvec x[],
 -                   int ePBC, const matrix box, char chainid, int model_nr, gmx_conect conect, gmx_bool bTerSepChains)
 +void write_pdbfile(FILE*          out,
 +                   const char*    title,
 +                   const t_atoms* atoms,
 +                   const rvec     x[],
 +                   int            ePBC,
 +                   const matrix   box,
 +                   char           chainid,
 +                   int            model_nr,
 +                   gmx_conect     conect)
  {
      int i, *index;
  
      {
          index[i] = i;
      }
 -    write_pdbfile_indexed(out, title, atoms, x, ePBC, box, chainid, model_nr,
 -                          atoms->nr, index, conect, bTerSepChains, false);
 +    write_pdbfile_indexed(out, title, atoms, x, ePBC, box, chainid, model_nr, atoms->nr, index,
 +                          conect, false);
      sfree(index);
  }
  
 -static int line2type(const char *line)
 +static int line2type(const charline)
  {
      int  k;
      char type[8];
      return k;
  }
  
 -static void read_anisou(char line[], int natom, t_atoms *atoms)
 +static void read_anisou(char line[], int natom, t_atomsatoms)
  {
      int  i, j, k, atomnr;
      char nc = '\0';
  
      /* Search backwards for number and name only */
      atomnr = std::strtol(anr, nullptr, 10);
 -    for (i = natom-1; (i >= 0); i--)
 +    for (i = natom - 1; (i >= 0); i--)
      {
 -        if ((std::strcmp(anm, *(atoms->atomname[i])) == 0) &&
 -            (atomnr == atoms->pdbinfo[i].atomnr))
 +        if ((std::strcmp(anm, *(atoms->atomname[i])) == 0) && (atomnr == atoms->pdbinfo[i].atomnr))
          {
              break;
          }
      }
      if (i < 0)
      {
 -        fprintf(stderr, "Skipping ANISOU record (atom %s %d not found)\n",
 -                anm, atomnr);
 +        fprintf(stderr, "Skipping ANISOU record (atom %s %d not found)\n", anm, atomnr);
      }
      else
      {
 -        if (sscanf(line+29, "%d%d%d%d%d%d",
 -                   &atoms->pdbinfo[i].uij[U11], &atoms->pdbinfo[i].uij[U22],
 +        if (sscanf(line + 29, "%d%d%d%d%d%d", &atoms->pdbinfo[i].uij[U11], &atoms->pdbinfo[i].uij[U22],
                     &atoms->pdbinfo[i].uij[U33], &atoms->pdbinfo[i].uij[U12],
                     &atoms->pdbinfo[i].uij[U13], &atoms->pdbinfo[i].uij[U23])
              == 6)
      }
  }
  
 -void get_pdb_atomnumber(const t_atoms *atoms, gmx_atomprop_t aps)
 +void get_pdb_atomnumber(const t_atoms* atoms, AtomProperties* aps)
  {
      int    i, atomnumber, len;
      size_t k;
 -    char   anm[6], anm_copy[6], *ptr;
 +    char   anm[6], anm_copy[6];
      char   nc = '\0';
      real   eval;
  
          std::strcpy(anm, atoms->pdbinfo[i].atomnm);
          std::strcpy(anm_copy, atoms->pdbinfo[i].atomnm);
          bool atomNumberSet = false;
 -        len        = strlen(anm);
 +        len                = strlen(anm);
          if ((anm[0] != ' ') && ((len <= 2) || !std::isdigit(anm[2])))
          {
              anm_copy[2] = nc;
 -            if (gmx_atomprop_query(aps, epropElement, "???", anm_copy, &eval))
 +            if (aps->setAtomProperty(epropElement, "???", anm_copy, &eval))
              {
                  atomnumber    = gmx::roundToInt(eval);
                  atomNumberSet = true;
              else
              {
                  anm_copy[1] = nc;
 -                if (gmx_atomprop_query(aps, epropElement, "???", anm_copy, &eval))
 +                if (aps->setAtomProperty(epropElement, "???", anm_copy, &eval))
                  {
                      atomnumber    = gmx::roundToInt(eval);
                      atomNumberSet = true;
              }
              anm_copy[0] = anm[k];
              anm_copy[1] = nc;
 -            if (gmx_atomprop_query(aps, epropElement, "???", anm_copy, &eval))
 +            if (aps->setAtomProperty(epropElement, "???", anm_copy, &eval))
              {
                  atomnumber    = gmx::roundToInt(eval);
                  atomNumberSet = true;
              }
          }
 +        std::string buf;
          if (atomNumberSet)
          {
              atoms->atom[i].atomnumber = atomnumber;
 -            ptr = gmx_atomprop_element(aps, atomnumber);
 +            buf                       = aps->elementFromAtomNumber(atomnumber);
              if (debug)
              {
 -                fprintf(debug, "Atomnumber for atom '%s' is %d\n",
 -                        anm, atomnumber);
 +                fprintf(debug, "Atomnumber for atom '%s' is %d\n", anm, atomnumber);
              }
          }
 -        else
 -        {
 -            ptr = nullptr;
 -        }
 -        std::strncpy(atoms->atom[i].elem, ptr == nullptr ? "" : ptr, 4);
 +        buf.resize(3);
 +        std::strncpy(atoms->atom[i].elem, buf.c_str(), 4);
      }
  }
  
 -static int read_atom(t_symtab *symtab,
 -                     const char line[], int type, int natom,
 -                     t_atoms *atoms, rvec x[], int chainnum, gmx_bool bChange)
 +static int
 +read_atom(t_symtab* symtab, const char line[], int type, int natom, t_atoms* atoms, rvec x[], int chainnum, gmx_bool bChange)
  {
 -    t_atom       *atomn;
 +    t_atom*       atomn;
      int           j, k;
      char          nc = '\0';
      char          anr[12], anm[12], anm_copy[12], altloc, resnm[12], rnr[12], elem[3];
  
      if (natom >= atoms->nr)
      {
 -        gmx_fatal(FARGS, "\nFound more atoms (%d) in pdb file than expected (%d)",
 -                  natom+1, atoms->nr);
 +        gmx_fatal(FARGS, "\nFound more atoms (%d) in pdb file than expected (%d)", natom + 1, atoms->nr);
      }
  
      /* Skip over type */
      trim(rnr);
      resnr = std::strtol(rnr, nullptr, 10);
      resic = line[j];
 -    j    += 4;
 +    j += 4;
  
      /* X,Y,Z Coordinate */
      for (k = 0; (k < 8); k++, j++)
      if (atoms->atom)
      {
          atomn = &(atoms->atom[natom]);
 -        if ((natom == 0) ||
 -            atoms->resinfo[atoms->atom[natom-1].resind].nr != resnr ||
 -            atoms->resinfo[atoms->atom[natom-1].resind].ic != resic ||
 -            (strcmp(*atoms->resinfo[atoms->atom[natom-1].resind].name, resnm) != 0))
 +        if ((natom == 0) || atoms->resinfo[atoms->atom[natom - 1].resind].nr != resnr
 +            || atoms->resinfo[atoms->atom[natom - 1].resind].ic != resic
 +            || (strcmp(*atoms->resinfo[atoms->atom[natom - 1].resind].name, resnm) != 0))
          {
              if (natom == 0)
              {
              }
              else
              {
 -                atomn->resind = atoms->atom[natom-1].resind + 1;
 +                atomn->resind = atoms->atom[natom - 1].resind + 1;
              }
              atoms->nres = atomn->resind + 1;
              t_atoms_set_resinfo(atoms, natom, symtab, resnm, resnr, resic, chainnum, chainid);
          }
          else
          {
 -            atomn->resind = atoms->atom[natom-1].resind;
 +            atomn->resind = atoms->atom[natom - 1].resind;
          }
          if (bChange)
          {
          atomn->atomnumber      = atomnumber;
          strncpy(atomn->elem, elem, 4);
      }
 -    x[natom][XX] = strtod(xc, nullptr)*0.1;
 -    x[natom][YY] = strtod(yc, nullptr)*0.1;
 -    x[natom][ZZ] = strtod(zc, nullptr)*0.1;
 +    x[natom][XX] = strtod(xc, nullptr) * 0.1;
 +    x[natom][YY] = strtod(yc, nullptr) * 0.1;
 +    x[natom][ZZ] = strtod(zc, nullptr) * 0.1;
      if (atoms->pdbinfo)
      {
          atoms->pdbinfo[natom].type   = type;
      return natom;
  }
  
 -gmx_bool is_hydrogen(const char *nm)
 +gmx_bool is_hydrogen(const charnm)
  {
      char buf[30];
  
      return FALSE;
  }
  
 -gmx_bool is_dummymass(const char *nm)
 +gmx_bool is_dummymass(const charnm)
  {
      char buf[30];
  
      std::strcpy(buf, nm);
      trim(buf);
  
 -    return (buf[0] == 'M') && (std::isdigit(buf[strlen(buf)-1]) != 0);
 +    return (buf[0] == 'M') && (std::isdigit(buf[strlen(buf) - 1]) != 0);
  }
  
 -static void gmx_conect_addline(gmx_conect_t *con, char *line)
 +static void gmx_conect_addline(gmx_conect_t* con, char* line)
  {
 -    int         n, ai, aj;
 +    int n, ai, aj;
  
      std::string form2  = "%%*s";
      std::string format = form2 + "%%d";
              n      = sscanf(line, format.c_str(), &aj);
              if (n == 1)
              {
-                 srenew(con->conect, ++con->nconect);
-                 con->conect[con->nconect - 1].ai = ai - 1;
-                 con->conect[con->nconect - 1].aj = aj - 1;
+                 gmx_conect_add(con, ai - 1, aj - 1); /* to prevent duplicated records */
              }
 -        }
 -        while (n == 1);
 +        } while (n == 1);
      }
  }
  
 -void gmx_conect_dump(FILE *fp, gmx_conect conect)
 +void gmx_conect_dump(FILEfp, gmx_conect conect)
  {
 -    gmx_conect_t *gc = static_cast<gmx_conect_t *>(conect);
 +    gmx_conect_t* gc = static_cast<gmx_conect_t*>(conect);
      int           i;
  
      for (i = 0; (i < gc->nconect); i++)
      {
 -        fprintf(fp, "%6s%5d%5d\n", "CONECT",
 -                gc->conect[i].ai+1, gc->conect[i].aj+1);
 +        fprintf(fp, "%6s%5d%5d\n", "CONECT", gc->conect[i].ai + 1, gc->conect[i].aj + 1);
      }
  }
  
  gmx_conect gmx_conect_init()
  {
 -    gmx_conect_t *gc;
 +    gmx_conect_tgc;
  
      snew(gc, 1);
  
  
  void gmx_conect_done(gmx_conect conect)
  {
 -    gmx_conect_t *gc = conect;
 +    gmx_conect_tgc = conect;
  
      sfree(gc->conect);
  }
  
  gmx_bool gmx_conect_exist(gmx_conect conect, int ai, int aj)
  {
 -    gmx_conect_t *gc = conect;
 +    gmx_conect_tgc = conect;
      int           i;
  
      /* if (!gc->bSorted)
  
      for (i = 0; (i < gc->nconect); i++)
      {
 -        if (((gc->conect[i].ai == ai) &&
 -             (gc->conect[i].aj == aj)) ||
 -            ((gc->conect[i].aj == ai) &&
 -             (gc->conect[i].ai == aj)))
 +        if (((gc->conect[i].ai == ai) && (gc->conect[i].aj == aj))
 +            || ((gc->conect[i].aj == ai) && (gc->conect[i].ai == aj)))
          {
              return TRUE;
          }
  
  void gmx_conect_add(gmx_conect conect, int ai, int aj)
  {
 -    gmx_conect_t *gc = static_cast<gmx_conect_t *>(conect);
 +    gmx_conect_t* gc = static_cast<gmx_conect_t*>(conect);
  
      /* if (!gc->bSorted)
         sort_conect(gc);*/
      if (!gmx_conect_exist(conect, ai, aj))
      {
          srenew(gc->conect, ++gc->nconect);
 -        gc->conect[gc->nconect-1].ai = ai;
 -        gc->conect[gc->nconect-1].aj = aj;
 +        gc->conect[gc->nconect - 1].ai = ai;
 +        gc->conect[gc->nconect - 1].aj = aj;
      }
  }
  
 -int read_pdbfile(FILE *in, char *title, int *model_nr,
 -                 t_atoms *atoms, t_symtab *symtab, rvec x[], int *ePBC,
 -                 matrix box, gmx_bool bChange, gmx_conect conect)
 +int read_pdbfile(FILE*      in,
 +                 char*      title,
 +                 int*       model_nr,
 +                 t_atoms*   atoms,
 +                 t_symtab*  symtab,
 +                 rvec       x[],
 +                 int*       ePBC,
 +                 matrix     box,
 +                 gmx_bool   bChange,
 +                 gmx_conect conect)
  {
 -    gmx_conect_t *gc = conect;
 +    gmx_conect_tgc = conect;
      gmx_bool      bCOMPND;
      gmx_bool      bConnWarn = FALSE;
 -    char          line[STRLEN+1];
 +    char          line[STRLEN + 1];
      int           line_type;
 -    char         *c, *d;
 +    char *        c, *d;
      int           natom, chainnum;
 -    gmx_bool      bStop   = FALSE;
 +    gmx_bool      bStop = FALSE;
  
      if (ePBC)
      {
                  }
                  break;
  
 -            case epdbCRYST1:
 -                read_cryst1(line, ePBC, box);
 -                break;
 +            case epdbCRYST1: read_cryst1(line, ePBC, box); break;
  
              case epdbTITLE:
              case epdbHEADER:
                  if (std::strlen(line) > 6)
                  {
 -                    c = line+6;
 +                    c = line + 6;
                      /* skip HEADER or TITLE and spaces */
                      while (c[0] != ' ')
                      {
                  break;
  
              case epdbCOMPND:
 -                if ((!std::strstr(line, ": ")) || (std::strstr(line+6, "MOLECULE:")))
 +                if ((!std::strstr(line, ": ")) || (std::strstr(line + 6, "MOLECULE:")))
                  {
 -                    if (!(c = std::strstr(line+6, "MOLECULE:")) )
 +                    if (!(c = std::strstr(line + 6, "MOLECULE:")))
                      {
                          c = line;
                      }
                      d = strstr(c, "   ");
                      if (d)
                      {
 -                        while ( (d[-1] == ';') && d > c)
 +                        while ((d[-1] == ';') && d > c)
                          {
                              d--;
                          }
                  }
                  break;
  
 -            case epdbTER:
 -                chainnum++;
 -                break;
 +            case epdbTER: chainnum++; break;
  
              case epdbMODEL:
                  if (model_nr)
                  }
                  break;
  
 -            case epdbENDMDL:
 -                bStop = TRUE;
 -                break;
 +            case epdbENDMDL: bStop = TRUE; break;
              case epdbCONECT:
                  if (gc)
                  {
                  }
                  break;
  
 -            default:
 -                break;
 +            default: break;
          }
      }
  
      return natom;
  }
  
 -void get_pdb_coordnum(FILE *in, int *natoms)
 +void get_pdb_coordnum(FILE* in, int* natoms)
  {
      char line[STRLEN];
  
      }
  }
  
 -void gmx_pdb_read_conf(const char *infile,
 -                       t_symtab *symtab, char **name, t_atoms *atoms,
 -                       rvec x[], int *ePBC, matrix box)
 +void gmx_pdb_read_conf(const char* infile, t_symtab* symtab, char** name, t_atoms* atoms, rvec x[], int* ePBC, matrix box)
  {
 -    FILE *in = gmx_fio_fopen(infile, "r");
 +    FILEin = gmx_fio_fopen(infile, "r");
      char  title[STRLEN];
      read_pdbfile(in, title, nullptr, atoms, symtab, x, ePBC, box, TRUE, nullptr);
      if (name != nullptr)
      gmx_fio_fclose(in);
  }
  
 -gmx_conect gmx_conect_generate(const t_topology *top)
 +gmx_conect gmx_conect_generate(const t_topologytop)
  {
      int        f, i;
      gmx_conect gc;
  
      /* Fill the conect records */
 -    gc  = gmx_conect_init();
 +    gc = gmx_conect_init();
  
      for (f = 0; (f < F_NRE); f++)
      {
          if (IS_CHEMBOND(f))
          {
 -            for (i = 0; (i < top->idef.il[f].nr); i += interaction_function[f].nratoms+1)
 +            for (i = 0; (i < top->idef.il[f].nr); i += interaction_function[f].nratoms + 1)
              {
 -                gmx_conect_add(gc, top->idef.il[f].iatoms[i+1],
 -                               top->idef.il[f].iatoms[i+2]);
 +                gmx_conect_add(gc, top->idef.il[f].iatoms[i + 1], top->idef.il[f].iatoms[i + 2]);
              }
          }
      }
      return gc;
  }
  
 -int
 -gmx_fprintf_pdb_atomline(FILE *            fp,
 -                         enum PDB_record   record,
 -                         int               atom_seq_number,
 -                         const char *      atom_name,
 -                         char              alternate_location,
 -                         const char *      res_name,
 -                         char              chain_id,
 -                         int               res_seq_number,
 -                         char              res_insertion_code,
 -                         real              x,
 -                         real              y,
 -                         real              z,
 -                         real              occupancy,
 -                         real              b_factor,
 -                         const char *      element)
 +int gmx_fprintf_pdb_atomline(FILE*           fp,
 +                             enum PDB_record record,
 +                             int             atom_seq_number,
 +                             const char*     atom_name,
 +                             char            alternate_location,
 +                             const char*     res_name,
 +                             char            chain_id,
 +                             int             res_seq_number,
 +                             char            res_insertion_code,
 +                             real            x,
 +                             real            y,
 +                             real            z,
 +                             real            occupancy,
 +                             real            b_factor,
 +                             const char*     element)
  {
      char     tmp_atomname[6], tmp_resname[6];
      gmx_bool start_name_in_col13;
          /* If the atom name is an element name with two chars, it should start already in column 13.
           * Otherwise it should start in column 14, unless the name length is 4 chars.
           */
 -        if ( (element != nullptr) && (std::strlen(element) >= 2) && (gmx_strncasecmp(atom_name, element, 2) == 0) )
 +        if ((element != nullptr) && (std::strlen(element) >= 2)
 +            && (gmx_strncasecmp(atom_name, element, 2) == 0))
          {
              start_name_in_col13 = TRUE;
          }
      atom_seq_number = atom_seq_number % 100000;
      res_seq_number  = res_seq_number % 10000;
  
 -    n = fprintf(fp,
 -                "%-6s%5d %-4.4s%c%4.4s%c%4d%c   %8.3f%8.3f%8.3f%6.2f%6.2f          %2s\n",
 -                pdbtp[record],
 -                atom_seq_number,
 -                tmp_atomname,
 -                alternate_location,
 -                tmp_resname,
 -                chain_id,
 -                res_seq_number,
 -                res_insertion_code,
 -                x, y, z,
 -                occupancy,
 -                b_factor,
 +    n = fprintf(fp, "%-6s%5d %-4.4s%c%4.4s%c%4d%c   %8.3f%8.3f%8.3f%6.2f%6.2f          %2s\n",
 +                pdbtp[record], atom_seq_number, tmp_atomname, alternate_location, tmp_resname,
 +                chain_id, res_seq_number, res_insertion_code, x, y, z, occupancy, b_factor,
                  (element != nullptr) ? element : "");
  
      return n;
index 4e159aa7606a9e68e05966f421ee30a7586e9fe1,aaa86cc9819d7c43269f7b12054dc456b315ea12..c20deec924102628ce1a33e4c3a6cfa4709c0e39
@@@ -46,9 -46,9 +46,9 @@@
  #include <cstdlib>
  
  #include <algorithm>
 +#include <memory>
  
  #include "gromacs/commandline/filenm.h"
 -#include "gromacs/compat/make_unique.h"
  #include "gromacs/domdec/domdec_struct.h"
  #include "gromacs/domdec/localatomset.h"
  #include "gromacs/domdec/localatomsetmanager.h"
@@@ -59,6 -59,7 +59,6 @@@
  #include "gromacs/math/vec.h"
  #include "gromacs/math/vectypes.h"
  #include "gromacs/mdlib/gmx_omp_nthreads.h"
 -#include "gromacs/mdlib/mdrun.h"
  #include "gromacs/mdtypes/commrec.h"
  #include "gromacs/mdtypes/forceoutput.h"
  #include "gromacs/mdtypes/inputrec.h"
  
  #include "pull_internal.h"
  
 -static int groupPbcFromParams(const t_pull_group &params, bool setPbcRefToPrevStepCOM)
 +namespace gmx
 +{
 +extern template LocalAtomSet LocalAtomSetManager::add<void, void>(ArrayRef<const int> globalAtomIndex);
 +} // namespace gmx
 +
 +static int groupPbcFromParams(const t_pull_group& params, bool setPbcRefToPrevStepCOM)
  {
      if (params.nat <= 1)
      {
   *       should not end before that of this object.
   *       This will be fixed when the pointers are replacted by std::vector.
   */
 -pull_group_work_t::pull_group_work_t(const t_pull_group &params,
 +pull_group_work_t::pull_group_work_t(const t_pull_groupparams,
                                       gmx::LocalAtomSet   atomSet,
                                       bool                bSetPbcRefToPrevStepCOM) :
      params(params),
      clear_dvec(xp);
  };
  
 -bool pull_coordinate_is_angletype(const t_pull_coord *pcrd)
 +bool pull_coordinate_is_angletype(const t_pull_coordpcrd)
  {
 -    return (pcrd->eGeom == epullgANGLE ||
 -            pcrd->eGeom == epullgDIHEDRAL ||
 -            pcrd->eGeom == epullgANGLEAXIS);
 +    return (pcrd->eGeom == epullgANGLE || pcrd->eGeom == epullgDIHEDRAL || pcrd->eGeom == epullgANGLEAXIS);
  }
  
 -static bool pull_coordinate_is_directional(const t_pull_coord *pcrd)
 +static bool pull_coordinate_is_directional(const t_pull_coordpcrd)
  {
 -    return (pcrd->eGeom == epullgDIR ||
 -            pcrd->eGeom == epullgDIRPBC ||
 -            pcrd->eGeom == epullgDIRRELATIVE ||
 -            pcrd->eGeom == epullgCYL);
 +    return (pcrd->eGeom == epullgDIR || pcrd->eGeom == epullgDIRPBC
 +            || pcrd->eGeom == epullgDIRRELATIVE || pcrd->eGeom == epullgCYL);
  }
  
 -const char *pull_coordinate_units(const t_pull_coord *pcrd)
 +const char* pull_coordinate_units(const t_pull_coord* pcrd)
  {
 -    return pull_coordinate_is_angletype(pcrd) ?  "deg" : "nm";
 +    return pull_coordinate_is_angletype(pcrd) ? "deg" : "nm";
  }
  
 -double pull_conversion_factor_userinput2internal(const t_pull_coord *pcrd)
 +double pull_conversion_factor_userinput2internal(const t_pull_coordpcrd)
  {
      if (pull_coordinate_is_angletype(pcrd))
      {
      }
  }
  
 -double pull_conversion_factor_internal2userinput(const t_pull_coord *pcrd)
 +double pull_conversion_factor_internal2userinput(const t_pull_coordpcrd)
  {
      if (pull_coordinate_is_angletype(pcrd))
      {
  }
  
  /* Apply forces in a mass weighted fashion for part of the pull group */
 -static void apply_forces_grp_part(const pull_group_work_t *pgrp,
 -                                  int ind_start, int ind_end,
 -                                  const t_mdatoms *md,
 -                                  const dvec f_pull, int sign, rvec *f)
 +static void apply_forces_grp_part(const pull_group_work_t* pgrp,
 +                                  int                      ind_start,
 +                                  int                      ind_end,
 +                                  const t_mdatoms*         md,
 +                                  const dvec               f_pull,
 +                                  int                      sign,
 +                                  rvec*                    f)
  {
      double inv_wm = pgrp->mwscale;
  
 -    auto   localAtomIndices = pgrp->atomSet.localIndex();
 +    auto localAtomIndices = pgrp->atomSet.localIndex();
      for (int i = ind_start; i < ind_end; i++)
      {
          int    ii    = localAtomIndices[i];
  }
  
  /* Apply forces in a mass weighted fashion */
 -static void apply_forces_grp(const pull_group_work_t *pgrp,
 -                             const t_mdatoms *md,
 -                             const dvec f_pull, int sign, rvec *f,
 -                             int nthreads)
 +static void apply_forces_grp(const pull_group_work_t* pgrp,
 +                             const t_mdatoms*         md,
 +                             const dvec               f_pull,
 +                             int                      sign,
 +                             rvec*                    f,
 +                             int                      nthreads)
  {
      auto localAtomIndices = pgrp->atomSet.localIndex();
  
           */
          for (int d = 0; d < DIM; d++)
          {
 -            f[localAtomIndices[0]][d] += sign*f_pull[d];
 +            f[localAtomIndices[0]][d] += sign * f_pull[d];
          }
      }
      else
  #pragma omp parallel for num_threads(nthreads) schedule(static)
              for (int th = 0; th < nthreads; th++)
              {
 -                int ind_start = (localAtomIndices.size()*(th + 0))/nthreads;
 -                int ind_end   = (localAtomIndices.size()*(th + 1))/nthreads;
 -                apply_forces_grp_part(pgrp, ind_start, ind_end,
 -                                      md, f_pull, sign, f);
 +                int ind_start = (localAtomIndices.size() * (th + 0)) / nthreads;
 +                int ind_end   = (localAtomIndices.size() * (th + 1)) / nthreads;
 +                apply_forces_grp_part(pgrp, ind_start, ind_end, md, f_pull, sign, f);
              }
          }
      }
  }
  
  /* Apply forces in a mass weighted fashion to a cylinder group */
 -static void apply_forces_cyl_grp(const pull_group_work_t *pgrp,
 -                                 const double dv_corr,
 -                                 const t_mdatoms *md,
 -                                 const dvec f_pull, double f_scal,
 -                                 int sign, rvec *f,
 +static void apply_forces_cyl_grp(const pull_group_work_t* pgrp,
 +                                 const double             dv_corr,
 +                                 const t_mdatoms*         md,
 +                                 const dvec               f_pull,
 +                                 double                   f_scal,
 +                                 int                      sign,
 +                                 rvec*                    f,
                                   int gmx_unused nthreads)
  {
      double inv_wm = pgrp->mwscale;
  
 -    auto   localAtomIndices = pgrp->atomSet.localIndex();
 +    auto localAtomIndices = pgrp->atomSet.localIndex();
  
      /* The cylinder group is always a slab in the system, thus large.
       * Therefore we always thread-parallelize this group.
          {
              continue;
          }
 -        int    ii     = localAtomIndices[i];
 -        double mass   = md->massT[ii];
 +        int    ii   = localAtomIndices[i];
 +        double mass = md->massT[ii];
          /* The stored axial distance from the cylinder center (dv) needs
           * to be corrected for an offset (dv_corr), which was unknown when
           * we calculated dv.
           */
          for (int m = 0; m < DIM; m++)
          {
 -            f[ii][m] += sign*inv_wm*(mass*weight*f_pull[m] +
 -                                     pgrp->mdw[i][m]*dv_com*f_scal);
 +            f[ii][m] += sign * inv_wm * (mass * weight * f_pull[m] + pgrp->mdw[i][m] * dv_com * f_scal);
          }
      }
  }
  /* Apply torque forces in a mass weighted fashion to the groups that define
   * the pull vector direction for pull coordinate pcrd.
   */
 -static void apply_forces_vec_torque(const struct pull_t     *pull,
 -                                    const pull_coord_work_t *pcrd,
 -                                    const t_mdatoms         *md,
 -                                    rvec                    *f)
 +static void apply_forces_vec_torque(const struct pull_t*     pull,
 +                                    const pull_coord_work_tpcrd,
 +                                    const t_mdatoms*         md,
 +                                    rvec*                    f)
  {
 -    const PullCoordSpatialData &spatialData = pcrd->spatialData;
 +    const PullCoordSpatialDataspatialData = pcrd->spatialData;
  
      /* The component inpr along the pull vector is accounted for in the usual
       * way. Here we account for the component perpendicular to vec.
      double inpr = 0;
      for (int m = 0; m < DIM; m++)
      {
 -        inpr += spatialData.dr01[m]*spatialData.vec[m];
 +        inpr += spatialData.dr01[m] * spatialData.vec[m];
      }
      /* The torque force works along the component of the distance vector
       * of between the two "usual" pull groups that is perpendicular to
      dvec f_perp;
      for (int m = 0; m < DIM; m++)
      {
 -        f_perp[m] = (spatialData.dr01[m] - inpr*spatialData.vec[m])/spatialData.vec_len*pcrd->scalarForce;
 +        f_perp[m] = (spatialData.dr01[m] - inpr * spatialData.vec[m]) / spatialData.vec_len * pcrd->scalarForce;
      }
  
      /* Apply the force to the groups defining the vector using opposite signs */
 -    apply_forces_grp(&pull->group[pcrd->params.group[2]], md,
 -                     f_perp, -1, f, pull->nthreads);
 -    apply_forces_grp(&pull->group[pcrd->params.group[3]], md,
 -                     f_perp,  1, f, pull->nthreads);
 +    apply_forces_grp(&pull->group[pcrd->params.group[2]], md, f_perp, -1, f, pull->nthreads);
 +    apply_forces_grp(&pull->group[pcrd->params.group[3]], md, f_perp, 1, f, pull->nthreads);
  }
  
  /* Apply forces in a mass weighted fashion */
 -static void apply_forces_coord(struct pull_t * pull, int coord,
 -                               const PullCoordVectorForces &forces,
 -                               const t_mdatoms * md,
 -                               rvec *f)
 +static void apply_forces_coord(struct pull_t*               pull,
 +                               int                          coord,
 +                               const PullCoordVectorForces& forces,
 +                               const t_mdatoms*             md,
 +                               rvec*                        f)
  {
      /* Here it would be more efficient to use one large thread-parallel
       * region instead of potential parallel regions within apply_forces_grp.
       * to data races.
       */
  
 -    const pull_coord_work_t &pcrd = pull->coord[coord];
 +    const pull_coord_work_tpcrd = pull->coord[coord];
  
      if (pcrd.params.eGeom == epullgCYL)
      {
 -        apply_forces_cyl_grp(&pull->dyna[coord], pcrd.spatialData.cyl_dev, md,
 -                             forces.force01, pcrd.scalarForce, -1, f,
 -                             pull->nthreads);
 +        apply_forces_cyl_grp(&pull->dyna[coord], pcrd.spatialData.cyl_dev, md, forces.force01,
 +                             pcrd.scalarForce, -1, f, pull->nthreads);
  
          /* Sum the force along the vector and the radial force */
          dvec f_tot;
          for (int m = 0; m < DIM; m++)
          {
 -            f_tot[m] = forces.force01[m] + pcrd.scalarForce*pcrd.spatialData.ffrad[m];
 +            f_tot[m] = forces.force01[m] + pcrd.scalarForce * pcrd.spatialData.ffrad[m];
          }
 -        apply_forces_grp(&pull->group[pcrd.params.group[1]], md,
 -                         f_tot, 1, f, pull->nthreads);
 +        apply_forces_grp(&pull->group[pcrd.params.group[1]], md, f_tot, 1, f, pull->nthreads);
      }
      else
      {
  
          if (pull->group[pcrd.params.group[0]].params.nat > 0)
          {
 -            apply_forces_grp(&pull->group[pcrd.params.group[0]], md,
 -                             forces.force01, -1, f, pull->nthreads);
 +            apply_forces_grp(&pull->group[pcrd.params.group[0]], md, forces.force01, -1, f, pull->nthreads);
          }
 -        apply_forces_grp(&pull->group[pcrd.params.group[1]], md,
 -                         forces.force01, 1, f, pull->nthreads);
 +        apply_forces_grp(&pull->group[pcrd.params.group[1]], md, forces.force01, 1, f, pull->nthreads);
  
          if (pcrd.params.ngroup >= 4)
          {
 -            apply_forces_grp(&pull->group[pcrd.params.group[2]], md,
 -                             forces.force23, -1, f, pull->nthreads);
 -            apply_forces_grp(&pull->group[pcrd.params.group[3]], md,
 -                             forces.force23,  1, f, pull->nthreads);
 +            apply_forces_grp(&pull->group[pcrd.params.group[2]], md, forces.force23, -1, f, pull->nthreads);
 +            apply_forces_grp(&pull->group[pcrd.params.group[3]], md, forces.force23, 1, f, pull->nthreads);
          }
          if (pcrd.params.ngroup >= 6)
          {
 -            apply_forces_grp(&pull->group[pcrd.params.group[4]], md,
 -                             forces.force45, -1, f, pull->nthreads);
 -            apply_forces_grp(&pull->group[pcrd.params.group[5]], md,
 -                             forces.force45,  1, f, pull->nthreads);
 +            apply_forces_grp(&pull->group[pcrd.params.group[4]], md, forces.force45, -1, f, pull->nthreads);
 +            apply_forces_grp(&pull->group[pcrd.params.group[5]], md, forces.force45, 1, f, pull->nthreads);
          }
      }
  }
  
 -real max_pull_distance2(const pull_coord_work_t *pcrd,
 -                        const t_pbc             *pbc)
 +real max_pull_distance2(const pull_coord_work_t* pcrd, const t_pbc* pbc)
  {
      /* Note that this maximum distance calculation is more complex than
       * most other cases in GROMACS, since here we have to take care of
          }
      }
  
 -    return 0.25*max_d2;
 +    return 0.25 * max_d2;
  }
  
  /* This function returns the distance based on coordinates xg and xref.
   * Note that the pull coordinate struct pcrd is not modified.
   */
 -static void low_get_pull_coord_dr(const struct pull_t *pull,
 -                                  const pull_coord_work_t *pcrd,
 -                                  const t_pbc *pbc,
 -                                  dvec xg, dvec xref, double max_dist2,
 -                                  dvec dr)
 +static void low_get_pull_coord_dr(const struct pull_t*     pull,
 +                                  const pull_coord_work_t* pcrd,
 +                                  const t_pbc*             pbc,
 +                                  dvec                     xg,
 +                                  dvec                     xref,
 +                                  double                   max_dist2,
 +                                  dvec                     dr)
  {
 -    const pull_group_work_t *pgrp0 = &pull->group[pcrd->params.group[0]];
 +    const pull_group_work_tpgrp0 = &pull->group[pcrd->params.group[0]];
  
      /* Only the first group can be an absolute reference, in that case nat=0 */
      if (pgrp0->params.nat == 0)
      dvec xrefr;
      copy_dvec(xref, xrefr);
  
 -    dvec dref = {0, 0, 0};
 +    dvec dref = { 0, 0, 0 };
      if (pcrd->params.eGeom == epullgDIRPBC)
      {
          for (int m = 0; m < DIM; m++)
          {
 -            dref[m] = pcrd->value_ref*pcrd->spatialData.vec[m];
 +            dref[m] = pcrd->value_ref * pcrd->spatialData.vec[m];
          }
          /* Add the reference position, so we use the correct periodic image */
          dvec_inc(xrefr, dref);
          dr[m] *= pcrd->params.dim[m];
          if (pcrd->params.dim[m] && !(directional && pcrd->spatialData.vec[m] == 0))
          {
 -            dr2 += dr[m]*dr[m];
 +            dr2 += dr[m] * dr[m];
          }
      }
      /* Check if we are close to switching to another periodic image */
 -    if (max_dist2 > 0 && dr2 > 0.98*0.98*max_dist2)
 +    if (max_dist2 > 0 && dr2 > 0.98 * 0.98 * max_dist2)
      {
          /* Note that technically there is no issue with switching periodic
           * image, as pbc_dx_d returns the distance to the closest periodic
           * image. However in all cases where periodic image switches occur,
           * the pull results will be useless in practice.
           */
 -        gmx_fatal(FARGS, "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the box size (%f).\n%s",
 -                  pcrd->params.group[0], pcrd->params.group[1],
 -                  sqrt(dr2), sqrt(0.98*0.98*max_dist2),
 -                  pcrd->params.eGeom == epullgDIR ? "You might want to consider using \"pull-geometry = direction-periodic\" instead.\n" : "");
 +        gmx_fatal(FARGS,
 +                  "Distance between pull groups %d and %d (%f nm) is larger than 0.49 times the "
 +                  "box size (%f).\n%s",
 +                  pcrd->params.group[0], pcrd->params.group[1], sqrt(dr2),
 +                  sqrt(0.98 * 0.98 * max_dist2), pcrd->params.eGeom == epullgDIR ? "You might want to consider using \"pull-geometry = direction-periodic\" instead.\n" : "");
      }
  
      if (pcrd->params.eGeom == epullgDIRPBC)
  /* This function returns the distance based on the contents of the pull struct.
   * pull->coord[coord_ind].dr, and potentially vector, are updated.
   */
 -static void get_pull_coord_dr(struct pull_t *pull,
 -                              int            coord_ind,
 -                              const t_pbc   *pbc)
 +static void get_pull_coord_dr(struct pull_t* pull, int coord_ind, const t_pbc* pbc)
  {
 -    pull_coord_work_t    *pcrd        = &pull->coord[coord_ind];
 -    PullCoordSpatialData &spatialData = pcrd->spatialData;
 +    pull_coord_work_t*    pcrd        = &pull->coord[coord_ind];
 +    PullCoordSpatialDataspatialData = pcrd->spatialData;
  
 -    double                md2;
 +    double md2;
-     if (pcrd->params.eGeom == epullgDIRPBC)
+     /* With AWH pulling we allow for periodic pulling with geometry=direction.
+      * TODO: Store a periodicity flag instead of checking for external pull provider.
+      */
 -    if (pcrd->params.eGeom == epullgDIRPBC || (pcrd->params.eGeom == epullgDIR &&
 -                                               pcrd->params.eType == epullEXTERNAL))
++    if (pcrd->params.eGeom == epullgDIRPBC
++        || (pcrd->params.eGeom == epullgDIR && pcrd->params.eType == epullEXTERNAL))
      {
          md2 = -1;
      }
      if (pcrd->params.eGeom == epullgDIRRELATIVE)
      {
          /* We need to determine the pull vector */
 -        dvec                     vec;
 -        int                      m;
 +        dvec vec;
 +        int  m;
  
 -        const pull_group_work_t &pgrp2 = pull->group[pcrd->params.group[2]];
 -        const pull_group_work_t &pgrp3 = pull->group[pcrd->params.group[3]];
 +        const pull_group_work_tpgrp2 = pull->group[pcrd->params.group[2]];
 +        const pull_group_work_tpgrp3 = pull->group[pcrd->params.group[3]];
  
          pbc_dx_d(pbc, pgrp3.x, pgrp2.x, vec);
  
          spatialData.vec_len = dnorm(vec);
          for (m = 0; m < DIM; m++)
          {
 -            spatialData.vec[m] = vec[m]/spatialData.vec_len;
 +            spatialData.vec[m] = vec[m] / spatialData.vec_len;
          }
          if (debug)
          {
              fprintf(debug, "pull coord %d vector: %6.3f %6.3f %6.3f normalized: %6.3f %6.3f %6.3f\n",
 -                    coord_ind,
 -                    vec[XX], vec[YY], vec[ZZ],
 -                    spatialData.vec[XX], spatialData.vec[YY], spatialData.vec[ZZ]);
 +                    coord_ind, vec[XX], vec[YY], vec[ZZ], spatialData.vec[XX], spatialData.vec[YY],
 +                    spatialData.vec[ZZ]);
          }
      }
  
 -    pull_group_work_t *pgrp0 = &pull->group[pcrd->params.group[0]];
 -    pull_group_work_t *pgrp1 = &pull->group[pcrd->params.group[1]];
 +    pull_group_work_tpgrp0 = &pull->group[pcrd->params.group[0]];
 +    pull_group_work_tpgrp1 = &pull->group[pcrd->params.group[1]];
  
 -    low_get_pull_coord_dr(pull, pcrd, pbc,
 -                          pgrp1->x,
 -                          pcrd->params.eGeom == epullgCYL ? pull->dyna[coord_ind].x : pgrp0->x,
 -                          md2,
 +    low_get_pull_coord_dr(pull, pcrd, pbc, pgrp1->x,
 +                          pcrd->params.eGeom == epullgCYL ? pull->dyna[coord_ind].x : pgrp0->x, md2,
                            spatialData.dr01);
  
      if (pcrd->params.ngroup >= 4)
          pgrp2 = &pull->group[pcrd->params.group[2]];
          pgrp3 = &pull->group[pcrd->params.group[3]];
  
 -        low_get_pull_coord_dr(pull, pcrd, pbc,
 -                              pgrp3->x,
 -                              pgrp2->x,
 -                              md2,
 -                              spatialData.dr23);
 +        low_get_pull_coord_dr(pull, pcrd, pbc, pgrp3->x, pgrp2->x, md2, spatialData.dr23);
      }
      if (pcrd->params.ngroup >= 6)
      {
          pgrp4 = &pull->group[pcrd->params.group[4]];
          pgrp5 = &pull->group[pcrd->params.group[5]];
  
 -        low_get_pull_coord_dr(pull, pcrd, pbc,
 -                              pgrp5->x,
 -                              pgrp4->x,
 -                              md2,
 -                              spatialData.dr45);
 +        low_get_pull_coord_dr(pull, pcrd, pbc, pgrp5->x, pgrp4->x, md2, spatialData.dr45);
      }
  }
  
   * It is assumed that x is in [-3pi, 3pi) so that x
   * needs to be shifted by at most one period.
   */
 -static void make_periodic_2pi(double *x)
 +static void make_periodic_2pi(doublex)
  {
      if (*x >= M_PI)
      {
  }
  
  /* This function should always be used to modify pcrd->value_ref */
 -static void low_set_pull_coord_reference_value(pull_coord_work_t  *pcrd,
 -                                               int                 coord_ind,
 -                                               double              value_ref)
 +static void low_set_pull_coord_reference_value(pull_coord_work_t* pcrd, int coord_ind, double value_ref)
  {
 -    GMX_ASSERT(pcrd->params.eType != epullEXTERNAL, "The pull coord reference value should not be used with type external-potential");
 +    GMX_ASSERT(pcrd->params.eType != epullEXTERNAL,
 +               "The pull coord reference value should not be used with type external-potential");
  
      if (pcrd->params.eGeom == epullgDIST)
      {
          if (value_ref < 0)
          {
 -            gmx_fatal(FARGS, "Pull reference distance for coordinate %d (%f) needs to be non-negative", coord_ind + 1, value_ref);
 +            gmx_fatal(FARGS,
 +                      "Pull reference distance for coordinate %d (%f) needs to be non-negative",
 +                      coord_ind + 1, value_ref);
          }
      }
      else if (pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgANGLEAXIS)
      {
          if (value_ref < 0 || value_ref > M_PI)
          {
 -            gmx_fatal(FARGS, "Pull reference angle for coordinate %d (%f) needs to be in the allowed interval [0,180] deg", coord_ind + 1, value_ref*pull_conversion_factor_internal2userinput(&pcrd->params));
 +            gmx_fatal(FARGS,
 +                      "Pull reference angle for coordinate %d (%f) needs to be in the allowed "
 +                      "interval [0,180] deg",
 +                      coord_ind + 1,
 +                      value_ref * pull_conversion_factor_internal2userinput(&pcrd->params));
          }
      }
      else if (pcrd->params.eGeom == epullgDIHEDRAL)
      pcrd->value_ref = value_ref;
  }
  
 -static void update_pull_coord_reference_value(pull_coord_work_t *pcrd, int coord_ind, double t)
 +static void update_pull_coord_reference_value(pull_coord_work_tpcrd, int coord_ind, double t)
  {
      /* With zero rate the reference value is set initially and doesn't change */
      if (pcrd->params.rate != 0)
      {
 -        double value_ref = (pcrd->params.init + pcrd->params.rate*t)*pull_conversion_factor_userinput2internal(&pcrd->params);
 +        double value_ref = (pcrd->params.init + pcrd->params.rate * t)
 +                           * pull_conversion_factor_userinput2internal(&pcrd->params);
          low_set_pull_coord_reference_value(pcrd, coord_ind, value_ref);
      }
  }
  
  /* Returns the dihedral angle. Updates the plane normal vectors m, n. */
 -static double get_dihedral_angle_coord(PullCoordSpatialData *spatialData)
 +static double get_dihedral_angle_coord(PullCoordSpatialDataspatialData)
  {
      double phi, sign;
      dvec   dr32; /* store instead of dr23? */
  
      dsvmul(-1, spatialData->dr23, dr32);
 -    dcprod(spatialData->dr01, dr32, spatialData->planevec_m);  /* Normal of first plane */
 -    dcprod(dr32, spatialData->dr45, spatialData->planevec_n);  /* Normal of second plane */
 +    dcprod(spatialData->dr01, dr32, spatialData->planevec_m); /* Normal of first plane */
 +    dcprod(dr32, spatialData->dr45, spatialData->planevec_n); /* Normal of second plane */
      phi = gmx_angle_between_dvecs(spatialData->planevec_m, spatialData->planevec_n);
  
      /* Note 1: the sign below is opposite of that in the bondeds or Bekker 1994
       * we get a positive sign below. Thus, the range of the dihedral angle is (-180, 180].
       */
      sign = (diprod(spatialData->dr01, spatialData->planevec_n) < 0.0) ? 1.0 : -1.0;
 -    return sign*phi;
 +    return sign * phi;
  }
  
  /* Calculates pull->coord[coord_ind].value.
   * This function also updates pull->coord[coord_ind].dr.
   */
 -static void get_pull_coord_distance(struct pull_t *pull,
 -                                    int            coord_ind,
 -                                    const t_pbc   *pbc)
 +static void get_pull_coord_distance(struct pull_t* pull, int coord_ind, const t_pbc* pbc)
  {
 -    pull_coord_work_t *pcrd;
 +    pull_coord_work_tpcrd;
      int                m;
  
      pcrd = &pull->coord[coord_ind];
  
      get_pull_coord_dr(pull, coord_ind, pbc);
  
 -    PullCoordSpatialData &spatialData = pcrd->spatialData;
 +    PullCoordSpatialDataspatialData = pcrd->spatialData;
  
      switch (pcrd->params.eGeom)
      {
              spatialData.value = 0;
              for (m = 0; m < DIM; m++)
              {
 -                spatialData.value += spatialData.vec[m]*spatialData.dr01[m];
 +                spatialData.value += spatialData.vec[m] * spatialData.dr01[m];
              }
              break;
          case epullgANGLE:
              spatialData.value = gmx_angle_between_dvecs(spatialData.dr01, spatialData.dr23);
              break;
 -        case epullgDIHEDRAL:
 -            spatialData.value = get_dihedral_angle_coord(&spatialData);
 -            break;
 +        case epullgDIHEDRAL: spatialData.value = get_dihedral_angle_coord(&spatialData); break;
          case epullgANGLEAXIS:
              spatialData.value = gmx_angle_between_dvecs(spatialData.dr01, spatialData.vec);
              break;
 -        default:
 -            gmx_incons("Unsupported pull type in get_pull_coord_distance");
 +        default: gmx_incons("Unsupported pull type in get_pull_coord_distance");
      }
  }
  
  /* Returns the deviation from the reference value.
   * Updates pull->coord[coord_ind].dr, .value and .value_ref.
   */
 -static double get_pull_coord_deviation(struct pull_t *pull,
 -                                       int            coord_ind,
 -                                       const t_pbc   *pbc,
 -                                       double         t)
 +static double get_pull_coord_deviation(struct pull_t* pull, int coord_ind, const t_pbc* pbc, double t)
  {
 -    pull_coord_work_t *pcrd;
 +    pull_coord_work_tpcrd;
      double             dev = 0;
  
      pcrd = &pull->coord[coord_ind];
      return dev;
  }
  
 -double get_pull_coord_value(struct pull_t *pull,
 -                            int            coord_ind,
 -                            const t_pbc   *pbc)
 +double get_pull_coord_value(struct pull_t* pull, int coord_ind, const t_pbc* pbc)
  {
      get_pull_coord_distance(pull, coord_ind, pbc);
  
      return pull->coord[coord_ind].spatialData.value;
  }
  
 -void clear_pull_forces(pull_t *pull)
 +void clear_pull_forces(pull_tpull)
  {
      /* Zeroing the forces is only required for constraint pulling.
       * It can happen that multiple constraint steps need to be applied
       * and therefore the constraint forces need to be accumulated.
       */
 -    for (pull_coord_work_t &coord : pull->coord)
 +    for (pull_coord_work_tcoord : pull->coord)
      {
          coord.scalarForce = 0;
      }
  }
  
  /* Apply constraint using SHAKE */
 -static void do_constraint(struct pull_t *pull, t_pbc *pbc,
 -                          rvec *x, rvec *v,
 -                          gmx_bool bMaster, tensor vir,
 -                          double dt, double t)
 +static void
 +do_constraint(struct pull_t* pull, t_pbc* pbc, rvec* x, rvec* v, gmx_bool bMaster, tensor vir, double dt, double t)
  {
  
 -    dvec         *r_ij;   /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */
 -    dvec          unc_ij; /* xp[i] com of i this step, before constr.   -> unc_ij  */
 -    dvec         *rnew;   /* current 'new' positions of the groups */
 -    double       *dr_tot; /* the total update of the coords */
 -    dvec          vec;
 -    double        inpr;
 -    double        lambda, rm, invdt = 0;
 -    gmx_bool      bConverged_all, bConverged = FALSE;
 -    int           niter = 0, ii, j, m, max_iter = 100;
 -    double        a;
 -    dvec          tmp, tmp3;
 -
 -    snew(r_ij,   pull->coord.size());
 +    dvec*    r_ij;   /* x[i] com of i in prev. step. Obeys constr. -> r_ij[i] */
 +    dvec     unc_ij; /* xp[i] com of i this step, before constr.   -> unc_ij  */
 +    dvec*    rnew;   /* current 'new' positions of the groups */
 +    double*  dr_tot; /* the total update of the coords */
 +    dvec     vec;
 +    double   inpr;
 +    double   lambda, rm, invdt = 0;
 +    gmx_bool bConverged_all, bConverged = FALSE;
 +    int      niter = 0, ii, j, m, max_iter = 100;
 +    double   a;
 +    dvec     tmp, tmp3;
 +
 +    snew(r_ij, pull->coord.size());
      snew(dr_tot, pull->coord.size());
  
      snew(rnew, pull->group.size());
      /* Determine the constraint directions from the old positions */
      for (size_t c = 0; c < pull->coord.size(); c++)
      {
 -        pull_coord_work_t *pcrd;
 +        pull_coord_work_tpcrd;
  
          pcrd = &pull->coord[c];
  
           */
          get_pull_coord_distance(pull, c, pbc);
  
 -        const PullCoordSpatialData &spatialData = pcrd->spatialData;
 +        const PullCoordSpatialDataspatialData = pcrd->spatialData;
          if (debug)
          {
 -            fprintf(debug, "Pull coord %zu dr %f %f %f\n",
 -                    c, spatialData.dr01[XX], spatialData.dr01[YY], spatialData.dr01[ZZ]);
 +            fprintf(debug, "Pull coord %zu dr %f %f %f\n", c, spatialData.dr01[XX],
 +                    spatialData.dr01[YY], spatialData.dr01[ZZ]);
          }
  
 -        if (pcrd->params.eGeom == epullgDIR ||
 -            pcrd->params.eGeom == epullgDIRPBC)
 +        if (pcrd->params.eGeom == epullgDIR || pcrd->params.eGeom == epullgDIRPBC)
          {
              /* Select the component along vec */
              a = 0;
              for (m = 0; m < DIM; m++)
              {
 -                a += spatialData.vec[m]*spatialData.dr01[m];
 +                a += spatialData.vec[m] * spatialData.dr01[m];
              }
              for (m = 0; m < DIM; m++)
              {
 -                r_ij[c][m] = a*spatialData.vec[m];
 +                r_ij[c][m] = a * spatialData.vec[m];
              }
          }
          else
  
          if (dnorm2(r_ij[c]) == 0)
          {
 -            gmx_fatal(FARGS, "Distance for pull coordinate %zu is zero with constraint pulling, which is not allowed.", c + 1);
 +            gmx_fatal(FARGS,
 +                      "Distance for pull coordinate %zu is zero with constraint pulling, which is "
 +                      "not allowed.",
 +                      c + 1);
          }
      }
  
          /* loop over all constraints */
          for (size_t c = 0; c < pull->coord.size(); c++)
          {
 -            pull_coord_work_t *pcrd;
 +            pull_coord_work_tpcrd;
              pull_group_work_t *pgrp0, *pgrp1;
              dvec               dr0, dr1;
  
 -            pcrd  = &pull->coord[c];
 +            pcrd = &pull->coord[c];
  
              if (pcrd->params.eType != epullCONSTRAINT)
              {
              pgrp1 = &pull->group[pcrd->params.group[1]];
  
              /* Get the current difference vector */
 -            low_get_pull_coord_dr(pull, pcrd, pbc,
 -                                  rnew[pcrd->params.group[1]],
 -                                  rnew[pcrd->params.group[0]],
 -                                  -1, unc_ij);
 +            low_get_pull_coord_dr(pull, pcrd, pbc, rnew[pcrd->params.group[1]],
 +                                  rnew[pcrd->params.group[0]], -1, unc_ij);
  
              if (debug)
              {
                  fprintf(debug, "Pull coord %zu, iteration %d\n", c, niter);
              }
  
 -            rm = 1.0/(pgrp0->invtm + pgrp1->invtm);
 +            rm = 1.0 / (pgrp0->invtm + pgrp1->invtm);
  
              switch (pcrd->params.eGeom)
              {
                  case epullgDIST:
                      if (pcrd->value_ref <= 0)
                      {
 -                        gmx_fatal(FARGS, "The pull constraint reference distance for group %zu is <= 0 (%f)", c, pcrd->value_ref);
 +                        gmx_fatal(
 +                                FARGS,
 +                                "The pull constraint reference distance for group %zu is <= 0 (%f)",
 +                                c, pcrd->value_ref);
                      }
  
                      {
                          double q, c_a, c_b, c_c;
  
                          c_a = diprod(r_ij[c], r_ij[c]);
 -                        c_b = diprod(unc_ij, r_ij[c])*2;
 +                        c_b = diprod(unc_ij, r_ij[c]) * 2;
                          c_c = diprod(unc_ij, unc_ij) - gmx::square(pcrd->value_ref);
  
                          if (c_b < 0)
                          {
 -                            q      = -0.5*(c_b - std::sqrt(c_b*c_b - 4*c_a*c_c));
 -                            lambda = -q/c_a;
 +                            q      = -0.5 * (c_b - std::sqrt(c_b * c_b - 4 * c_a * c_c));
 +                            lambda = -q / c_a;
                          }
                          else
                          {
 -                            q      = -0.5*(c_b + std::sqrt(c_b*c_b - 4*c_a*c_c));
 -                            lambda = -c_c/q;
 +                            q      = -0.5 * (c_b + std::sqrt(c_b * c_b - 4 * c_a * c_c));
 +                            lambda = -c_c / q;
                          }
  
                          if (debug)
                          {
 -                            fprintf(debug,
 -                                    "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n",
 -                                    c_a, c_b, c_c, lambda);
 +                            fprintf(debug, "Pull ax^2+bx+c=0: a=%e b=%e c=%e lambda=%e\n", c_a, c_b,
 +                                    c_c, lambda);
                          }
                      }
  
                      /* The position corrections dr due to the constraints */
 -                    dsvmul(-lambda*rm*pgrp1->invtm, r_ij[c], dr1);
 -                    dsvmul( lambda*rm*pgrp0->invtm, r_ij[c], dr0);
 -                    dr_tot[c] += -lambda*dnorm(r_ij[c]);
 +                    dsvmul(-lambda * rm * pgrp1->invtm, r_ij[c], dr1);
 +                    dsvmul(lambda * rm * pgrp0->invtm, r_ij[c], dr0);
 +                    dr_tot[c] += -lambda * dnorm(r_ij[c]);
                      break;
                  case epullgDIR:
                  case epullgDIRPBC:
                      for (m = 0; m < DIM; m++)
                      {
                          vec[m] = pcrd->spatialData.vec[m];
 -                        a     += unc_ij[m]*vec[m];
 +                        a += unc_ij[m] * vec[m];
                      }
                      /* Select only the component along the vector */
                      dsvmul(a, vec, unc_ij);
                      }
  
                      /* The position corrections dr due to the constraints */
 -                    dsvmul(-lambda*rm*pgrp1->invtm, vec, dr1);
 -                    dsvmul( lambda*rm*pgrp0->invtm, vec, dr0);
 +                    dsvmul(-lambda * rm * pgrp1->invtm, vec, dr1);
 +                    dsvmul(lambda * rm * pgrp0->invtm, vec, dr0);
                      dr_tot[c] += -lambda;
                      break;
 -                default:
 -                    gmx_incons("Invalid enumeration value for eGeom");
 +                default: gmx_incons("Invalid enumeration value for eGeom");
              }
  
              /* DEBUG */
                  g1 = pcrd->params.group[1];
                  low_get_pull_coord_dr(pull, pcrd, pbc, rnew[g1], rnew[g0], -1, tmp);
                  low_get_pull_coord_dr(pull, pcrd, pbc, dr1, dr0, -1, tmp3);
 -                fprintf(debug,
 -                        "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
 -                        rnew[g0][0], rnew[g0][1], rnew[g0][2],
 -                        rnew[g1][0], rnew[g1][1], rnew[g1][2], dnorm(tmp));
 -                fprintf(debug,
 -                        "Pull ref %8s %8s %8s   %8s %8s %8s d: %8.5f\n",
 -                        "", "", "", "", "", "", pcrd->value_ref);
 -                fprintf(debug,
 -                        "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n",
 -                        dr0[0], dr0[1], dr0[2],
 -                        dr1[0], dr1[1], dr1[2],
 -                        dnorm(tmp3));
 +                fprintf(debug, "Pull cur %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n", rnew[g0][0],
 +                        rnew[g0][1], rnew[g0][2], rnew[g1][0], rnew[g1][1], rnew[g1][2], dnorm(tmp));
 +                fprintf(debug, "Pull ref %8s %8s %8s   %8s %8s %8s d: %8.5f\n", "", "", "", "", "",
 +                        "", pcrd->value_ref);
 +                fprintf(debug, "Pull cor %8.5f %8.5f %8.5f j:%8.5f %8.5f %8.5f d: %8.5f\n", dr0[0],
 +                        dr0[1], dr0[2], dr1[0], dr1[1], dr1[2], dnorm(tmp3));
              } /* END DEBUG */
  
              /* Update the COMs with dr */
          }
  
          /* Check if all constraints are fullfilled now */
 -        for (pull_coord_work_t &coord : pull->coord)
 +        for (pull_coord_work_tcoord : pull->coord)
          {
              if (coord.params.eType != epullCONSTRAINT)
              {
                  continue;
              }
  
 -            low_get_pull_coord_dr(pull, &coord, pbc,
 -                                  rnew[coord.params.group[1]],
 -                                  rnew[coord.params.group[0]],
 -                                  -1, unc_ij);
 +            low_get_pull_coord_dr(pull, &coord, pbc, rnew[coord.params.group[1]],
 +                                  rnew[coord.params.group[0]], -1, unc_ij);
  
              switch (coord.params.eGeom)
              {
                  case epullgDIST:
 -                    bConverged =
 -                        fabs(dnorm(unc_ij) - coord.value_ref) < pull->params.constr_tol;
 +                    bConverged = fabs(dnorm(unc_ij) - coord.value_ref) < pull->params.constr_tol;
                      break;
                  case epullgDIR:
                  case epullgDIRPBC:
                      }
                      inpr = diprod(unc_ij, vec);
                      dsvmul(inpr, vec, unc_ij);
 -                    bConverged =
 -                        fabs(diprod(unc_ij, vec) - coord.value_ref) < pull->params.constr_tol;
 +                    bConverged = fabs(diprod(unc_ij, vec) - coord.value_ref) < pull->params.constr_tol;
                      break;
              }
  
              {
                  if (debug)
                  {
 -                    fprintf(debug, "Pull constraint not converged: "
 +                    fprintf(debug,
 +                            "Pull constraint not converged: "
                              "groups %d %d,"
                              "d_ref = %f, current d = %f\n",
 -                            coord.params.group[0], coord.params.group[1],
 -                            coord.value_ref, dnorm(unc_ij));
 +                            coord.params.group[0], coord.params.group[1], coord.value_ref, dnorm(unc_ij));
                  }
  
                  bConverged_all = FALSE;
  
      if (v)
      {
 -        invdt = 1/dt;
 +        invdt = 1 / dt;
      }
  
      /* update atoms in the groups */
      for (size_t g = 0; g < pull->group.size(); g++)
      {
 -        const pull_group_work_t *pgrp;
 +        const pull_group_work_tpgrp;
          dvec                     dr;
  
          pgrp = &pull->group[g];
          /* update the atom positions */
          auto localAtomIndices = pgrp->atomSet.localIndex();
          copy_dvec(dr, tmp);
 -        for (gmx::index j = 0; j < localAtomIndices.size(); j++)
 +        for (gmx::index j = 0; j < localAtomIndices.ssize(); j++)
          {
              ii = localAtomIndices[j];
              if (!pgrp->localWeights.empty())
              {
 -                dsvmul(pgrp->wscale*pgrp->localWeights[j], dr, tmp);
 +                dsvmul(pgrp->wscale * pgrp->localWeights[j], dr, tmp);
              }
              for (m = 0; m < DIM; m++)
              {
              {
                  for (m = 0; m < DIM; m++)
                  {
 -                    v[ii][m] += invdt*tmp[m];
 +                    v[ii][m] += invdt * tmp[m];
                  }
              }
          }
      /* calculate the constraint forces, used for output and virial only */
      for (size_t c = 0; c < pull->coord.size(); c++)
      {
 -        pull_coord_work_t *pcrd;
 +        pull_coord_work_tpcrd;
  
          pcrd = &pull->coord[c];
  
          }
  
          /* Accumulate the forces, in case we have multiple constraint steps */
 -        double force = dr_tot[c]/((pull->group[pcrd->params.group[0]].invtm + pull->group[pcrd->params.group[1]].invtm)*dt*dt);
 +        double force =
 +                dr_tot[c]
 +                / ((pull->group[pcrd->params.group[0]].invtm + pull->group[pcrd->params.group[1]].invtm)
 +                   * dt * dt);
          pcrd->scalarForce += force;
  
          if (vir != nullptr && pcrd->params.eGeom != epullgDIRPBC && bMaster)
  
              /* Add the pull contribution to the virial */
              /* We have already checked above that r_ij[c] != 0 */
 -            f_invr = pcrd->scalarForce/dnorm(r_ij[c]);
 +            f_invr = pcrd->scalarForce / dnorm(r_ij[c]);
  
              for (j = 0; j < DIM; j++)
              {
                  for (m = 0; m < DIM; m++)
                  {
 -                    vir[j][m] -= 0.5*f_invr*r_ij[c][j]*r_ij[c][m];
 +                    vir[j][m] -= 0.5 * f_invr * r_ij[c][j] * r_ij[c][m];
                  }
              }
          }
@@@ -1119,13 -1150,15 +1123,13 @@@ static void add_virial_coord_dr(tensor 
      {
          for (int m = 0; m < DIM; m++)
          {
 -            vir[j][m] -= 0.5*f[j]*dr[m];
 +            vir[j][m] -= 0.5 * f[j] * dr[m];
          }
      }
  }
  
  /* Adds the pull contribution to the virial */
 -static void add_virial_coord(tensor                       vir,
 -                             const pull_coord_work_t     &pcrd,
 -                             const PullCoordVectorForces &forces)
 +static void add_virial_coord(tensor vir, const pull_coord_work_t& pcrd, const PullCoordVectorForces& forces)
  {
      if (vir != nullptr && pcrd.params.eGeom != epullgDIRPBC)
      {
      }
  }
  
 -static void calc_pull_coord_scalar_force_and_potential(pull_coord_work_t *pcrd,
 -                                                       double dev, real lambda,
 -                                                       real *V, real *dVdl)
 +static void calc_pull_coord_scalar_force_and_potential(pull_coord_work_t* pcrd,
 +                                                       double             dev,
 +                                                       real               lambda,
 +                                                       real*              V,
 +                                                       real*              dVdl)
  {
 -    real   k, dkdl;
 +    real k, dkdl;
  
 -    k    = (1.0 - lambda)*pcrd->params.k + lambda*pcrd->params.kB;
 +    k    = (1.0 - lambda) * pcrd->params.k + lambda * pcrd->params.kB;
      dkdl = pcrd->params.kB - pcrd->params.k;
  
      switch (pcrd->params.eType)
               * potential is that a flat-bottom is zero above or below
                 the reference value.
               */
 -            if ((pcrd->params.eType == epullFLATBOTTOM && dev < 0) ||
 -                (pcrd->params.eType == epullFLATBOTTOMHIGH && dev > 0))
 +            if ((pcrd->params.eType == epullFLATBOTTOM && dev < 0)
 +                || (pcrd->params.eType == epullFLATBOTTOMHIGH && dev > 0))
              {
                  dev = 0;
              }
  
 -            pcrd->scalarForce    = -k*dev;
 -            *V                  += 0.5*   k*gmx::square(dev);
 -            *dVdl               += 0.5*dkdl*gmx::square(dev);
 +            pcrd->scalarForce = -k * dev;
 +            *V += 0.5 * k * gmx::square(dev);
 +            *dVdl += 0.5 * dkdl * gmx::square(dev);
              break;
          case epullCONST_F:
 -            pcrd->scalarForce    = -k;
 -            *V                  +=    k*pcrd->spatialData.value;
 -            *dVdl               += dkdl*pcrd->spatialData.value;
 +            pcrd->scalarForce = -k;
 +            *V += k * pcrd->spatialData.value;
 +            *dVdl += dkdl * pcrd->spatialData.value;
              break;
          case epullEXTERNAL:
 -            gmx_incons("the scalar pull force should not be calculated internally for pull type external");
 -        default:
 -            gmx_incons("Unsupported pull type in do_pull_pot");
 +            gmx_incons(
 +                    "the scalar pull force should not be calculated internally for pull type "
 +                    "external");
 +        default: gmx_incons("Unsupported pull type in do_pull_pot");
      }
  }
  
 -static PullCoordVectorForces
 -calculateVectorForces(const pull_coord_work_t &pcrd)
 +static PullCoordVectorForces calculateVectorForces(const pull_coord_work_t& pcrd)
  {
 -    const t_pull_coord         &params      = pcrd.params;
 -    const PullCoordSpatialData &spatialData = pcrd.spatialData;
 +    const t_pull_coord&         params      = pcrd.params;
 +    const PullCoordSpatialDataspatialData = pcrd.spatialData;
  
      /* The geometry of the coordinate determines how the scalar force relates to the force on each group */
 -    PullCoordVectorForces    forces;
 +    PullCoordVectorForces forces;
  
      if (params.eGeom == epullgDIST)
      {
 -        double invdr01 = spatialData.value > 0 ? 1./spatialData.value : 0.;
 +        double invdr01 = spatialData.value > 0 ? 1. / spatialData.value : 0.;
          for (int m = 0; m < DIM; m++)
          {
 -            forces.force01[m] = pcrd.scalarForce*spatialData.dr01[m]*invdr01;
 +            forces.force01[m] = pcrd.scalarForce * spatialData.dr01[m] * invdr01;
          }
      }
      else if (params.eGeom == epullgANGLE)
               * f23 = -dV/dtheta*(a*dr01/|dr01| - b*dr23*|dr23|)/|dr23|,
               */
              double a       = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
 -            double b       = a*cos_theta;
 -            double invdr01 = 1./dnorm(spatialData.dr01);
 -            double invdr23 = 1./dnorm(spatialData.dr23);
 +            double b       = a * cos_theta;
 +            double invdr01 = 1. / dnorm(spatialData.dr01);
 +            double invdr23 = 1. / dnorm(spatialData.dr23);
              dvec   normalized_dr01, normalized_dr23;
              dsvmul(invdr01, spatialData.dr01, normalized_dr01);
              dsvmul(invdr23, spatialData.dr23, normalized_dr23);
              for (int m = 0; m < DIM; m++)
              {
                  /* Here, f_scal is -dV/dtheta */
 -                forces.force01[m] = pcrd.scalarForce*invdr01*(a*normalized_dr23[m] - b*normalized_dr01[m]);
 -                forces.force23[m] = pcrd.scalarForce*invdr23*(a*normalized_dr01[m] - b*normalized_dr23[m]);
 +                forces.force01[m] =
 +                        pcrd.scalarForce * invdr01 * (a * normalized_dr23[m] - b * normalized_dr01[m]);
 +                forces.force23[m] =
 +                        pcrd.scalarForce * invdr23 * (a * normalized_dr01[m] - b * normalized_dr23[m]);
              }
          }
          else
              double invdr01;
              dvec   normalized_dr01;
  
 -            invdr01 = 1./dnorm(spatialData.dr01);
 +            invdr01 = 1. / dnorm(spatialData.dr01);
              dsvmul(invdr01, spatialData.dr01, normalized_dr01);
 -            a       = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
 -            b       = a*cos_theta;
 +            a = -gmx::invsqrt(1 - cos_theta2); /* comes from d/dx acos(x) */
 +            b = a * cos_theta;
  
              for (int m = 0; m < DIM; m++)
              {
 -                forces.force01[m] = pcrd.scalarForce*invdr01*(a*spatialData.vec[m] - b*normalized_dr01[m]);
 +                forces.force01[m] =
 +                        pcrd.scalarForce * invdr01 * (a * spatialData.vec[m] - b * normalized_dr01[m]);
              }
          }
          else
          n2 = diprod(spatialData.planevec_n, spatialData.planevec_n);
          dsvmul(-1, spatialData.dr23, dr32);
          sqrdist_32 = diprod(dr32, dr32);
 -        tol        = sqrdist_32*GMX_REAL_EPS; /* Avoid tiny angles */
 +        tol        = sqrdist_32 * GMX_REAL_EPS; /* Avoid tiny angles */
          if ((m2 > tol) && (n2 > tol))
          {
              double a_01, a_23_01, a_23_45, a_45;
              double inv_dist_32, inv_sqrdist_32, dist_32;
              dvec   u, v;
              inv_dist_32    = gmx::invsqrt(sqrdist_32);
 -            inv_sqrdist_32 = inv_dist_32*inv_dist_32;
 -            dist_32        = sqrdist_32*inv_dist_32;
 +            inv_sqrdist_32 = inv_dist_32 * inv_dist_32;
 +            dist_32        = sqrdist_32 * inv_dist_32;
  
              /* Forces on groups 0, 1 */
 -            a_01 = pcrd.scalarForce*dist_32/m2;                    /* scalarForce is -dV/dphi */
 -            dsvmul(-a_01, spatialData.planevec_m, forces.force01); /* added sign to get force on group 1, not 0 */
 +            a_01 = pcrd.scalarForce * dist_32 / m2; /* scalarForce is -dV/dphi */
 +            dsvmul(-a_01, spatialData.planevec_m,
 +                   forces.force01); /* added sign to get force on group 1, not 0 */
  
              /* Forces on groups 4, 5 */
 -            a_45 = -pcrd.scalarForce*dist_32/n2;
 -            dsvmul(a_45, spatialData.planevec_n, forces.force45);  /* force on group 5 */
 +            a_45 = -pcrd.scalarForce * dist_32 / n2;
 +            dsvmul(a_45, spatialData.planevec_n, forces.force45); /* force on group 5 */
  
              /* Force on groups 2, 3 (defining the axis) */
 -            a_23_01 = -diprod(spatialData.dr01, dr32)*inv_sqrdist_32;
 -            a_23_45 = -diprod(spatialData.dr45, dr32)*inv_sqrdist_32;
 +            a_23_01 = -diprod(spatialData.dr01, dr32) * inv_sqrdist_32;
 +            a_23_45 = -diprod(spatialData.dr45, dr32) * inv_sqrdist_32;
              dsvmul(-a_23_01, forces.force01, u); /* added sign to get force from group 0, not 1 */
              dsvmul(a_23_45, forces.force45, v);
 -            dvec_sub(u, v, forces.force23);      /* force on group 3 */
 +            dvec_sub(u, v, forces.force23); /* force on group 3 */
          }
          else
          {
      {
          for (int m = 0; m < DIM; m++)
          {
 -            forces.force01[m] = pcrd.scalarForce*spatialData.vec[m];
 +            forces.force01[m] = pcrd.scalarForce * spatialData.vec[m];
          }
      }
  
@@@ -1346,39 -1373,32 +1350,39 @@@ static gmx::Mutex registrationMutex
  
  using Lock = gmx::lock_guard<gmx::Mutex>;
  
 -void register_external_pull_potential(struct pull_t *pull,
 -                                      int            coord_index,
 -                                      const char    *provider)
 +void register_external_pull_potential(struct pull_t* pull, int coord_index, const char* provider)
  {
      GMX_RELEASE_ASSERT(pull != nullptr, "register_external_pull_potential called before init_pull");
 -    GMX_RELEASE_ASSERT(provider != nullptr, "register_external_pull_potential called with NULL as provider name");
 +    GMX_RELEASE_ASSERT(provider != nullptr,
 +                       "register_external_pull_potential called with NULL as provider name");
  
 -    if (coord_index < 0 || coord_index >= static_cast<int>(pull->coord.size()))
 +    if (coord_index < 0 || coord_index >= gmx::ssize(pull->coord))
      {
 -        gmx_fatal(FARGS, "Module '%s' attempted to register an external potential for pull coordinate %d which is out of the pull coordinate range %d - %zu\n",
 +        gmx_fatal(FARGS,
 +                  "Module '%s' attempted to register an external potential for pull coordinate %d "
 +                  "which is out of the pull coordinate range %d - %zu\n",
                    provider, coord_index + 1, 1, pull->coord.size());
      }
  
 -    pull_coord_work_t *pcrd = &pull->coord[coord_index];
 +    pull_coord_work_tpcrd = &pull->coord[coord_index];
  
      if (pcrd->params.eType != epullEXTERNAL)
      {
 -        gmx_fatal(FARGS, "Module '%s' attempted to register an external potential for pull coordinate %d which of type '%s', whereas external potentials are only supported with type '%s'",
 -                  provider, coord_index + 1, epull_names[pcrd->params.eType], epull_names[epullEXTERNAL]);
 +        gmx_fatal(
 +                FARGS,
 +                "Module '%s' attempted to register an external potential for pull coordinate %d "
 +                "which of type '%s', whereas external potentials are only supported with type '%s'",
 +                provider, coord_index + 1, epull_names[pcrd->params.eType], epull_names[epullEXTERNAL]);
      }
  
 -    GMX_RELEASE_ASSERT(pcrd->params.externalPotentialProvider != nullptr, "The external potential provider string for a pull coordinate is NULL");
 +    GMX_RELEASE_ASSERT(pcrd->params.externalPotentialProvider != nullptr,
 +                       "The external potential provider string for a pull coordinate is NULL");
  
      if (gmx_strcasecmp(provider, pcrd->params.externalPotentialProvider) != 0)
      {
 -        gmx_fatal(FARGS, "Module '%s' attempted to register an external potential for pull coordinate %d which expects the external potential to be provided by a module named '%s'",
 +        gmx_fatal(FARGS,
 +                  "Module '%s' attempted to register an external potential for pull coordinate %d "
 +                  "which expects the external potential to be provided by a module named '%s'",
                    provider, coord_index + 1, pcrd->params.externalPotentialProvider);
      }
  
  
      if (pcrd->bExternalPotentialProviderHasBeenRegistered)
      {
 -        gmx_fatal(FARGS, "Module '%s' attempted to register an external potential for pull coordinate %d more than once",
 +        gmx_fatal(FARGS,
 +                  "Module '%s' attempted to register an external potential for pull coordinate %d "
 +                  "more than once",
                    provider, coord_index + 1);
      }
  
      pcrd->bExternalPotentialProviderHasBeenRegistered = true;
      pull->numUnregisteredExternalPotentials--;
  
 -    GMX_RELEASE_ASSERT(pull->numUnregisteredExternalPotentials >= 0, "Negative unregistered potentials, the pull code is inconsistent");
 +    GMX_RELEASE_ASSERT(pull->numUnregisteredExternalPotentials >= 0,
 +                       "Negative unregistered potentials, the pull code is inconsistent");
  }
  
  
 -static void check_external_potential_registration(const struct pull_t *pull)
 +static void check_external_potential_registration(const struct pull_tpull)
  {
      if (pull->numUnregisteredExternalPotentials > 0)
      {
          size_t c;
          for (c = 0; c < pull->coord.size(); c++)
          {
 -            if (pull->coord[c].params.eType == epullEXTERNAL &&
 -                !pull->coord[c].bExternalPotentialProviderHasBeenRegistered)
 +            if (pull->coord[c].params.eType == epullEXTERNAL
 +                && !pull->coord[c].bExternalPotentialProviderHasBeenRegistered)
              {
                  break;
              }
          }
  
 -        GMX_RELEASE_ASSERT(c < pull->coord.size(), "Internal inconsistency in the pull potential provider counting");
 +        GMX_RELEASE_ASSERT(c < pull->coord.size(),
 +                           "Internal inconsistency in the pull potential provider counting");
  
 -        gmx_fatal(FARGS, "No external provider for external pull potentials have been provided for %d pull coordinates. The first coordinate without provider is number %zu, which expects a module named '%s' to provide the external potential.",
 -                  pull->numUnregisteredExternalPotentials,
 -                  c + 1, pull->coord[c].params.externalPotentialProvider);
 +        gmx_fatal(FARGS,
 +                  "No external provider for external pull potentials have been provided for %d "
 +                  "pull coordinates. The first coordinate without provider is number %zu, which "
 +                  "expects a module named '%s' to provide the external potential.",
 +                  pull->numUnregisteredExternalPotentials, c + 1,
 +                  pull->coord[c].params.externalPotentialProvider);
      }
  }
  
   * potential energy is added either to the pull term or to a term
   * specific to the external module.
   */
 -void apply_external_pull_coord_force(struct pull_t        *pull,
 +void apply_external_pull_coord_force(struct pull_t*        pull,
                                       int                   coord_index,
                                       double                coord_force,
 -                                     const t_mdatoms      *mdatoms,
 -                                     gmx::ForceWithVirial *forceWithVirial)
 +                                     const t_mdatoms*      mdatoms,
 +                                     gmx::ForceWithVirialforceWithVirial)
  {
 -    pull_coord_work_t *pcrd;
 +    pull_coord_work_tpcrd;
  
 -    GMX_ASSERT(coord_index >= 0 && coord_index < static_cast<int>(pull->coord.size()), "apply_external_pull_coord_force called with coord_index out of range");
 +    GMX_ASSERT(coord_index >= 0 && coord_index < gmx::ssize(pull->coord),
 +               "apply_external_pull_coord_force called with coord_index out of range");
  
      if (pull->comm.bParticipate)
      {
          pcrd = &pull->coord[coord_index];
  
 -        GMX_RELEASE_ASSERT(pcrd->params.eType == epullEXTERNAL, "The pull force can only be set externally on pull coordinates of external type");
 +        GMX_RELEASE_ASSERT(
 +                pcrd->params.eType == epullEXTERNAL,
 +                "The pull force can only be set externally on pull coordinates of external type");
  
 -        GMX_ASSERT(pcrd->bExternalPotentialProviderHasBeenRegistered, "apply_external_pull_coord_force called for an unregistered pull coordinate");
 +        GMX_ASSERT(pcrd->bExternalPotentialProviderHasBeenRegistered,
 +                   "apply_external_pull_coord_force called for an unregistered pull coordinate");
  
          /* Set the force */
          pcrd->scalarForce = coord_force;
  /* Calculate the pull potential and scalar force for a pull coordinate.
   * Returns the vector forces for the pull coordinate.
   */
 -static PullCoordVectorForces
 -do_pull_pot_coord(struct pull_t *pull, int coord_ind, t_pbc *pbc,
 -                  double t, real lambda,
 -                  real *V, tensor vir, real *dVdl)
 +static PullCoordVectorForces do_pull_pot_coord(struct pull_t* pull,
 +                                               int            coord_ind,
 +                                               t_pbc*         pbc,
 +                                               double         t,
 +                                               real           lambda,
 +                                               real*          V,
 +                                               tensor         vir,
 +                                               real*          dVdl)
  {
 -    pull_coord_work_t &pcrd = pull->coord[coord_ind];
 +    pull_coord_work_tpcrd = pull->coord[coord_ind];
  
      assert(pcrd.params.eType != epullCONSTRAINT);
  
      return pullCoordForces;
  }
  
 -real pull_potential(struct pull_t *pull, const t_mdatoms *md, t_pbc *pbc,
 -                    const t_commrec *cr, double t, real lambda,
 -                    const rvec *x, gmx::ForceWithVirial *force, real *dvdlambda)
 +real pull_potential(struct pull_t*        pull,
 +                    const t_mdatoms*      md,
 +                    t_pbc*                pbc,
 +                    const t_commrec*      cr,
 +                    double                t,
 +                    real                  lambda,
 +                    const rvec*           x,
 +                    gmx::ForceWithVirial* force,
 +                    real*                 dvdlambda)
  {
      real V = 0;
  
  
          pull_calc_coms(cr, pull, md, pbc, t, x, nullptr);
  
 -        rvec       *f             = as_rvec_array(force->force_.data());
 -        matrix      virial        = { { 0 } };
 -        const bool  computeVirial = (force->computeVirial_ && MASTER(cr));
 +        rvec*      f             = as_rvec_array(force->force_.data());
 +        matrix     virial        = { { 0 } };
 +        const bool computeVirial = (force->computeVirial_ && MASTER(cr));
          for (size_t c = 0; c < pull->coord.size(); c++)
          {
 -            pull_coord_work_t *pcrd;
 +            pull_coord_work_tpcrd;
              pcrd = &pull->coord[c];
  
 -            /* For external potential the force is assumed to be given by an external module by a call to
 -               apply_pull_coord_external_force */
 +            /* For external potential the force is assumed to be given by an external module by a
 +               call to apply_pull_coord_external_force */
              if (pcrd->params.eType == epullCONSTRAINT || pcrd->params.eType == epullEXTERNAL)
              {
                  continue;
              }
  
 -            PullCoordVectorForces pullCoordForces =
 -                do_pull_pot_coord(pull, c, pbc, t, lambda,
 -                                  &V,
 -                                  computeVirial ? virial : nullptr,
 -                                  &dVdl);
 +            PullCoordVectorForces pullCoordForces = do_pull_pot_coord(
 +                    pull, c, pbc, t, lambda, &V, computeVirial ? virial : nullptr, &dVdl);
  
              /* Distribute the force over the atoms in the pulled groups */
              apply_forces_coord(pull, c, pullCoordForces, md, f);
          }
      }
  
 -    GMX_ASSERT(pull->numExternalPotentialsStillToBeAppliedThisStep == 0, "Too few or too many external pull potentials have been applied the previous step");
 +    GMX_ASSERT(pull->numExternalPotentialsStillToBeAppliedThisStep == 0,
 +               "Too few or too many external pull potentials have been applied the previous step");
      /* All external pull potentials still need to be applied */
 -    pull->numExternalPotentialsStillToBeAppliedThisStep =
 -        pull->numCoordinatesWithExternalPotential;
 +    pull->numExternalPotentialsStillToBeAppliedThisStep = pull->numCoordinatesWithExternalPotential;
  
      return (MASTER(cr) ? V : 0.0);
  }
  
 -void pull_constraint(struct pull_t *pull, const t_mdatoms *md, t_pbc *pbc,
 -                     const t_commrec *cr, double dt, double t,
 -                     rvec *x, rvec *xp, rvec *v, tensor vir)
 +void pull_constraint(struct pull_t*   pull,
 +                     const t_mdatoms* md,
 +                     t_pbc*           pbc,
 +                     const t_commrec* cr,
 +                     double           dt,
 +                     double           t,
 +                     rvec*            x,
 +                     rvec*            xp,
 +                     rvec*            v,
 +                     tensor           vir)
  {
      assert(pull != nullptr);
  
      }
  }
  
 -void dd_make_local_pull_groups(const t_commrec *cr, struct pull_t *pull)
 +void dd_make_local_pull_groups(const t_commrec* cr, struct pull_t* pull)
  {
 -    gmx_domdec_t   *dd;
 -    pull_comm_t    *comm;
 -    gmx_bool        bMustParticipate;
 +    gmx_domdec_tdd;
 +    pull_comm_t*  comm;
 +    gmx_bool      bMustParticipate;
  
      dd = cr->dd;
  
       */
      bMustParticipate = (comm->bParticipateAll || comm->isMasterRank);
  
 -    for (pull_group_work_t &group : pull->group)
 +    for (pull_group_work_tgroup : pull->group)
      {
          if (!group.globalWeights.empty())
          {
              }
          }
  
 -        GMX_ASSERT(bMustParticipate || dd != nullptr, "Either all ranks (including this rank) participate, or we use DD and need to have access to dd here");
 +        GMX_ASSERT(bMustParticipate || dd != nullptr,
 +                   "Either all ranks (including this rank) participate, or we use DD and need to "
 +                   "have access to dd here");
  
          /* We should participate if we have pull or pbc atoms */
 -        if (!bMustParticipate &&
 -            (group.atomSet.numAtomsLocal() > 0 || (group.epgrppbc == epgrppbcREFAT &&
 -                                                   group.pbcAtomSet->numAtomsLocal() > 0)))
 +        if (!bMustParticipate
 +            && (group.atomSet.numAtomsLocal() > 0
 +                || (group.epgrppbc == epgrppbcREFAT && group.pbcAtomSet->numAtomsLocal() > 0)))
          {
              bMustParticipate = TRUE;
          }
           * if they needed to participate up to 20 decompositions ago.
           * This avoids frequent rebuilds due to atoms jumping back and forth.
           */
 -        const int64_t     history_count = 20;
 -        gmx_bool          bWillParticipate;
 -        int               count[2];
 +        const int64_t history_count = 20;
 +        gmx_bool      bWillParticipate;
 +        int           count[2];
  
          /* Increase the decomposition counter for the current call */
          comm->setup_count++;
          }
  
          bWillParticipate =
 -            bMustParticipate ||
 -            (comm->bParticipate &&
 -             comm->must_count >= comm->setup_count - history_count);
 +                bMustParticipate
 +                || (comm->bParticipate && comm->must_count >= comm->setup_count - history_count);
  
          if (debug && dd != nullptr)
          {
 -            fprintf(debug, "Our DD rank (%3d) pull #atoms>0 or master: %s, will be part %s\n",
 -                    dd->rank, gmx::boolToString(bMustParticipate), gmx::boolToString(bWillParticipate));
 +            fprintf(debug, "Our DD rank (%3d) pull #atoms>0 or master: %s, will be part %s\n", dd->rank,
 +                    gmx::boolToString(bMustParticipate), gmx::boolToString(bWillParticipate));
          }
  
          if (bWillParticipate)
          /* If we are missing ranks or if we have 20% more ranks than needed
           * we make a new sub-communicator.
           */
 -        if (count[1] > 0 || 6*count[0] < 5*comm->nparticipate)
 +        if (count[1] > 0 || 6 * count[0] < 5 * comm->nparticipate)
          {
              if (debug)
              {
 -                fprintf(debug, "Creating new pull subcommunicator of size %d\n",
 -                        count[0]);
 +                fprintf(debug, "Creating new pull subcommunicator of size %d\n", count[0]);
              }
  
  #if GMX_MPI
               * to avoid this splitting as much as possible.
               */
              assert(dd != nullptr);
 -            MPI_Comm_split(dd->mpi_comm_all, bWillParticipate ? 0 : 1, dd->rank,
 -                           &comm->mpi_comm_com);
 +            MPI_Comm_split(dd->mpi_comm_all, bWillParticipate ? 0 : 1, dd->rank, &comm->mpi_comm_com);
  #endif
  
              comm->bParticipate = bWillParticipate;
              /* When we use the previous COM for PBC, we need to broadcast
               * the previous COM to ranks that have joined the communicator.
               */
 -            for (pull_group_work_t &group : pull->group)
 +            for (pull_group_work_tgroup : pull->group)
              {
                  if (group.epgrppbc == epgrppbcPREVSTEPCOM)
                  {
                      GMX_ASSERT(comm->bParticipate || !MASTER(cr),
 -                               "The master rank has to participate, as it should pass an up to date prev. COM "
 +                               "The master rank has to participate, as it should pass an up to "
 +                               "date prev. COM "
                                 "to bcast here as well as to e.g. checkpointing");
  
                      gmx_bcast(sizeof(dvec), group.x_prev_step, cr);
      pull->bSetPBCatoms = TRUE;
  }
  
 -static void init_pull_group_index(FILE *fplog, const t_commrec *cr,
 -                                  int g, pull_group_work_t *pg,
 -                                  gmx_bool bConstraint, const ivec pulldim_con,
 -                                  const gmx_mtop_t *mtop,
 -                                  const t_inputrec *ir, real lambda)
 +static void init_pull_group_index(FILE*              fplog,
 +                                  const t_commrec*   cr,
 +                                  int                g,
 +                                  pull_group_work_t* pg,
 +                                  gmx_bool           bConstraint,
 +                                  const ivec         pulldim_con,
 +                                  const gmx_mtop_t*  mtop,
 +                                  const t_inputrec*  ir,
 +                                  real               lambda)
  {
      /* With EM and BD there are no masses in the integrator.
       * But we still want to have the correct mass-weighted COMs.
       * So we store the real masses in the weights.
       */
 -    const bool setWeights = (pg->params.nweight > 0 ||
 -                             EI_ENERGY_MINIMIZATION(ir->eI) ||
 -                             ir->eI == eiBD);
 +    const bool setWeights = (pg->params.nweight > 0 || EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD);
  
      /* In parallel, store we need to extract localWeights from weights at DD time */
 -    std::vector<real>  &weights = ((cr && PAR(cr)) ? pg->globalWeights : pg->localWeights);
 +    std::vector<real>weights = ((cr && PAR(cr)) ? pg->globalWeights : pg->localWeights);
  
 -    const gmx_groups_t *groups  = &mtop->groups;
 +    const SimulationGroups& groups = mtop->groups;
  
      /* Count frozen dimensions and (weighted) mass */
      int    nfrozen = 0;
          {
              for (int d = 0; d < DIM; d++)
              {
 -                if (pulldim_con[d] == 1 &&
 -                    ir->opts.nFreeze[getGroupType(groups, egcFREEZE, ii)][d])
 +                if (pulldim_con[d] == 1
 +                    && ir->opts.nFreeze[getGroupType(groups, SimulationAtomGroupType::Freeze, ii)][d])
                  {
                      nfrozen++;
                  }
              }
          }
 -        const t_atom &atom = mtopGetAtomParameters(mtop, ii, &molb);
 +        const t_atomatom = mtopGetAtomParameters(mtop, ii, &molb);
          real          m;
          if (ir->efep == efepNO)
          {
          }
          else
          {
 -            m = (1 - lambda)*atom.m + lambda*atom.mB;
 +            m = (1 - lambda) * atom.m + lambda * atom.mB;
          }
          real w;
          if (pg->params.nweight > 0)
          if (EI_ENERGY_MINIMIZATION(ir->eI))
          {
              /* Move the mass to the weight */
 -            w                   *= m;
 -            m                    = 1;
 +            w *= m;
 +            m = 1;
          }
          else if (ir->eI == eiBD)
          {
              real mbd;
 -            if (ir->bd_fric != 0.0f)
 +            if (ir->bd_fric != 0.0F)
              {
 -                mbd = ir->bd_fric*ir->delta_t;
 +                mbd = ir->bd_fric * ir->delta_t;
              }
              else
              {
 -                if (groups->grpnr[egcTC] == nullptr)
 +                if (groups.groupNumbers[SimulationAtomGroupType::TemperatureCoupling].empty())
                  {
 -                    mbd = ir->delta_t/ir->opts.tau_t[0];
 +                    mbd = ir->delta_t / ir->opts.tau_t[0];
                  }
                  else
                  {
 -                    mbd = ir->delta_t/ir->opts.tau_t[groups->grpnr[egcTC][ii]];
 +                    mbd = ir->delta_t
 +                          / ir->opts.tau_t[groups.groupNumbers[SimulationAtomGroupType::TemperatureCoupling][ii]];
                  }
              }
 -            w                   *= m/mbd;
 -            m                    = mbd;
 +            w *= m / mbd;
 +            m = mbd;
          }
          if (setWeights)
          {
              weights.push_back(w);
          }
 -        tmass  += m;
 -        wmass  += m*w;
 -        wwmass += m*w*w;
 +        tmass += m;
 +        wmass += m * w;
 +        wwmass += m * w * w;
      }
  
      if (wmass == 0)
      }
      if (fplog)
      {
 -        fprintf(fplog,
 -                "Pull group %d: %5d atoms, mass %9.3f",
 -                g, pg->params.nat, tmass);
 -        if (pg->params.weight ||
 -            EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
 +        fprintf(fplog, "Pull group %d: %5d atoms, mass %9.3f", g, pg->params.nat, tmass);
 +        if (pg->params.weight || EI_ENERGY_MINIMIZATION(ir->eI) || ir->eI == eiBD)
          {
 -            fprintf(fplog, ", weighted mass %9.3f", wmass*wmass/wwmass);
 +            fprintf(fplog, ", weighted mass %9.3f", wmass * wmass / wwmass);
          }
          if (pg->epgrppbc == epgrppbcCOS)
          {
      if (nfrozen == 0)
      {
          /* A value != 0 signals not frozen, it is updated later */
 -        pg->invtm  = -1.0;
 +        pg->invtm = -1.0;
      }
      else
      {
          int ndim = 0;
          for (int d = 0; d < DIM; d++)
          {
 -            ndim += pulldim_con[d]*pg->params.nat;
 +            ndim += pulldim_con[d] * pg->params.nat;
          }
          if (fplog && nfrozen > 0 && nfrozen < ndim)
          {
      }
  }
  
 -struct pull_t *
 -init_pull(FILE *fplog, const pull_params_t *pull_params, const t_inputrec *ir,
 -          const gmx_mtop_t *mtop, const t_commrec *cr, gmx::LocalAtomSetManager *atomSets,
 -          real lambda)
 +struct pull_t* init_pull(FILE*                     fplog,
 +                         const pull_params_t*      pull_params,
 +                         const t_inputrec*         ir,
 +                         const gmx_mtop_t*         mtop,
 +                         const t_commrec*          cr,
 +                         gmx::LocalAtomSetManager* atomSets,
 +                         real                      lambda)
  {
 -    struct pull_t *pull;
 -    pull_comm_t   *comm;
 +    struct pull_tpull;
 +    pull_comm_t*   comm;
  
      pull = new pull_t;
  
      /* Copy the pull parameters */
 -    pull->params       = *pull_params;
 +    pull->params = *pull_params;
      /* Avoid pointer copies */
      pull->params.group = nullptr;
      pull->params.coord = nullptr;
  
      for (int i = 0; i < pull_params->ngroup; ++i)
      {
 -        pull->group.emplace_back(pull_params->group[i], atomSets->add({pull_params->group[i].ind, pull_params->group[i].ind+pull_params->group[i].nat}),
 +        pull->group.emplace_back(pull_params->group[i],
 +                                 atomSets->add({ pull_params->group[i].ind,
 +                                                 pull_params->group[i].ind + pull_params->group[i].nat }),
                                   pull_params->bSetPbcRefToPrevStepCOM);
      }
  
      if (cr != nullptr && DOMAINDECOMP(cr))
      {
          /* Set up the global to local atom mapping for PBC atoms */
 -        for (pull_group_work_t &group : pull->group)
 +        for (pull_group_work_tgroup : pull->group)
          {
              if (group.epgrppbc == epgrppbcREFAT || group.epgrppbc == epgrppbcPREVSTEPCOM)
              {
                  /* pbcAtomSet consists of a single atom */
 -                group.pbcAtomSet = gmx::compat::make_unique<gmx::LocalAtomSet>(atomSets->add({&group.params.pbcatom, &group.params.pbcatom + 1}));
 +                group.pbcAtomSet = std::make_unique<gmx::LocalAtomSet>(
 +                        atomSets->add({ &group.params.pbcatom, &group.params.pbcatom + 1 }));
              }
          }
      }
      pull->bXOutAverage = pull_params->bXOutAverage;
      pull->bFOutAverage = pull_params->bFOutAverage;
  
 -    GMX_RELEASE_ASSERT(pull->group[0].params.nat == 0, "pull group 0 is an absolute reference group and should not contain atoms");
 +    GMX_RELEASE_ASSERT(pull->group[0].params.nat == 0,
 +                       "pull group 0 is an absolute reference group and should not contain atoms");
  
      pull->numCoordinatesWithExternalPotential = 0;
  
          /* Construct a pull coordinate, copying all coordinate parameters */
          pull->coord.emplace_back(pull_params->coord[c]);
  
 -        pull_coord_work_t *pcrd = &pull->coord.back();
 +        pull_coord_work_tpcrd = &pull->coord.back();
  
          switch (pcrd->params.eGeom)
          {
              case epullgDIST:
 -            case epullgDIRRELATIVE:  /* Direction vector is determined at each step */
 +            case epullgDIRRELATIVE: /* Direction vector is determined at each step */
              case epullgANGLE:
 -            case epullgDIHEDRAL:
 -                break;
 +            case epullgDIHEDRAL: break;
              case epullgDIR:
              case epullgDIRPBC:
              case epullgCYL:
                   * the string corresponding to the geometry, which will result
                   * in printing "UNDEFINED".
                   */
 -                gmx_fatal(FARGS, "Pull geometry not supported for pull coordinate %d. The geometry enum %d in the input is larger than that supported by the code (up to %d). You are probably reading a tpr file generated with a newer version of Gromacs with an binary from an older version of Gromacs.",
 +                gmx_fatal(FARGS,
 +                          "Pull geometry not supported for pull coordinate %d. The geometry enum "
 +                          "%d in the input is larger than that supported by the code (up to %d). "
 +                          "You are probably reading a tpr file generated with a newer version of "
 +                          "Gromacs with an binary from an older version of Gromacs.",
                            c + 1, pcrd->params.eGeom, epullgNR - 1);
          }
  
          if (pcrd->params.eType == epullCONSTRAINT)
          {
              /* Check restrictions of the constraint pull code */
 -            if (pcrd->params.eGeom == epullgCYL ||
 -                pcrd->params.eGeom == epullgDIRRELATIVE ||
 -                pcrd->params.eGeom == epullgANGLE ||
 -                pcrd->params.eGeom == epullgDIHEDRAL ||
 -                pcrd->params.eGeom == epullgANGLEAXIS)
 +            if (pcrd->params.eGeom == epullgCYL || pcrd->params.eGeom == epullgDIRRELATIVE
 +                || pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgDIHEDRAL
 +                || pcrd->params.eGeom == epullgANGLEAXIS)
              {
 -                gmx_fatal(FARGS, "Pulling of type %s can not be combined with geometry %s. Consider using pull type %s.",
 -                          epull_names[pcrd->params.eType],
 -                          epullg_names[pcrd->params.eGeom],
 +                gmx_fatal(FARGS,
 +                          "Pulling of type %s can not be combined with geometry %s. Consider using "
 +                          "pull type %s.",
 +                          epull_names[pcrd->params.eType], epullg_names[pcrd->params.eGeom],
                            epull_names[epullUMBRELLA]);
              }
  
          {
              pull->bCylinder = TRUE;
          }
 -        else if (pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgDIHEDRAL || pcrd->params.eGeom == epullgANGLEAXIS)
 +        else if (pcrd->params.eGeom == epullgANGLE || pcrd->params.eGeom == epullgDIHEDRAL
 +                 || pcrd->params.eGeom == epullgANGLEAXIS)
          {
              pull->bAngle = TRUE;
          }
          for (int i = 0; i < pcrd->params.ngroup; i++)
          {
              int groupIndex = pcrd->params.group[i];
 -            if (groupIndex > 0 &&
 -                !(pcrd->params.eGeom == epullgCYL && i == 0))
 +            if (groupIndex > 0 && !(pcrd->params.eGeom == epullgCYL && i == 0))
              {
                  pull->group[groupIndex].needToCalcCom = true;
              }
              /* Initialize the constant reference value */
              if (pcrd->params.eType != epullEXTERNAL)
              {
 -                low_set_pull_coord_reference_value(pcrd, c, pcrd->params.init*pull_conversion_factor_userinput2internal(&pcrd->params));
 +                low_set_pull_coord_reference_value(
 +                        pcrd, c,
 +                        pcrd->params.init * pull_conversion_factor_userinput2internal(&pcrd->params));
              }
              else
              {
  
          if (pcrd->params.eType == epullEXTERNAL)
          {
 -            GMX_RELEASE_ASSERT(pcrd->params.rate == 0, "With an external potential, a pull coordinate should have rate = 0");
 +            GMX_RELEASE_ASSERT(
 +                    pcrd->params.rate == 0,
 +                    "With an external potential, a pull coordinate should have rate = 0");
  
              /* This external potential needs to be registered later */
              pull->numCoordinatesWithExternalPotential++;
          pcrd->bExternalPotentialProviderHasBeenRegistered = false;
      }
  
 -    pull->numUnregisteredExternalPotentials             =
 -        pull->numCoordinatesWithExternalPotential;
 +    pull->numUnregisteredExternalPotentials             = pull->numCoordinatesWithExternalPotential;
      pull->numExternalPotentialsStillToBeAppliedThisStep = 0;
  
      pull->ePBC = ir->ePBC;
      switch (pull->ePBC)
      {
          case epbcNONE: pull->npbcdim = 0; break;
 -        case epbcXY:   pull->npbcdim = 2; break;
 -        default:       pull->npbcdim = 3; break;
 +        case epbcXY: pull->npbcdim = 2; break;
 +        default: pull->npbcdim = 3; break;
      }
  
      if (fplog)
          gmx_bool bAbs, bCos;
  
          bAbs = FALSE;
 -        for (const pull_coord_work_t &coord : pull->coord)
 +        for (const pull_coord_work_tcoord : pull->coord)
          {
 -            if (pull->group[coord.params.group[0]].params.nat == 0 ||
 -                pull->group[coord.params.group[1]].params.nat == 0)
 +            if (pull->group[coord.params.group[0]].params.nat == 0
 +                || pull->group[coord.params.group[1]].params.nat == 0)
              {
                  bAbs = TRUE;
              }
          }
          // Don't include the reference group 0 in output, so we report ngroup-1
          int numRealGroups = pull->group.size() - 1;
 -        GMX_RELEASE_ASSERT(numRealGroups > 0, "The reference absolute position pull group should always be present");
 -        fprintf(fplog, "with %zu pull coordinate%s and %d group%s\n",
 -                pull->coord.size(), pull->coord.size() == 1 ? "" : "s",
 -                numRealGroups, numRealGroups == 1 ? "" : "s");
 +        GMX_RELEASE_ASSERT(numRealGroups > 0,
 +                           "The reference absolute position pull group should always be present");
 +        fprintf(fplog, "with %zu pull coordinate%s and %d group%s\n", pull->coord.size(),
 +                pull->coord.size() == 1 ? "" : "s", numRealGroups, numRealGroups == 1 ? "" : "s");
          if (bAbs)
          {
              fprintf(fplog, "with an absolute reference\n");
          // Don't include the reference group 0 in loop
          for (size_t g = 1; g < pull->group.size(); g++)
          {
 -            if (pull->group[g].params.nat > 1 &&
 -                pull->group[g].params.pbcatom < 0)
 +            if (pull->group[g].params.nat > 1 && pull->group[g].params.pbcatom < 0)
              {
                  /* We are using cosine weighting */
                  fprintf(fplog, "Cosine weighting is used for group %zu\n", g);
          }
      }
  
 -    pull->bRefAt   = FALSE;
 -    pull->cosdim   = -1;
 +    pull->bRefAt = FALSE;
 +    pull->cosdim = -1;
      for (size_t g = 0; g < pull->group.size(); g++)
      {
 -        pull_group_work_t *pgrp;
 +        pull_group_work_tpgrp;
  
 -        pgrp           = &pull->group[g];
 +        pgrp = &pull->group[g];
          if (pgrp->params.nat > 0)
          {
              /* There is an issue when a group is used in multiple coordinates
              bConstraint = FALSE;
              clear_ivec(pulldim);
              clear_ivec(pulldim_con);
 -            for (const pull_coord_work_t &coord : pull->coord)
 +            for (const pull_coord_work_tcoord : pull->coord)
              {
                  gmx_bool bGroupUsed = FALSE;
                  for (int gi = 0; gi < coord.params.ngroup; gi++)
               */
              switch (pgrp->epgrppbc)
              {
 -                case epgrppbcREFAT:
 -                    pull->bRefAt = TRUE;
 -                    break;
 +                case epgrppbcREFAT: pull->bRefAt = TRUE; break;
                  case epgrppbcCOS:
                      if (pgrp->params.weight != nullptr)
                      {
 -                        gmx_fatal(FARGS, "Pull groups can not have relative weights and cosine weighting at same time");
 +                        gmx_fatal(FARGS,
 +                                  "Pull groups can not have relative weights and cosine weighting "
 +                                  "at same time");
                      }
                      for (int m = 0; m < DIM; m++)
                      {
                          {
                              if (pull->cosdim >= 0 && pull->cosdim != m)
                              {
 -                                gmx_fatal(FARGS, "Can only use cosine weighting with pulling in one dimension (use mdp option pull-coord?-dim)");
 +                                gmx_fatal(FARGS,
 +                                          "Can only use cosine weighting with pulling in one "
 +                                          "dimension (use mdp option pull-coord?-dim)");
                              }
                              pull->cosdim = m;
                          }
                      }
                      break;
 -                case epgrppbcNONE:
 -                    break;
 +                case epgrppbcNONE: break;
              }
  
              /* Set the indices */
 -            init_pull_group_index(fplog, cr, g, pgrp,
 -                                  bConstraint, pulldim_con,
 -                                  mtop, ir, lambda);
 +            init_pull_group_index(fplog, cr, g, pgrp, bConstraint, pulldim_con, mtop, ir, lambda);
          }
          else
          {
      /* If we use cylinder coordinates, do some initialising for them */
      if (pull->bCylinder)
      {
 -        for (const pull_coord_work_t &coord : pull->coord)
 +        for (const pull_coord_work_tcoord : pull->coord)
          {
              if (coord.params.eGeom == epullgCYL)
              {
                  if (pull->group[coord.params.group[0]].params.nat == 0)
                  {
 -                    gmx_fatal(FARGS, "A cylinder pull group is not supported when using absolute reference!\n");
 +                    gmx_fatal(FARGS,
 +                              "A cylinder pull group is not supported when using absolute "
 +                              "reference!\n");
                  }
              }
 -            const auto &referenceGroup = pull->group[coord.params.group[0]];
 -            pull->dyna.emplace_back(referenceGroup.params, referenceGroup.atomSet, pull->params.bSetPbcRefToPrevStepCOM);
 +            const auto& referenceGroup = pull->group[coord.params.group[0]];
 +            pull->dyna.emplace_back(referenceGroup.params, referenceGroup.atomSet,
 +                                    pull->params.bSetPbcRefToPrevStepCOM);
          }
      }
  
       * when we have an external pull potential, since then the external
       * potential provider expects each rank to have the coordinate.
       */
 -    comm->bParticipateAll = (cr == nullptr || !DOMAINDECOMP(cr) ||
 -                             cr->dd->nnodes <= 32 ||
 -                             pull->numCoordinatesWithExternalPotential > 0 ||
 -                             getenv("GMX_PULL_PARTICIPATE_ALL") != nullptr);
 +    comm->bParticipateAll = (cr == nullptr || !DOMAINDECOMP(cr) || cr->dd->nnodes <= 32
 +                             || pull->numCoordinatesWithExternalPotential > 0
 +                             || getenv("GMX_PULL_PARTICIPATE_ALL") != nullptr);
      /* This sub-commicator is not used with comm->bParticipateAll,
       * so we can always initialize it to NULL.
       */
 -    comm->mpi_comm_com    = MPI_COMM_NULL;
 -    comm->nparticipate    = 0;
 -    comm->isMasterRank    = (cr == nullptr || MASTER(cr));
 +    comm->mpi_comm_com = MPI_COMM_NULL;
 +    comm->nparticipate = 0;
 +    comm->isMasterRank = (cr == nullptr || MASTER(cr));
  #else
      /* No MPI: 1 rank: all ranks pull */
      comm->bParticipateAll = TRUE;
      comm->isMasterRank    = true;
  #endif
 -    comm->bParticipate    = comm->bParticipateAll;
 -    comm->setup_count     = 0;
 -    comm->must_count      = 0;
 +    comm->bParticipate = comm->bParticipateAll;
 +    comm->setup_count  = 0;
 +    comm->must_count   = 0;
  
      if (!comm->bParticipateAll && fplog != nullptr)
      {
      }
  
      comm->pbcAtomBuffer.resize(pull->group.size());
 -    comm->comBuffer.resize(pull->group.size()*c_comBufferStride);
 +    comm->comBuffer.resize(pull->group.size() * c_comBufferStride);
      if (pull->bCylinder)
      {
 -        comm->cylinderBuffer.resize(pull->coord.size()*c_cylinderBufferStride);
 +        comm->cylinderBuffer.resize(pull->coord.size() * c_cylinderBufferStride);
      }
  
      /* We still need to initialize the PBC reference coordinates */
      return pull;
  }
  
 -static void destroy_pull(struct pull_t *pull)
 +static void destroy_pull(struct pull_tpull)
  {
  #if GMX_MPI
      if (pull->comm.mpi_comm_com != MPI_COMM_NULL)
      delete pull;
  }
  
 -void preparePrevStepPullCom(const t_inputrec *ir, const t_mdatoms *md, t_state *state, const t_state *state_global, const t_commrec *cr, bool startingFromCheckpoint)
 +void preparePrevStepPullCom(const t_inputrec* ir,
 +                            pull_t*           pull_work,
 +                            const t_mdatoms*  md,
 +                            t_state*          state,
 +                            const t_state*    state_global,
 +                            const t_commrec*  cr,
 +                            bool              startingFromCheckpoint)
  {
      if (!ir->pull || !ir->pull->bSetPbcRefToPrevStepCOM)
      {
          return;
      }
 -    allocStatePrevStepPullCom(state, ir->pull_work);
 +    allocStatePrevStepPullCom(state, pull_work);
      if (startingFromCheckpoint)
      {
          if (MASTER(cr))
              /* Only the master rank has the checkpointed COM from the previous step */
              gmx_bcast(sizeof(double) * state->pull_com_prev_step.size(), &state->pull_com_prev_step[0], cr);
          }
 -        setPrevStepPullComFromState(ir->pull_work, state);
 +        setPrevStepPullComFromState(pull_work, state);
      }
      else
      {
          t_pbc pbc;
          set_pbc(&pbc, ir->ePBC, state->box);
 -        initPullComFromPrevStep(cr, ir->pull_work, md, &pbc, state->x.rvec_array());
 -        updatePrevStepPullCom(ir->pull_work, state);
 +        initPullComFromPrevStep(cr, pull_work, md, &pbc, state->x.rvec_array());
 +        updatePrevStepPullCom(pull_work, state);
      }
  }
  
 -void finish_pull(struct pull_t *pull)
 +void finish_pull(struct pull_tpull)
  {
      check_external_potential_registration(pull);
  
      destroy_pull(pull);
  }
  
 -gmx_bool pull_have_potential(const struct pull_t *pull)
 +gmx_bool pull_have_potential(const struct pull_tpull)
  {
      return pull->bPotential;
  }
  
 -gmx_bool pull_have_constraint(const struct pull_t *pull)
 +gmx_bool pull_have_constraint(const struct pull_tpull)
  {
      return pull->bConstraint;
  }