Merge origin/release-2019 into release-2020
authorPaul Bauer <paul.bauer.q@gmail.com>
Fri, 27 Dec 2019 12:58:41 +0000 (13:58 +0100)
committerPaul Bauer <paul.bauer.q@gmail.com>
Fri, 27 Dec 2019 12:58:41 +0000 (13:58 +0100)
Resolved Conflicts:
cmake/FindHwloc.cmake
cmake/gmxVersionInfo.cmake
docs/CMakeLists.txt
src/gromacs/gmxana/anadih.cpp
src/gromacs/gmxana/gmx_angle.cpp
src/gromacs/mdlib/forcerec.cpp

Change-Id: Ie4408c932a712d4f9d1f42117a512e0474535df5

1  2 
docs/CMakeLists.txt
docs/release-notes/index.rst
src/gromacs/fileio/pdbio.cpp
src/gromacs/gmxana/anadih.cpp
src/gromacs/gmxana/angle_correction.cpp
src/gromacs/gmxana/cmat.cpp
src/gromacs/gmxana/gmx_angle.cpp
src/gromacs/mdlib/forcerec.cpp

diff --combined docs/CMakeLists.txt
index b13a72c54dffd727f7ca89f71dd39ece142ff06d,204e2fea8849340d830ab95ca1b289872062dc36..32636d3dd1e1aa1305379bd4adf267b86d2abc2f
@@@ -60,13 -60,18 +60,13 @@@ set(EXPECTED_DOXYGEN_VERSION 1.8.5
  
  set(EXPECTED_SPHINX_VERSION 1.6.1)
  
 -if (DEFINED PYTHON_EXECUTABLE)
 -    # Keep quiet on subsequent runs of cmake
 -    set(PythonInterp_FIND_QUIETLY ON)
 -endif()
 -find_package(PythonInterp 2.7)
 -
 -
 -if (NOT ${PYTHON_VERSION_MAJOR} EQUAL 3)
 -    find_package(Sphinx ${EXPECTED_SPHINX_VERSION} QUIET COMPONENTS pygments)
 -else()
 -    MESSAGE(STATUS "Can not build documentation with Python 3")
 +# By default, suppress output after first configuration.
 +if(SPHINX_ALREADY_SEARCHED)
 +    set(Sphinx_FIND_QUIETLY ON)
  endif()
 +find_package(Sphinx ${EXPECTED_SPHINX_VERSION} COMPONENTS pygments)
 +set(SPHINX_ALREADY_SEARCHED TRUE CACHE BOOL "True if a search for Sphinx has already been done")
 +mark_as_advanced(SPHINX_ALREADY_SEARCHED)
  
  # Even if we aren't going to make the full webpage, set up to put all
  # the documentation output in the same place, for convenience
@@@ -108,7 -113,10 +108,7 @@@ elseif (BUILD_IS_INSOURCE
      set(MANUAL_BUILD_NOT_POSSIBLE_REASON "the build is in-source")
  else()
      include(manual/UseLATEX.cmake)
 -    if(${PYTHON_VERSION_MAJOR} EQUAL 3)
 -        set(MANUAL_BUILD_IS_POSSIBLE OFF)
 -        set(MANUAL_BUILD_NOT_POSSIBLE_REASON "We can not build the documentation when using python3")
 -    elseif(NOT SPHINX_FOUND)
 +    if(NOT SPHINX_FOUND)
          set(MANUAL_BUILD_IS_POSSIBLE OFF)
          set(MANUAL_BUILD_NOT_POSSIBLE_REASON "Sphinx has not been found and is needed to create the LaTex input files")
      elseif(NOT PDFLATEX_COMPILER)
@@@ -223,7 -231,6 +223,7 @@@ if (SPHINX_FOUND
          reference-manual/special/vmd-imd.rst
          reference-manual/special/membrane-embedding.rst
          reference-manual/special/mimic-qmmm.rst
 +        reference-manual/special/density-guided-simulation.rst
          # Analysis chapter
          reference-manual/analysis.rst
          reference-manual/analysis/using-groups.rst
          reference-manual/analysis/rmsd.rst
          reference-manual/analysis/covariance-analysis.rst
          reference-manual/analysis/dihedral-pca.rst
 +        reference-manual/analysis/hydrogen-bonds.rst
          reference-manual/analysis/protein-related.rst
          reference-manual/analysis/interface-related.rst)
      # The image files have also been ordered by the respective
          dev-manual/style.rst
          dev-manual/testutils.rst
          dev-manual/tools.rst
 -        dev-manual/uncrustify.rst
 +        dev-manual/code-formatting.rst
          fragments/doxygen-links.rst
          how-to/index.rst
          how-to/beginners.rst
          how-to/visualize.rst
          install-guide/index.rst
          release-notes/index.rst
 +        release-notes/2020/major/highlights.rst
 +        release-notes/2020/major/features.rst
 +        release-notes/2020/major/performance.rst
 +        release-notes/2020/major/tools.rst
 +        release-notes/2020/major/bugs-fixed.rst
 +        release-notes/2020/major/removed-functionality.rst
 +        release-notes/2020/major/deprecated-functionality.rst
 +        release-notes/2020/major/portability.rst
 +        release-notes/2020/major/miscellaneous.rst
+         release-notes/2019/2019.6.rst
          release-notes/2019/2019.5.rst
          release-notes/2019/2019.4.rst
          release-notes/2019/2019.3.rst
          # if the documentation will be build with the full reference
          # manual or without.
          user-guide/cmdline.rst
 -        user-guide/cutoff-schemes.rst
          user-guide/deprecation-policy.rst
          user-guide/environment-variables.rst
          user-guide/faq.rst
      endif()
  
      set(SPHINX_CONFIG_VARS_FILE ${SPHINX_INPUT_DIR}/conf-vars.py)
 +    if (GMX_PYTHON_PACKAGE)
 +        set(GMXAPI_PYTHON_STAGING_DIR ${CMAKE_BINARY_DIR}/python_packaging/src/gmxapi_staging)
 +        # TODO: Resolve circular reference. We would like to get the CMake build-time directory for
 +        # the gmxapi Python package from the _gmxapi target, as we do when building sample_restraint.
 +        # Instead of the above hard-coded path, how can we do
 +        # get_target_property(GMXAPI_PYTHON_STAGING_DIR _gmxapi staging_dir)
 +        # in this context?
 +    endif ()
 +
      gmx_configure_version_file(conf-vars.py.cmakein ${SPHINX_CONFIG_VARS_FILE}
          EXTRA_VARS
              SPHINX_EXTENSION_PATH RELENG_PATH
              GMX_LMFIT_REQUIRED_VERSION
              GMX_MANUAL_DOI_STRING
              GMX_SOURCE_DOI_STRING
 +            GMXAPI_PYTHON_STAGING_DIR
          COMMENT "Configuring Sphinx configuration file")
      gmx_add_sphinx_input_file(${SPHINX_CONFIG_VARS_FILE})
      gmx_add_sphinx_source_files(FILES ${SPHINX_SOURCE_FILES})
              dev-manual/releng/jenkins-ui.rst
              )
      endif()
 +
 +    gmx_add_sphinx_source_files(
 +            FILES
 +            gmxapi/index.rst
 +            gmxapi/userguide/install.rst
 +            gmxapi/userguide/usage.rst
 +            gmxapi/userguide/userguide.rst
 +    )
 +
 +    if (GMX_PYTHON_PACKAGE)
 +        gmx_add_sphinx_source_files(
 +            FILES
 +            gmxapi/userguide/pythonreference.rst
 +            )
 +    else()
 +        gmx_add_sphinx_source_files(
 +            FROM ${CMAKE_CURRENT_SOURCE_DIR}/gmxapi/userguide-stub
 +            TO gmxapi/userguide/ 
 +            FILES pythonreference.rst 
 +            )
 +    endif ()
 +
      gmx_add_sphinx_source_files(
          FILES
          ${REFERENCEMANUAL_SPHINX_FILES_GENERAL})
      gmx_add_sphinx_image_conversion_target(sphinx-image-conversion)
      add_custom_target(sphinx-input)
      add_dependencies(sphinx-input sphinx-input-rst sphinx-image-conversion)
 +    if (GMX_PYTHON_PACKAGE)
 +        add_dependencies(sphinx-input _gmxapi)
 +    endif()
      # Remove other rst files from the build tree, since they confuse Sphinx.
      # Skip generated files in onlinehelp/, and fragments.
      # The latter do not cause issues with obsolete files, as they
      add_custom_target(install-guide
          COMMAND
              ${SPHINX_EXECUTABLE}
 -            -q -E -b text
 +            -q -b text
              -w sphinx-install.log
              -d ${CMAKE_CURRENT_BINARY_DIR}/install-guide/_doctrees
              -c ${SPHINX_INPUT_DIR}
      add_custom_target(webpage-sphinx
          DEPENDS sphinx-programs
          DEPENDS sphinx-input
 -        DEPENDS sphinx-image-conversion 
 +        DEPENDS sphinx-image-conversion
 +        DEPENDS manual
          COMMAND
              ${CMAKE_COMMAND} -E make_directory ${SPHINX_INPUT_DIR}/_static
          COMMAND
              ${SPHINX_EXECUTABLE}
 -            -q -E -b html
 +            -q -b html
              -w sphinx-html.log
              -d "${SPHINX_CACHE_DIR}"
              "${SPHINX_INPUT_DIR}"
      add_custom_target(man
          COMMAND
              ${SPHINX_EXECUTABLE}
 -            -q -E -b man
 +            -q -b man
              -w sphinx-man.log
              -d ${SPHINX_CACHE_DIR}
              -t do_man
@@@ -681,7 -644,7 +682,7 @@@ set(HTML_BUILD_NOT_POSSIBLE_REASON
  set(HTML_BUILD_WARNINGS)
  
  # Next, turn it off if any of the preconditions are unsatisified
 -if (NOT PYTHON_EXECUTABLE)
 +if (NOT PythonInterp_FOUND)
      set(HTML_BUILD_IS_POSSIBLE OFF)
      set(HTML_BUILD_NOT_POSSIBLE_REASON "Python is required")
  elseif (NOT SPHINX_FOUND)
index bfb9ab19afa60d3101646d7a99b290f10595e0ea,a5723a06723e703781ccca770bcd8ff51ca4954b..16cb7ea8ac75c86ddba6de2e545b61ec5b65f3e8
@@@ -8,38 -8,18 +8,38 @@@ releases of |Gromacs|. Major releases c
  functionality supported, whereas patch releases contain only fixes for
  issues identified in the corresponding major releases.
  
 -Two versions of |Gromacs| are under active maintenance, the 2019
 -series and the 2018 series. In the latter, only highly conservative
 +Two versions of |Gromacs| are under active maintenance, the 2020
 +series and the 2019 series. In the latter, only highly conservative
  fixes will be made, and only to address issues that affect scientific
  correctness. Naturally, some of those releases will be made after the
 -year 2018 ends, but we keep 2018 in the name so users understand how
 +year 2019 ends, but we keep 2018 in the name so users understand how
  up to date their version is. Such fixes will also be incorporated into
 -the 2019 release series, as appropriate. Around the time the 2020
 -release is made, the 2018 series will no longer be maintained.
 +the 2020 release series, as appropriate. Around the time the 2021
 +release is made, the 2019 series will no longer be maintained.
  
  Where issue numbers are reported in these release notes, more details
  can be found at https://redmine.gromacs.org at that issue number.
  
 +|Gromacs| 2020 series
 +---------------------
 +
 +Major release
 +^^^^^^^^^^^^^
 +
 +.. toctree::
 +   :maxdepth: 1
 +
 +   2020/major/highlights
 +   2020/major/features
 +   2020/major/performance
 +   2020/major/tools
 +   2020/major/bugs-fixed
 +   2020/major/deprecated-functionality
 +   2020/major/removed-functionality
 +   2020/major/portability
 +   2020/major/miscellaneous
 +
 +
  |Gromacs| 2019 series
  ---------------------
  
@@@ -49,6 -29,7 +49,7 @@@ Patch release
  .. toctree::
     :maxdepth: 1
  
+    2019/2019.6
     2019/2019.5
     2019/2019.4
     2019/2019.3
@@@ -71,8 -52,6 +72,8 @@@ Major releas
     2019/major/portability
     2019/major/miscellaneous
  
 +Older (unmaintained) |Gromacs| series
 +-------------------------------------------------------
  
  |Gromacs| 2018 series
  ---------------------
@@@ -136,6 -115,8 +137,6 @@@ Major releas
     2016/major/removed-features
     2016/major/miscellaneous
  
 -Older (unmaintained) |Gromacs| series
 --------------------------------------------------------
  
  .. toctree::
     :maxdepth: 1
index 3466d8d4f1114644918dbe3c9fba228062ccc701,9a30b7fa5df3c2586238d83144658471a7e7656e..f431614cc8a512e39d767b6fd97c9a9d9726a755
  #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";
+     std::string form2  = "%*s";
+     std::string format = form2 + "%d";
      if (sscanf(line, format.c_str(), &ai) == 1)
      {
          do
          {
              form2 += "%*s";
-             format = form2 + "%%d";
+             format = form2 + "%d";
              n      = sscanf(line, format.c_str(), &aj);
              if (n == 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 b76f99dc1aed9e2bf31e3bcd5ccedf66b7595668,c266e8961dc0078d39c2c4a2417b5fd0b8ac4c95..bfe16bede134eead6646469ce52e79c2d1819237
@@@ -45,8 -45,9 +45,9 @@@
  #include "gromacs/fileio/confio.h"
  #include "gromacs/fileio/trxio.h"
  #include "gromacs/fileio/xvgr.h"
+ #include "gromacs/gmxana/angle_correction.h"
  #include "gromacs/gmxana/gstat.h"
 -#include "gromacs/listed-forces/bonded.h"
 +#include "gromacs/listed_forces/bonded.h"
  #include "gromacs/math/functions.h"
  #include "gromacs/math/units.h"
  #include "gromacs/math/vec.h"
  #include "gromacs/utility/fatalerror.h"
  #include "gromacs/utility/smalloc.h"
  
 -void print_one(const gmx_output_env_t *oenv, const char *base, const char *name,
 -               const char *title, const char *ylabel, int nf, real time[],
 -               real data[])
 +void print_one(const gmx_output_env_t* oenv,
 +               const char*             base,
 +               const char*             name,
 +               const char*             title,
 +               const char*             ylabel,
 +               int                     nf,
 +               real                    time[],
 +               real                    data[])
  {
 -    FILE *fp;
 +    FILEfp;
      char  buf[256], t2[256];
      int   k;
  
@@@ -84,9 -80,9 +85,9 @@@ static int calc_RBbin(real phi, int gmx
  {
      /* multiplicity and core_frac NOT used,
       * just given to enable use of pt-to-fn in caller low_ana_dih_trans*/
 -    static const real r30  = M_PI/6.0;
 -    static const real r90  = M_PI/2.0;
 -    static const real r150 = M_PI*5.0/6.0;
 +    static const real r30  = M_PI / 6.0;
 +    static const real r90  = M_PI / 2.0;
 +    static const real r150 = M_PI * 5.0 / 6.0;
  
      if ((phi < r30) && (phi > -r30))
      {
  
  static int calc_Nbin(real phi, int multiplicity, real core_frac)
  {
 -    static const real r360 = 360*DEG2RAD;
 +    static const real r360 = 360 * DEG2RAD;
      real              rot_width, core_width, core_offset, low, hi;
      int               bin;
      /* with multiplicity 3 and core_frac 0.5
          phi += r360;
      }
  
 -    rot_width   = 360./multiplicity;
 +    rot_width   = 360. / multiplicity;
      core_width  = core_frac * rot_width;
 -    core_offset = (rot_width - core_width)/2.0;
 +    core_offset = (rot_width - core_width) / 2.0;
      for (bin = 1; bin <= multiplicity; bin++)
      {
 -        low  = ((bin - 1) * rot_width ) + core_offset;
 -        hi   = ((bin - 1) * rot_width ) + core_offset + core_width;
 +        low = ((bin - 1) * rot_width) + core_offset;
 +        hi  = ((bin - 1) * rot_width) + core_offset + core_width;
          low *= DEG2RAD;
 -        hi  *= DEG2RAD;
 +        hi *= DEG2RAD;
          if ((phi > low) && (phi < hi))
          {
              return bin;
      return 0;
  }
  
 -void ana_dih_trans(const char *fn_trans, const char *fn_histo,
 -                   real **dih, int nframes, int nangles,
 -                   const char *grpname, real *time, gmx_bool bRb,
 -                   const gmx_output_env_t *oenv)
 +void ana_dih_trans(const char*             fn_trans,
 +                   const char*             fn_histo,
 +                   real**                  dih,
 +                   int                     nframes,
 +                   int                     nangles,
 +                   const char*             grpname,
 +                   real*                   time,
 +                   gmx_bool                bRb,
 +                   const gmx_output_env_t* oenv)
  {
      /* just a wrapper; declare extra args, then chuck away at end. */
      int      maxchi = 0;
 -    t_dlist *dlist;
 -    int     *multiplicity;
 +    t_dlistdlist;
 +    int*     multiplicity;
      int      nlist = nangles;
      int      k;
  
          multiplicity[k] = 3;
      }
  
 -    low_ana_dih_trans(TRUE, fn_trans, TRUE, fn_histo, maxchi,
 -                      dih, nlist, dlist, nframes,
 -                      nangles, grpname, multiplicity, time, bRb, 0.5, oenv);
 +    low_ana_dih_trans(TRUE, fn_trans, TRUE, fn_histo, maxchi, dih, nlist, dlist, nframes, nangles,
 +                      grpname, multiplicity, time, bRb, 0.5, oenv);
      sfree(dlist);
      sfree(multiplicity);
 -
  }
  
 -void low_ana_dih_trans(gmx_bool bTrans, const char *fn_trans,
 -                       gmx_bool bHisto, const char *fn_histo, int maxchi,
 -                       real **dih, int nlist, t_dlist dlist[], int nframes,
 -                       int nangles, const char *grpname, int multiplicity[],
 -                       real *time, gmx_bool bRb, real core_frac,
 -                       const gmx_output_env_t *oenv)
 +void low_ana_dih_trans(gmx_bool                bTrans,
 +                       const char*             fn_trans,
 +                       gmx_bool                bHisto,
 +                       const char*             fn_histo,
 +                       int                     maxchi,
 +                       real**                  dih,
 +                       int                     nlist,
 +                       t_dlist                 dlist[],
 +                       int                     nframes,
 +                       int                     nangles,
 +                       const char*             grpname,
 +                       int                     multiplicity[],
 +                       real*                   time,
 +                       gmx_bool                bRb,
 +                       real                    core_frac,
 +                       const gmx_output_env_t* oenv)
  {
 -    FILE *fp;
 -    int  *tr_f, *tr_h;
 +    FILEfp;
 +    int tr_f, *tr_h;
      char  title[256];
      int   i, j, k, Dih, ntrans;
      int   cur_bin, new_bin;
      real  ttime;
 -    real *rot_occ[NROT];
 -    int   (*calc_bin)(real, int, real);
 -    real  dt;
 +    realrot_occ[NROT];
 +    int (*calc_bin)(real, int, real);
 +    real dt;
  
      if (1 <= nframes)
      {
          return;
      }
      /* Assumes the frames are equally spaced in time */
 -    dt = (time[nframes-1]-time[0])/(nframes-1);
 +    dt = (time[nframes - 1] - time[0]) / (nframes - 1);
  
      /* Analysis of dihedral transitions */
      fprintf(stderr, "Now calculating transitions...\n");
              }
  #else
              /* why is all this md rubbish periodic? Remove 360 degree periodicity */
 -            if ( (dih[i][j] - prev) > M_PI)
 +            if ((dih[i][j] - prev) > M_PI)
              {
 -                dih[i][j] -= 2*M_PI;
 +                dih[i][j] -= 2 * M_PI;
              }
 -            else if ( (dih[i][j] - prev) < -M_PI)
 +            else if ((dih[i][j] - prev) < -M_PI)
              {
 -                dih[i][j] += 2*M_PI;
 +                dih[i][j] += 2 * M_PI;
              }
  
              prev = dih[i][j];
  
              mind = std::min(mind, dih[i][j]);
              maxd = std::max(maxd, dih[i][j]);
 -            if ( (maxd - mind) > 2*M_PI/3) /* or 120 degrees, assuming       */
 -            {                              /* multiplicity 3. Not so general.*/
 +            if ((maxd - mind) > 2 * M_PI / 3) /* or 120 degrees, assuming       */
 +            {                                 /* multiplicity 3. Not so general.*/
                  tr_f[j]++;
                  tr_h[i]++;
                  maxd = mind = dih[i][j]; /* get ready for next transition  */
      fprintf(stderr, "Total number of transitions: %10d\n", ntrans);
      if (ntrans > 0)
      {
 -        ttime = (dt*nframes*nangles)/ntrans;
 +        ttime = (dt * nframes * nangles) / ntrans;
          fprintf(stderr, "Time between transitions:    %10.3f ps\n", ttime);
      }
  
       * based on fn histogramming in g_chi. diff roles for i and j here */
  
      j = 0;
 -    for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
 +    for (Dih = 0; (Dih < NONCHI + maxchi); Dih++)
      {
          for (i = 0; (i < nlist); i++)
          {
 -            if (((Dih  < edOmega) ) ||
 -                ((Dih == edOmega) && (has_dihedral(edOmega, &(dlist[i])))) ||
 -                ((Dih  > edOmega) && (dlist[i].atm.Cn[Dih-NONCHI+3] != -1)))
 +            if (((Dih < edOmega)) || ((Dih == edOmega) && (has_dihedral(edOmega, &(dlist[i]))))
 +                || ((Dih > edOmega) && (dlist[i].atm.Cn[Dih - NONCHI + 3] != -1)))
              {
                  /* grs debug  printf("Not OK? i %d j %d Dih %d \n", i, j, Dih) ; */
                  dlist[i].ntr[Dih] = tr_h[j];
      {
          tr_f[tr_h[i]]++;
      }
 -    for (j = nframes; ((tr_f[j-1] == 0) && (j > 0)); j--)
 -    {
 -        ;
 -    }
 +    for (j = nframes; ((tr_f[j - 1] == 0) && (j > 0)); j--) {}
  
 -    ttime = dt*nframes;
 +    ttime = dt * nframes;
      if (bHisto)
      {
          sprintf(title, "Transition time: %s", grpname);
          fp = xvgropen(fn_histo, title, "Time (ps)", "#", oenv);
 -        for (i = j-1; (i > 0); i--)
 +        for (i = j - 1; (i > 0); i--)
          {
              if (tr_f[i] != 0)
              {
 -                fprintf(fp, "%10.3f  %10d\n", ttime/i, tr_f[i]);
 +                fprintf(fp, "%10.3f  %10d\n", ttime / i, tr_f[i]);
              }
          }
          xvgrclose(fp);
      {
          sfree(rot_occ[k]);
      }
 -
  }
  
 -void mk_multiplicity_lookup (int *multiplicity, int maxchi,
 -                             int nlist, t_dlist dlist[], int nangles)
 +void mk_multiplicity_lookup(int* multiplicity, int maxchi, int nlist, t_dlist dlist[], int nangles)
  {
      /* new by grs - for dihedral j (as in dih[j]) get multiplicity from dlist
       * and store in multiplicity[j]
      char name[4];
  
      j = 0;
 -    for (Dih = 0; (Dih < NONCHI+maxchi); Dih++)
 +    for (Dih = 0; (Dih < NONCHI + maxchi); Dih++)
      {
          for (i = 0; (i < nlist); i++)
          {
              std::strncpy(name, dlist[i].name, 3);
              name[3] = '\0';
 -            if (((Dih  < edOmega) ) ||
 -                ((Dih == edOmega) && (has_dihedral(edOmega, &(dlist[i])))) ||
 -                ((Dih  > edOmega) && (dlist[i].atm.Cn[Dih-NONCHI+3] != -1)))
 +            if (((Dih < edOmega)) || ((Dih == edOmega) && (has_dihedral(edOmega, &(dlist[i]))))
 +                || ((Dih > edOmega) && (dlist[i].atm.Cn[Dih - NONCHI + 3] != -1)))
              {
                  /* default - we will correct the rest below */
                  multiplicity[j] = 3;
                  }
  
                  /* dihedrals to aromatic rings, COO, CONH2 or guanidinium are 2fold*/
 -                if (Dih > edOmega && (dlist[i].atm.Cn[Dih-NONCHI+3] != -1))
 +                if (Dih > edOmega && (dlist[i].atm.Cn[Dih - NONCHI + 3] != -1))
                  {
 -                    if ( ((std::strstr(name, "PHE") != nullptr) && (Dih == edChi2))  ||
 -                         ((std::strstr(name, "TYR") != nullptr) && (Dih == edChi2))  ||
 -                         ((std::strstr(name, "PTR") != nullptr) && (Dih == edChi2))  ||
 -                         ((std::strstr(name, "TRP") != nullptr) && (Dih == edChi2))  ||
 -                         ((std::strstr(name, "HIS") != nullptr) && (Dih == edChi2))  ||
 -                         ((std::strstr(name, "GLU") != nullptr) && (Dih == edChi3))  ||
 -                         ((std::strstr(name, "ASP") != nullptr) && (Dih == edChi2))  ||
 -                         ((std::strstr(name, "GLN") != nullptr) && (Dih == edChi3))  ||
 -                         ((std::strstr(name, "ASN") != nullptr) && (Dih == edChi2))  ||
 -                         ((std::strstr(name, "ARG") != nullptr) && (Dih == edChi4))  )
 +                    if (((std::strstr(name, "PHE") != nullptr) && (Dih == edChi2))
 +                        || ((std::strstr(name, "TYR") != nullptr) && (Dih == edChi2))
 +                        || ((std::strstr(name, "PTR") != nullptr) && (Dih == edChi2))
 +                        || ((std::strstr(name, "TRP") != nullptr) && (Dih == edChi2))
 +                        || ((std::strstr(name, "HIS") != nullptr) && (Dih == edChi2))
 +                        || ((std::strstr(name, "GLU") != nullptr) && (Dih == edChi3))
 +                        || ((std::strstr(name, "ASP") != nullptr) && (Dih == edChi2))
 +                        || ((std::strstr(name, "GLN") != nullptr) && (Dih == edChi3))
 +                        || ((std::strstr(name, "ASN") != nullptr) && (Dih == edChi2))
 +                        || ((std::strstr(name, "ARG") != nullptr) && (Dih == edChi4)))
                      {
                          multiplicity[j] = 2;
                      }
      }
      if (j < nangles)
      {
 -        fprintf(stderr, "WARNING: not all dihedrals found in topology (only %d out of %d)!\n",
 -                j, nangles);
 +        fprintf(stderr, "WARNING: not all dihedrals found in topology (only %d out of %d)!\n", j, nangles);
      }
      /* Check for remaining dihedrals */
      for (; (j < nangles); j++)
      {
          multiplicity[j] = 3;
      }
 -
  }
  
 -void mk_chi_lookup (int **lookup, int maxchi,
 -                    int nlist, t_dlist dlist[])
 +void mk_chi_lookup(int** lookup, int maxchi, int nlist, t_dlist dlist[])
  {
  
      /* by grs. should rewrite everything to use this. (but haven't,
  
      j = 0;
      /* NONCHI points to chi1, therefore we have to start counting there. */
 -    for (Dih = NONCHI; (Dih < NONCHI+maxchi); Dih++)
 +    for (Dih = NONCHI; (Dih < NONCHI + maxchi); Dih++)
      {
          for (i = 0; (i < nlist); i++)
          {
              Chi = Dih - NONCHI;
 -            if (((Dih  < edOmega) ) ||
 -                ((Dih == edOmega) && (has_dihedral(edOmega, &(dlist[i])))) ||
 -                ((Dih  > edOmega) && (dlist[i].atm.Cn[Dih-NONCHI+3] != -1)))
 +            if (((Dih < edOmega)) || ((Dih == edOmega) && (has_dihedral(edOmega, &(dlist[i]))))
 +                || ((Dih > edOmega) && (dlist[i].atm.Cn[Dih - NONCHI + 3] != -1)))
              {
                  /* grs debug  printf("Not OK? i %d j %d Dih %d \n", i, j, Dih) ; */
                  if (Dih > edOmega)
              }
          }
      }
 -
  }
  
  
 -void get_chi_product_traj (real **dih, int nframes, int nlist,
 -                           int maxchi, t_dlist dlist[], real time[],
 -                           int **lookup, int *multiplicity, gmx_bool bRb, gmx_bool bNormalize,
 -                           real core_frac, gmx_bool bAll, const char *fnall,
 -                           const gmx_output_env_t *oenv)
 +void get_chi_product_traj(real**                  dih,
 +                          int                     nframes,
 +                          int                     nlist,
 +                          int                     maxchi,
 +                          t_dlist                 dlist[],
 +                          real                    time[],
 +                          int**                   lookup,
 +                          int*                    multiplicity,
 +                          gmx_bool                bRb,
 +                          gmx_bool                bNormalize,
 +                          real                    core_frac,
 +                          gmx_bool                bAll,
 +                          const char*             fnall,
 +                          const gmx_output_env_t* oenv)
  {
  
      gmx_bool bRotZero, bHaveChi = FALSE;
      int      accum = 0, index, i, j, k, Xi, n, b;
 -    real    *chi_prtrj;
 -    int     *chi_prhist;
 +    real*    chi_prtrj;
 +    int*     chi_prhist;
      int      nbin;
 -    FILE    *fp, *fpall;
 +    FILE *   fp, *fpall;
      char     hisfile[256], histitle[256], *namept;
  
 -    int      (*calc_bin)(real, int, real);
 +    int (*calc_bin)(real, int, real);
  
      /* Analysis of dihedral transitions */
      fprintf(stderr, "Now calculating Chi product trajectories...\n");
              if (index >= 0)
              {
                  n    = multiplicity[index];
 -                nbin = n*nbin;
 +                nbin = n * nbin;
              }
          }
          nbin += 1; /* for the "zero rotamer", outside the core region */
              else
              {
                  chi_prtrj[j] = accum;
 -                if (accum+1 > nbin)
 +                if (accum + 1 > nbin)
                  {
 -                    nbin = accum+1;
 +                    nbin = accum + 1;
                  }
              }
          }
                  {
                      if (bNormalize)
                      {
 -                        fprintf(fp, "%5d  %10g\n", k, (1.0*chi_prhist[k])/nframes);
 +                        fprintf(fp, "%5d  %10g\n", k, (1.0 * chi_prhist[k]) / nframes);
                      }
                      else
                      {
              {
                  if (bNormalize)
                  {
 -                    fprintf(fpall, "  %10g", (1.0*chi_prhist[k])/nframes);
 +                    fprintf(fpall, "  %10g", (1.0 * chi_prhist[k]) / nframes);
                  }
                  else
                  {
      sfree(chi_prtrj);
      xvgrclose(fpall);
      fprintf(stderr, "\n");
 -
  }
  
 -void calc_distribution_props(int nh, const int histo[], real start,
 -                             int nkkk, t_karplus kkk[],
 -                             real *S2)
 +void calc_distribution_props(int nh, const int histo[], real start, int nkkk, t_karplus kkk[], real* S2)
  {
      real d, dc, ds, c1, c2, tdc, tds;
      real fac, ang, invth, Jc;
      {
          gmx_fatal(FARGS, "No points in histogram (%s, %d)", __FILE__, __LINE__);
      }
 -    fac = 2*M_PI/nh;
 +    fac = 2 * M_PI / nh;
  
      /* Compute normalisation factor */
      th = 0;
      {
          th += histo[j];
      }
 -    invth = 1.0/th;
 +    invth = 1.0 / th;
  
      for (i = 0; (i < nkkk); i++)
      {
          kkk[i].Jc    = 0;
          kkk[i].Jcsig = 0;
      }
 -    tdc = 0; tds = 0;
 +    tdc = 0;
 +    tds = 0;
      for (j = 0; (j < nh); j++)
      {
 -        d    = invth*histo[j];
 -        ang  = j*fac-start;
 -        c1   = std::cos(ang);
 -        dc   = d*c1;
 -        ds   = d*std::sin(ang);
 +        d   = invth * histo[j];
 +        ang = j * fac - start;
 +        c1  = std::cos(ang);
 +        dc  = d * c1;
 +        ds  = d * std::sin(ang);
          tdc += dc;
          tds += ds;
          for (i = 0; (i < nkkk); i++)
          {
 -            c1            = std::cos(ang+kkk[i].offset);
 -            c2            = c1*c1;
 -            Jc            = (kkk[i].A*c2 + kkk[i].B*c1 + kkk[i].C);
 -            kkk[i].Jc    += histo[j]*Jc;
 -            kkk[i].Jcsig += histo[j]*gmx::square(Jc);
 +            c1 = std::cos(ang + kkk[i].offset);
 +            c2 = c1 * c1;
 +            Jc = (kkk[i].A * c2 + kkk[i].B * c1 + kkk[i].C);
 +            kkk[i].Jc += histo[j] * Jc;
 +            kkk[i].Jcsig += histo[j] * gmx::square(Jc);
          }
      }
      for (i = 0; (i < nkkk); i++)
      {
 -        kkk[i].Jc    /= th;
 -        kkk[i].Jcsig  = std::sqrt(kkk[i].Jcsig/th-gmx::square(kkk[i].Jc));
 +        kkk[i].Jc /= th;
 +        kkk[i].Jcsig = std::sqrt(kkk[i].Jcsig / th - gmx::square(kkk[i].Jc));
      }
 -    *S2 = tdc*tdc+tds*tds;
 +    *S2 = tdc * tdc + tds * tds;
  }
  
 -static void calc_angles(struct t_pbc *pbc,
 -                        int n3, int index[], real ang[], rvec x_s[])
 +static void calc_angles(struct t_pbc* pbc, int n3, int index[], real ang[], rvec x_s[])
  {
      int  i, ix, t1, t2;
      rvec r_ij, r_kj;
  
      for (i = ix = 0; (ix < n3); i++, ix += 3)
      {
 -        ang[i] = bond_angle(x_s[index[ix]], x_s[index[ix+1]], x_s[index[ix+2]],
 -                            pbc, r_ij, r_kj, &costh, &t1, &t2);
 +        ang[i] = bond_angle(x_s[index[ix]], x_s[index[ix + 1]], x_s[index[ix + 2]], pbc, r_ij, r_kj,
 +                            &costh, &t1, &t2);
      }
      if (debug)
      {
 -        fprintf(debug, "Angle[0]=%g, costh=%g, index0 = %d, %d, %d\n",
 -                ang[0], costh, index[0], index[1], index[2]);
 +        fprintf(debug, "Angle[0]=%g, costh=%g, index0 = %d, %d, %d\n", ang[0], costh, index[0],
 +                index[1], index[2]);
          pr_rvec(debug, 0, "rij", r_ij, DIM, TRUE);
          pr_rvec(debug, 0, "rkj", r_kj, DIM, TRUE);
      }
@@@ -731,9 -720,9 +732,9 @@@ static real calc_fraction(const real an
              gauche += 1.0;
          }
      }
 -    if (trans+gauche > 0)
 +    if (trans + gauche > 0)
      {
 -        return trans/(trans+gauche);
 +        return trans / (trans + gauche);
      }
      else
      {
      }
  }
  
 -static void calc_dihs(struct t_pbc *pbc,
 -                      int n4, const int index[], real ang[], rvec x_s[])
 +static void calc_dihs(struct t_pbc* pbc, int n4, const int index[], real ang[], rvec x_s[])
  {
      int  i, ix, t1, t2, t3;
      rvec r_ij, r_kj, r_kl, m, n;
  
      for (i = ix = 0; (ix < n4); i++, ix += 4)
      {
 -        aaa = dih_angle(x_s[index[ix]], x_s[index[ix+1]], x_s[index[ix+2]],
 -                        x_s[index[ix+3]], pbc,
 -                        r_ij, r_kj, r_kl, m, n,
 -                        &t1, &t2, &t3);
 +        aaa = dih_angle(x_s[index[ix]], x_s[index[ix + 1]], x_s[index[ix + 2]], x_s[index[ix + 3]],
 +                        pbc, r_ij, r_kj, r_kl, m, n, &t1, &t2, &t3);
  
          ang[i] = aaa; /* not taking into account ryckaert bellemans yet */
      }
  }
  
 -void make_histo(FILE *log,
 -                int ndata, real data[], int npoints, int histo[],
 -                real minx, real maxx)
 +void make_histo(FILE* log, int ndata, real data[], int npoints, int histo[], real minx, real maxx)
  {
      double dx;
      int    i, ind;
          }
          fprintf(log, "Min data: %10g  Max data: %10g\n", minx, maxx);
      }
 -    dx = npoints/(maxx-minx);
 +    dx = npoints / (maxx - minx);
      if (debug)
      {
 -        fprintf(debug,
 -                "Histogramming: ndata=%d, nhisto=%d, minx=%g,maxx=%g,dx=%g\n",
 -                ndata, npoints, minx, maxx, dx);
 +        fprintf(debug, "Histogramming: ndata=%d, nhisto=%d, minx=%g,maxx=%g,dx=%g\n", ndata,
 +                npoints, minx, maxx, dx);
      }
      for (i = 0; (i < ndata); i++)
      {
 -        ind = static_cast<int>((data[i]-minx)*dx);
 +        ind = static_cast<int>((data[i] - minx) * dx);
          if ((ind >= 0) && (ind < npoints))
          {
              histo[ind]++;
@@@ -799,59 -794,53 +800,59 @@@ void normalize_histo(int npoints, cons
      d = 0;
      for (i = 0; (i < npoints); i++)
      {
 -        d += dx*histo[i];
 +        d += dx * histo[i];
      }
      if (d == 0)
      {
          fprintf(stderr, "Empty histogram!\n");
          return;
      }
 -    fac = 1.0/d;
 +    fac = 1.0 / d;
      for (i = 0; (i < npoints); i++)
      {
 -        normhisto[i] = fac*histo[i];
 +        normhisto[i] = fac * histo[i];
      }
  }
  
 -void read_ang_dih(const char *trj_fn,
 -                  gmx_bool bAngles, gmx_bool bSaveAll, gmx_bool bRb, gmx_bool bPBC,
 -                  int maxangstat, int angstat[],
 -                  int *nframes, real **time,
 -                  int isize, int index[],
 -                  real **trans_frac,
 -                  real **aver_angle,
 -                  real *dih[],
 -                  const gmx_output_env_t *oenv)
 +void read_ang_dih(const char*             trj_fn,
 +                  gmx_bool                bAngles,
 +                  gmx_bool                bSaveAll,
 +                  gmx_bool                bRb,
 +                  gmx_bool                bPBC,
 +                  int                     maxangstat,
 +                  int                     angstat[],
 +                  int*                    nframes,
 +                  real**                  time,
 +                  int                     isize,
 +                  int                     index[],
 +                  real**                  trans_frac,
 +                  real**                  aver_angle,
 +                  real*                   dih[],
 +                  const gmx_output_env_t* oenv)
  {
 -    struct t_pbc *pbc;
 -    t_trxstatus  *status;
 +    struct t_pbcpbc;
 +    t_trxstatus*  status;
      int           i, angind, total, teller;
      int           nangles, n_alloc;
-     real          t, fraction, pifac, aa, angle;
+     real          t, fraction, pifac, angle;
 -    real         *angles[2];
 +    real*         angles[2];
      matrix        box;
 -    rvec         *x;
 +    rvec*         x;
      int           cur = 0;
 -#define prev (1-cur)
 +#define prev (1 - cur)
  
      snew(pbc, 1);
      read_first_x(oenv, &status, trj_fn, &t, &x, box);
  
      if (bAngles)
      {
 -        nangles = isize/3;
 +        nangles = isize / 3;
          pifac   = M_PI;
      }
      else
      {
 -        nangles = isize/4;
 -        pifac   = 2.0*M_PI;
 +        nangles = isize / 4;
 +        pifac   = 2.0 * M_PI;
      }
      snew(angles[cur], nangles);
      snew(angles[prev], nangles);
                  {
                      if (angles[cur][i] <= 0.0)
                      {
 -                        angles[cur][i] += 2*M_PI;
 +                        angles[cur][i] += 2 * M_PI;
                      }
                  }
              }
              {
                  for (i = 0; (i < nangles); i++)
                  {
 -                    real dd = angles[cur][i];
 +                    real dd        = angles[cur][i];
                      angles[cur][i] = std::atan2(std::sin(dd), std::cos(dd));
                  }
              }
                      {
                          while (angles[cur][i] <= angles[prev][i] - M_PI)
                          {
 -                            angles[cur][i] += 2*M_PI;
 +                            angles[cur][i] += 2 * M_PI;
                          }
                          while (angles[cur][i] > angles[prev][i] + M_PI)
                          {
 -                            angles[cur][i] -= 2*M_PI;
 +                            angles[cur][i] -= 2 * M_PI;
                          }
                      }
                  }
          }
  
          /* Average angles */
-         aa = 0;
+         double aa = 0;
          for (i = 0; (i < nangles); i++)
          {
 -                real diffa = angles[cur][i] - angles[cur][i-1];
+             if (!bAngles && i > 0)
+             {
 -                angles[cur][i] = angles[cur][i-1] + diffa;
++                real diffa     = angles[cur][i] - angles[cur][i - 1];
+                 diffa          = correctRadianAngleRange(diffa);
++                angles[cur][i] = angles[cur][i - 1] + diffa;
+             }
              aa = aa + angles[cur][i];
  
              /* angle in rad / 2Pi * max determines bin. bins go from 0 to maxangstat,
              angle = angles[cur][i];
              if (!bAngles)
              {
-                 while (angle < -M_PI)
-                 {
-                     angle += 2 * M_PI;
-                 }
-                 while (angle >= M_PI)
-                 {
-                     angle -= 2 * M_PI;
-                 }
 -                angle  = correctRadianAngleRange(angle);
++                angle = correctRadianAngleRange(angle);
                  angle += M_PI;
              }
  
              /* Update the distribution histogram */
 -            angind = gmx::roundToInt((angle*maxangstat)/pifac);
 +            angind = gmx::roundToInt((angle * maxangstat) / pifac);
              if (angind == maxangstat)
              {
                  angind = 0;
              }
 -            if ( (angind < 0) || (angind >= maxangstat) )
 +            if ((angind < 0) || (angind >= maxangstat))
              {
                  /* this will never happen */
 -                gmx_fatal(FARGS, "angle (%f) index out of range (0..%d) : %d\n",
 -                          angle, maxangstat, angind);
 +                gmx_fatal(FARGS, "angle (%f) index out of range (0..%d) : %d\n", angle, maxangstat, angind);
              }
  
              angstat[angind]++;
          }
  
          /* average over all angles */
-         (*aver_angle)[teller] = (aa / nangles);
 -        aa                    = correctRadianAngleRange(aa/nangles);
++        aa                    = correctRadianAngleRange(aa / nangles);
+         (*aver_angle)[teller] = (aa);
  
          /* this copies all current dih. angles to dih[i], teller is frame */
          if (bSaveAll)
          {
              for (i = 0; i < nangles; i++)
              {
-                 dih[i][teller] = angles[cur][i];
+                 if (!bAngles)
+                 {
+                     dih[i][teller] = correctRadianAngleRange(angles[cur][i]);
+                 }
+                 else
+                 {
+                     dih[i][teller] = angles[cur][i];
+                 }
              }
          }
  
  
          /* Increment loop counter */
          teller++;
 -    }
 -    while (read_next_x(oenv, status, &t, x, box));
 +    } while (read_next_x(oenv, status, &t, x, box));
      close_trx(status);
  
      sfree(x);
index 0000000000000000000000000000000000000000,aaa687f93aa0ea5e9ff78de19736369b466d21be..3ee9a99542e339aef73bcf8ce03262fd89b3fd00
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,56 +1,56 @@@
 -        correctedAngle += 2*M_PI;
+ /*
+  * This file is part of the GROMACS molecular simulation package.
+  *
+  * Copyright (c) 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 "angle_correction.h"
+ #include <algorithm>
+ #include "gromacs/math/units.h"
+ #include "gromacs/math/vec.h"
+ real correctRadianAngleRange(const real angle)
+ {
+     real correctedAngle = angle;
+     while (correctedAngle < -M_PI)
+     {
 -        correctedAngle -= 2*M_PI;
++        correctedAngle += 2 * M_PI;
+     }
+     while (correctedAngle >= M_PI)
+     {
++        correctedAngle -= 2 * M_PI;
+     }
+     return correctedAngle;
+ }
index cde327414c01eff198371fcab431a11d423d2111,b695f21f68df40cf3ebf0c7f0e7868ab3fab27a6..07368d9d41b2c5ddff94daa131bd7edcd54e5d96
@@@ -47,9 -47,9 +47,9 @@@
  #include "gromacs/utility/futil.h"
  #include "gromacs/utility/smalloc.h"
  
 -t_mat *init_mat(int n1, gmx_bool b1D)
 +t_matinit_mat(int n1, gmx_bool b1D)
  {
 -    t_mat *m;
 +    t_matm;
  
      snew(m, 1);
      m->n1     = n1;
@@@ -67,7 -67,7 +67,7 @@@
      return m;
  }
  
 -void copy_t_mat(t_mat *dst, t_mat *src)
 +void copy_t_mat(t_mat* dst, t_mat* src)
  {
      int i, j;
  
      }
  }
  
 -void enlarge_mat(t_mat *m, int deltan)
 +void enlarge_mat(t_matm, int deltan)
  {
      int i, j;
  
 -    srenew(m->erow, m->nn+deltan);
 -    srenew(m->m_ind, m->nn+deltan);
 -    srenew(m->mat, m->nn+deltan);
 +    srenew(m->erow, m->nn + deltan);
 +    srenew(m->m_ind, m->nn + deltan);
 +    srenew(m->mat, m->nn + deltan);
  
      /* Reallocate existing rows in the matrix, and set them to zero */
      for (i = 0; (i < m->nn); i++)
      {
 -        srenew(m->mat[i], m->nn+deltan);
 -        for (j = m->nn; (j < m->nn+deltan); j++)
 +        srenew(m->mat[i], m->nn + deltan);
 +        for (j = m->nn; (j < m->nn + deltan); j++)
          {
              m->mat[i][j] = 0;
          }
      }
      /* Allocate new rows of the matrix, set energies to zero */
 -    for (i = m->nn; (i < m->nn+deltan); i++)
 +    for (i = m->nn; (i < m->nn + deltan); i++)
      {
 -        m->erow[i]  = 0;
 -        snew(m->mat[i], m->nn+deltan);
 +        m->erow[i] = 0;
 +        snew(m->mat[i], m->nn + deltan);
      }
      m->nn += deltan;
  }
  
 -void reset_index(t_mat *m)
 +void reset_index(t_matm)
  {
      int i;
  
      }
  }
  
 -void set_mat_entry(t_mat *m, int i, int j, real val)
 +void set_mat_entry(t_matm, int i, int j, real val)
  {
      m->mat[i][j] = m->mat[j][i] = val;
 -    m->maxrms    = std::max(m->maxrms, val);
 +    m->maxrms                   = std::max(m->maxrms, val);
      if (j != i)
      {
 -        m->minrms  = std::min(m->minrms, val);
 +        m->minrms = std::min(m->minrms, val);
      }
 -    m->sumrms   += val;
 -    m->nn        = std::max(m->nn, std::max(j+1, i+1));
 +    m->sumrms += val;
 +    m->nn = std::max(m->nn, std::max(j + 1, i + 1));
  }
  
 -void done_mat(t_mat **m)
 +void done_mat(t_mat** m)
  {
      done_matrix((*m)->n1, &((*m)->mat));
      sfree((*m)->m_ind);
      *m = nullptr;
  }
  
 -real mat_energy(t_mat *m)
 +real mat_energy(t_matm)
  {
      int  j;
      real emat = 0;
  
 -    for (j = 0; (j < m->nn-1); j++)
 +    for (j = 0; (j < m->nn - 1); j++)
      {
 -        emat += gmx::square(m->mat[j][j+1]);
 +        emat += gmx::square(m->mat[j][j + 1]);
      }
      return emat;
  }
  
 -void swap_rows(t_mat *m, int iswap, int jswap)
 +void swap_rows(t_matm, int iswap, int jswap)
  {
      real *tmp, ttt;
      int   i, itmp;
      }
  }
  
 -void swap_mat(t_mat *m)
 +void swap_mat(t_matm)
  {
 -    t_mat *tmp;
 +    t_mattmp;
      int    i, j;
  
      tmp = init_mat(m->nn, FALSE);
      done_mat(&tmp);
  }
  
 -void low_rmsd_dist(const char *fn, real maxrms, int nn, real **mat,
 -                   const gmx_output_env_t *oenv)
 +void low_rmsd_dist(const char* fn, real maxrms, int nn, real** mat, const gmx_output_env_t* oenv)
  {
 -    FILE   *fp;
 -    int     i, j, *histo, x;
 -    real    fac;
 +    FILEfp;
 +    int   i, j, *histo, x;
 +    real  fac;
  
 -    fac = 100/maxrms;
 +    fac = 100 / maxrms;
      snew(histo, 101);
      for (i = 0; i < nn; i++)
      {
 -        for (j = i+1; j < nn; j++)
 +        for (j = i + 1; j < nn; j++)
          {
 -            x = gmx::roundToInt(fac*mat[i][j]);
 +            x = gmx::roundToInt(fac * mat[i][j]);
              if (x <= 100)
              {
                  histo[x]++;
          }
      }
  
-     fp = xvgropen(fn, "RMS Distribution", "RMS (nm)", "a.u.", oenv);
+     fp = xvgropen(fn, "RMS Distribution", "RMS (nm)", "counts", oenv);
      for (i = 0; (i < 101); i++)
      {
 -        fprintf(fp, "%10g  %10d\n", i/fac, histo[i]);
 +        fprintf(fp, "%10g  %10d\n", i / fac, histo[i]);
      }
      xvgrclose(fp);
      sfree(histo);
  }
  
 -void rmsd_distribution(const char *fn, t_mat *rms, const gmx_output_env_t *oenv)
 +void rmsd_distribution(const char* fn, t_mat* rms, const gmx_output_env_t* oenv)
  {
      low_rmsd_dist(fn, rms->maxrms, rms->nn, rms->mat, oenv);
  }
  
 -t_clustid *new_clustid(int n1)
 +t_clustidnew_clustid(int n1)
  {
 -    t_clustid *c;
 +    t_clustidc;
      int        i;
  
      snew(c, n1);
index 0a92c634d86bb4a2eb91a7e44024e3d772db76c5,b8f5da3b7a7bfb6f90666c2d2a34d0408601a2a9..e99bee5628e958d5ed9574e09c120adb03d3d753
@@@ -46,6 -46,7 +46,7 @@@
  #include "gromacs/correlationfunctions/autocorr.h"
  #include "gromacs/fileio/trrio.h"
  #include "gromacs/fileio/xvgr.h"
+ #include "gromacs/gmxana/angle_correction.h"
  #include "gromacs/gmxana/gmx_ana.h"
  #include "gromacs/gmxana/gstat.h"
  #include "gromacs/math/functions.h"
  #include "gromacs/utility/pleasecite.h"
  #include "gromacs/utility/smalloc.h"
  
 -static void dump_dih_trr(int nframes, int nangles, real **dih, const char *fn,
 -                         real *time)
 +static void dump_dih_trr(int nframes, int nangles, real** dih, const char* fn, real* time)
  {
      int              i, j, k, l, m, na;
 -    struct t_fileio *fio;
 -    rvec            *x;
 -    matrix           box = {{2, 0, 0}, {0, 2, 0}, {0, 0, 2}};
 +    struct t_fileiofio;
 +    rvec*            x;
 +    matrix           box = { { 2, 0, 0 }, { 0, 2, 0 }, { 0, 0, 2 } };
  
 -    na = (nangles*2);
 +    na = (nangles * 2);
      if ((na % 3) != 0)
      {
 -        na = 1+na/3;
 +        na = 1 + na / 3;
      }
      else
      {
 -        na = na/3;
 +        na = na / 3;
      }
 -    printf("There are %d dihedrals. Will fill %d atom positions with cos/sin\n",
 -           nangles, na);
 +    printf("There are %d dihedrals. Will fill %d atom positions with cos/sin\n", nangles, na);
      snew(x, na);
      fio = gmx_trr_open(fn, "w");
      for (i = 0; (i < nframes); i++)
      sfree(x);
  }
  
 -int gmx_g_angle(int argc, char *argv[])
 +int gmx_g_angle(int argc, charargv[])
  {
 -    static const char *desc[] = {
 +    static const chardesc[] = {
          "[THISMODULE] computes the angle distribution for a number of angles",
          "or dihedrals.[PAR]",
          "With option [TT]-ov[tt], you can plot the average angle of",
          "records a histogram of the times between such transitions,",
          "assuming the input trajectory frames are equally spaced in time."
      };
 -    static const char *opt[]    = { nullptr, "angle", "dihedral", "improper", "ryckaert-bellemans", nullptr };
 -    static gmx_bool    bALL     = FALSE, bChandler = FALSE, bAverCorr = FALSE, bPBC = TRUE;
 +    static const char* opt[] = { nullptr, "angle", "dihedral", "improper", "ryckaert-bellemans",
 +                                 nullptr };
 +    static gmx_bool    bALL = FALSE, bChandler = FALSE, bAverCorr = FALSE, bPBC = TRUE;
      static real        binwidth = 1;
      t_pargs            pa[]     = {
 -        { "-type", FALSE, etENUM, {opt},
 -          "Type of angle to analyse" },
 -        { "-all",    FALSE,  etBOOL, {&bALL},
 -          "Plot all angles separately in the averages file, in the order of appearance in the index file." },
 -        { "-binwidth", FALSE, etREAL, {&binwidth},
 +        { "-type", FALSE, etENUM, { opt }, "Type of angle to analyse" },
 +        { "-all",
 +          FALSE,
 +          etBOOL,
 +          { &bALL },
 +          "Plot all angles separately in the averages file, in the order of appearance in the "
 +          "index file." },
 +        { "-binwidth",
 +          FALSE,
 +          etREAL,
 +          { &binwidth },
            "binwidth (degrees) for calculating the distribution" },
 -        { "-periodic", FALSE, etBOOL, {&bPBC},
 -          "Print dihedral angles modulo 360 degrees" },
 -        { "-chandler", FALSE,  etBOOL, {&bChandler},
 -          "Use Chandler correlation function (N[trans] = 1, N[gauche] = 0) rather than cosine correlation function. Trans is defined as phi < -60 or phi > 60." },
 -        { "-avercorr", FALSE,  etBOOL, {&bAverCorr},
 +        { "-periodic", FALSE, etBOOL, { &bPBC }, "Print dihedral angles modulo 360 degrees" },
 +        { "-chandler",
 +          FALSE,
 +          etBOOL,
 +          { &bChandler },
 +          "Use Chandler correlation function (N[trans] = 1, N[gauche] = 0) rather than cosine "
 +          "correlation function. Trans is defined as phi < -60 or phi > 60." },
 +        { "-avercorr",
 +          FALSE,
 +          etBOOL,
 +          { &bAverCorr },
            "Average the correlation functions for the individual angles/dihedrals" }
      };
 -    static const char *bugs[] = {
 +    static const charbugs[] = {
          "Counting transitions only works for dihedrals with multiplicity 3"
      };
  
 -    FILE              *out;
 -    real               dt;
 -    int                isize;
 -    int               *index;
 -    char              *grpname;
 -    real               maxang, S2, norm_fac, maxstat;
 -    unsigned long      mode;
 -    int                nframes, maxangstat, mult, *angstat;
 -    int                i, j, nangles, first, last;
 -    gmx_bool           bAver, bRb, bPeriodic,
 -                       bFrac,               /* calculate fraction too?  */
 -                       bTrans,              /* worry about transtions too? */
 -                       bCorr;               /* correlation function ? */
 -    double             tfrac = 0;
 -    char               title[256];
 -    real             **dih = nullptr; /* mega array with all dih. angles at all times*/
 -    real              *time, *trans_frac, *aver_angle;
 -    t_filenm           fnm[] = {
 -        { efTRX, "-f", nullptr,  ffREAD  },
 -        { efNDX, nullptr, "angle",  ffREAD  },
 -        { efXVG, "-od", "angdist",  ffWRITE },
 -        { efXVG, "-ov", "angaver",  ffOPTWR },
 -        { efXVG, "-of", "dihfrac",  ffOPTWR },
 -        { efXVG, "-ot", "dihtrans", ffOPTWR },
 -        { efXVG, "-oh", "trhisto",  ffOPTWR },
 -        { efXVG, "-oc", "dihcorr",  ffOPTWR },
 -        { efTRR, "-or", nullptr,       ffOPTWR }
 -    };
 +    FILE*         out;
 +    real          dt;
 +    int           isize;
 +    int*          index;
 +    char*         grpname;
 +    real          maxang, S2, norm_fac, maxstat;
 +    unsigned long mode;
 +    int           nframes, maxangstat, mult, *angstat;
 +    int           i, j, nangles, first, last;
 +    gmx_bool      bAver, bRb, bPeriodic, bFrac, /* calculate fraction too?  */
 +            bTrans,                             /* worry about transtions too? */
 +            bCorr;                              /* correlation function ? */
-     real     aver, aver2, aversig;              /* fraction trans dihedrals */
 +    double   tfrac = 0;
 +    char     title[256];
 +    real**   dih = nullptr; /* mega array with all dih. angles at all times*/
 +    real *   time, *trans_frac, *aver_angle;
 +    t_filenm fnm[] = { { efTRX, "-f", nullptr, ffREAD },     { efNDX, nullptr, "angle", ffREAD },
 +                       { efXVG, "-od", "angdist", ffWRITE }, { efXVG, "-ov", "angaver", ffOPTWR },
 +                       { efXVG, "-of", "dihfrac", ffOPTWR }, { efXVG, "-ot", "dihtrans", ffOPTWR },
 +                       { efXVG, "-oh", "trhisto", ffOPTWR }, { efXVG, "-oc", "dihcorr", ffOPTWR },
 +                       { efTRR, "-or", nullptr, ffOPTWR } };
  #define NFILE asize(fnm)
 -    int                npargs;
 -    t_pargs           *ppa;
 -    gmx_output_env_t  *oenv;
 +    int               npargs;
 +    t_pargs*          ppa;
 +    gmx_output_env_toenv;
  
      npargs = asize(pa);
      ppa    = add_acf_pargs(&npargs, pa);
 -    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME,
 -                           NFILE, fnm, npargs, ppa, asize(desc), desc, asize(bugs), bugs,
 -                           &oenv))
 +    if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME, NFILE, fnm, npargs, ppa,
 +                           asize(desc), desc, asize(bugs), bugs, &oenv))
      {
          sfree(ppa);
          return 0;
      maxang = 360.0;
      bRb    = FALSE;
  
 -    GMX_RELEASE_ASSERT(opt[0] != nullptr, "Internal option inconsistency; opt[0]==NULL after processing");
 +    GMX_RELEASE_ASSERT(opt[0] != nullptr,
 +                       "Internal option inconsistency; opt[0]==NULL after processing");
  
      switch (opt[0][0])
      {
              mult   = 3;
              maxang = 180.0;
              break;
 -        case 'd':
 -            break;
 -        case 'i':
 -            break;
 -        case 'r':
 -            bRb = TRUE;
 -            break;
 +        case 'd': break;
 +        case 'i': break;
 +        case 'r': bRb = TRUE; break;
      }
  
      if (opt2bSet("-or", NFILE, fnm))
      }
  
      /* Calculate bin size */
 -    maxangstat = gmx::roundToInt(maxang/binwidth);
 -    binwidth   = maxang/maxangstat;
 +    maxangstat = gmx::roundToInt(maxang / binwidth);
 +    binwidth   = maxang / maxangstat;
  
      rd_index(ftp2fn(efNDX, NFILE, fnm), 1, &isize, &index, &grpname);
 -    nangles = isize/mult;
 +    nangles = isize / mult;
      if ((isize % mult) != 0)
      {
 -        gmx_fatal(FARGS, "number of index elements not multiple of %d, "
 +        gmx_fatal(FARGS,
 +                  "number of index elements not multiple of %d, "
                    "these can not be %s\n",
                    mult, (mult == 3) ? "angle triplets" : "dihedral quadruplets");
      }
  
      if (bFrac && !bRb)
      {
 -        fprintf(stderr, "Warning:"
 +        fprintf(stderr,
 +                "Warning:"
                  " calculating fractions as defined in this program\n"
                  "makes sense for Ryckaert Bellemans dihs. only. Ignoring -of\n\n");
          bFrac = FALSE;
      }
  
 -    if ( (bTrans || bFrac || bCorr) && mult == 3)
 +    if ((bTrans || bFrac || bCorr) && mult == 3)
      {
 -        gmx_fatal(FARGS, "Can only do transition, fraction or correlation\n"
 +        gmx_fatal(FARGS,
 +                  "Can only do transition, fraction or correlation\n"
                    "on dihedrals. Select -d\n");
      }
  
       * We need to know the nr of frames so we can allocate memory for an array
       * with all dihedral angles at all timesteps. Works for me.
       */
 -    if (bTrans || bCorr  || bALL || opt2bSet("-or", NFILE, fnm))
 +    if (bTrans || bCorr || bALL || opt2bSet("-or", NFILE, fnm))
      {
          snew(dih, nangles);
      }
      snew(angstat, maxangstat);
  
      read_ang_dih(ftp2fn(efTRX, NFILE, fnm), (mult == 3),
 -                 bALL || bCorr || bTrans || opt2bSet("-or", NFILE, fnm),
 -                 bRb, bPBC, maxangstat, angstat,
 -                 &nframes, &time, isize, index, &trans_frac, &aver_angle, dih,
 -                 oenv);
 +                 bALL || bCorr || bTrans || opt2bSet("-or", NFILE, fnm), bRb, bPBC, maxangstat,
 +                 angstat, &nframes, &time, isize, index, &trans_frac, &aver_angle, dih, oenv);
  
 -    dt = (time[nframes-1]-time[0])/(nframes-1);
 +    dt = (time[nframes - 1] - time[0]) / (nframes - 1);
  
      if (bAver)
      {
          sprintf(title, "Average Angle: %s", grpname);
 -        out = xvgropen(opt2fn("-ov", NFILE, fnm),
 -                       title, "Time (ps)", "Angle (degrees)", oenv);
 +        out = xvgropen(opt2fn("-ov", NFILE, fnm), title, "Time (ps)", "Angle (degrees)", oenv);
          for (i = 0; (i < nframes); i++)
          {
 -            fprintf(out, "%10.5f  %8.3f", time[i], aver_angle[i]*RAD2DEG);
 +            fprintf(out, "%10.5f  %8.3f", time[i], aver_angle[i] * RAD2DEG);
              if (bALL)
              {
                  for (j = 0; (j < nangles); j++)
                      if (bPBC)
                      {
                          real dd = dih[j][i];
 -                        fprintf(out, "  %8.3f", std::atan2(std::sin(dd), std::cos(dd))*RAD2DEG);
 +                        fprintf(out, "  %8.3f", std::atan2(std::sin(dd), std::cos(dd)) * RAD2DEG);
                      }
                      else
                      {
 -                        fprintf(out, "  %8.3f", dih[j][i]*RAD2DEG);
 +                        fprintf(out, "  %8.3f", dih[j][i] * RAD2DEG);
                      }
                  }
              }
      if (bFrac)
      {
          sprintf(title, "Trans fraction: %s", grpname);
 -        out = xvgropen(opt2fn("-of", NFILE, fnm),
 -                       title, "Time (ps)", "Fraction", oenv);
 +        out   = xvgropen(opt2fn("-of", NFILE, fnm), title, "Time (ps)", "Fraction", oenv);
          tfrac = 0.0;
          for (i = 0; (i < nframes); i++)
          {
  
      if (bTrans)
      {
 -        ana_dih_trans(opt2fn("-ot", NFILE, fnm), opt2fn("-oh", NFILE, fnm),
 -                      dih, nframes, nangles, grpname, time, bRb, oenv);
 +        ana_dih_trans(opt2fn("-ot", NFILE, fnm), opt2fn("-oh", NFILE, fnm), dih, nframes, nangles,
 +                      grpname, time, bRb, oenv);
      }
  
      if (bCorr)
  
              if (bChandler)
              {
 -                real     dval, sixty = DEG2RAD*60;
 +                real     dval, sixty = DEG2RAD * 60;
                  gmx_bool bTest;
  
                  for (i = 0; (i < nangles); i++)
                          }
                          if (bTest)
                          {
 -                            dih[i][j] = dval-tfrac;
 +                            dih[i][j] = dval - tfrac;
                          }
                          else
                          {
              {
                  mode = eacCos;
              }
 -            do_autocorr(opt2fn("-oc", NFILE, fnm), oenv,
 -                        "Dihedral Autocorrelation Function",
 +            do_autocorr(opt2fn("-oc", NFILE, fnm), oenv, "Dihedral Autocorrelation Function",
                          nframes, nangles, dih, dt, mode, bAverCorr);
          }
      }
  
  
      /* Determine the non-zero part of the distribution */
 -    for (first = 0; (first < maxangstat-1) && (angstat[first+1] == 0); first++)
 -    {
 -        ;
 -    }
 -    for (last = maxangstat-1; (last > 0) && (angstat[last-1] == 0); last--)
 -    {
 -        ;
 -    }
 +    for (first = 0; (first < maxangstat - 1) && (angstat[first + 1] == 0); first++) {}
 +    for (last = maxangstat - 1; (last > 0) && (angstat[last - 1] == 0); last--) {}
  
-     aver = aver2 = 0;
-     for (i = 0; (i < nframes); i++)
-     {
-         aver += RAD2DEG * aver_angle[i];
-         aver2 += gmx::square(RAD2DEG * aver_angle[i]);
 -    double aver  = 0;
 -    printf("Found points in the range from %d to %d (max %d)\n",
 -           first, last, maxangstat);
 -    if (bTrans || bCorr  || bALL || opt2bSet("-or", NFILE, fnm))
 -    {   /* It's better to re-calculate Std. Dev per sample */
++    double aver = 0;
++    printf("Found points in the range from %d to %d (max %d)\n", first, last, maxangstat);
++    if (bTrans || bCorr || bALL || opt2bSet("-or", NFILE, fnm))
++    { /* It's better to re-calculate Std. Dev per sample */
+         real b_aver = aver_angle[0];
+         real b      = dih[0][0];
+         real delta;
+         for (int i = 0; (i < nframes); i++)
+         {
 -            delta   = correctRadianAngleRange(aver_angle[i] - b_aver);
++            delta = correctRadianAngleRange(aver_angle[i] - b_aver);
+             b_aver += delta;
 -            aver   += b_aver;
++            aver += b_aver;
+             for (int j = 0; (j < nangles); j++)
+             {
 -                delta  = correctRadianAngleRange(dih[j][i] - b);
 -                b     += delta;
++                delta = correctRadianAngleRange(dih[j][i] - b);
++                b += delta;
+             }
+         }
+     }
+     else
 -    {   /* Incorrect  for Std. Dev. */
++    { /* Incorrect  for Std. Dev. */
+         real delta, b_aver = aver_angle[0];
+         for (i = 0; (i < nframes); i++)
+         {
 -            delta   = correctRadianAngleRange(aver_angle[i] - b_aver);
++            delta = correctRadianAngleRange(aver_angle[i] - b_aver);
+             b_aver += delta;
 -            aver   += b_aver;
++            aver += b_aver;
+         }
      }
 -    aver  /= nframes;
 +    aver /= nframes;
-     aver2 /= nframes;
-     aversig = std::sqrt(aver2 - gmx::square(aver));
-     printf("Found points in the range from %d to %d (max %d)\n", first, last, maxangstat);
-     printf(" < angle >  = %g\n", aver);
-     printf("< angle^2 > = %g\n", aver2);
-     printf("Std. Dev.   = %g\n", aversig);
+     double aversig = correctRadianAngleRange(aver);
+     aversig *= RAD2DEG;
 -    aver    *= RAD2DEG;
++    aver *= RAD2DEG;
+     printf(" < angle >  = %g\n", aversig);
  
      if (mult == 3)
      {
          fprintf(stderr, "Order parameter S^2 = %g\n", S2);
      }
  
 -    bPeriodic = (mult == 4) && (first == 0) && (last == maxangstat-1);
 +    bPeriodic = (mult == 4) && (first == 0) && (last == maxangstat - 1);
  
      out = xvgropen(opt2fn("-od", NFILE, fnm), title, "Degrees", "", oenv);
      if (output_env_get_print_xvgr_codes(oenv))
      {
          fprintf(out, "@    subtitle \"average angle: %g\\So\\N\"\n", aver);
      }
 -    norm_fac = 1.0/(nangles*nframes*binwidth);
 +    norm_fac = 1.0 / (nangles * nframes * binwidth);
      if (bPeriodic)
      {
          maxstat = 0;
          for (i = first; (i <= last); i++)
          {
 -            maxstat = std::max(maxstat, angstat[i]*norm_fac);
 +            maxstat = std::max(maxstat, angstat[i] * norm_fac);
          }
          if (output_env_get_print_xvgr_codes(oenv))
          {
              fprintf(out, "@    world xmin -180\n");
              fprintf(out, "@    world xmax  180\n");
              fprintf(out, "@    world ymin 0\n");
 -            fprintf(out, "@    world ymax %g\n", maxstat*1.05);
 +            fprintf(out, "@    world ymax %g\n", maxstat * 1.05);
              fprintf(out, "@    xaxis  tick major 60\n");
              fprintf(out, "@    xaxis  tick minor 30\n");
              fprintf(out, "@    yaxis  tick major 0.005\n");
      }
      for (i = first; (i <= last); i++)
      {
 -        fprintf(out, "%10g  %10f\n", i*binwidth+180.0-maxang, angstat[i]*norm_fac);
 +        fprintf(out, "%10g  %10f\n", i * binwidth + 180.0 - maxang, angstat[i] * norm_fac);
      }
      if (bPeriodic)
      {
          /* print first bin again as last one */
 -        fprintf(out, "%10g  %10f\n", 180.0, angstat[0]*norm_fac);
 +        fprintf(out, "%10g  %10f\n", 180.0, angstat[0] * norm_fac);
      }
  
      xvgrclose(out);
index b702a4a2552b91a025493e0720474fe4fc578451,e3fad65f76c6f4439e1bf49ce45c9d84f9d66829..c8a42aac5896ea3b95e02b2d792eb07133607c95
@@@ -3,7 -3,7 +3,7 @@@
   *
   * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
   * Copyright (c) 2001-2004, The GROMACS development team.
 - * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
 + * Copyright (c) 2013-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.
  #include <cstring>
  
  #include <algorithm>
 +#include <memory>
  
  #include "gromacs/commandline/filenm.h"
 -#include "gromacs/compat/make_unique.h"
  #include "gromacs/domdec/domdec.h"
  #include "gromacs/domdec/domdec_struct.h"
  #include "gromacs/ewald/ewald.h"
 -#include "gromacs/ewald/ewald-utils.h"
 +#include "gromacs/ewald/ewald_utils.h"
 +#include "gromacs/ewald/pme_pp_comm_gpu.h"
  #include "gromacs/fileio/filetypes.h"
  #include "gromacs/gmxlib/network.h"
  #include "gromacs/gmxlib/nonbonded/nonbonded.h"
  #include "gromacs/gpu_utils/gpu_utils.h"
  #include "gromacs/hardware/hw_info.h"
 -#include "gromacs/listed-forces/gpubonded.h"
 -#include "gromacs/listed-forces/manage-threading.h"
 -#include "gromacs/listed-forces/pairs.h"
 +#include "gromacs/listed_forces/gpubonded.h"
 +#include "gromacs/listed_forces/manage_threading.h"
 +#include "gromacs/listed_forces/pairs.h"
  #include "gromacs/math/functions.h"
  #include "gromacs/math/units.h"
  #include "gromacs/math/utilities.h"
  #include "gromacs/math/vec.h"
 +#include "gromacs/mdlib/dispersioncorrection.h"
  #include "gromacs/mdlib/force.h"
 -#include "gromacs/mdlib/forcerec-threading.h"
 +#include "gromacs/mdlib/forcerec_threading.h"
  #include "gromacs/mdlib/gmx_omp_nthreads.h"
  #include "gromacs/mdlib/md_support.h"
 -#include "gromacs/mdlib/nb_verlet.h"
 -#include "gromacs/mdlib/nbnxn_atomdata.h"
 -#include "gromacs/mdlib/nbnxn_gpu_data_mgmt.h"
 -#include "gromacs/mdlib/nbnxn_grid.h"
 -#include "gromacs/mdlib/nbnxn_internal.h"
 -#include "gromacs/mdlib/nbnxn_search.h"
 -#include "gromacs/mdlib/nbnxn_simd.h"
 -#include "gromacs/mdlib/nbnxn_tuning.h"
 -#include "gromacs/mdlib/nbnxn_util.h"
 -#include "gromacs/mdlib/ns.h"
  #include "gromacs/mdlib/qmmm.h"
  #include "gromacs/mdlib/rf_util.h"
 -#include "gromacs/mdlib/sim_util.h"
  #include "gromacs/mdlib/wall.h"
  #include "gromacs/mdtypes/commrec.h"
  #include "gromacs/mdtypes/fcdata.h"
  #include "gromacs/mdtypes/iforceprovider.h"
  #include "gromacs/mdtypes/inputrec.h"
  #include "gromacs/mdtypes/md_enums.h"
 +#include "gromacs/nbnxm/gpu_data_mgmt.h"
 +#include "gromacs/nbnxm/nbnxm.h"
 +#include "gromacs/nbnxm/nbnxm_geometry.h"
  #include "gromacs/pbcutil/ishift.h"
  #include "gromacs/pbcutil/pbc.h"
 -#include "gromacs/simd/simd.h"
  #include "gromacs/tables/forcetable.h"
  #include "gromacs/topology/mtop_util.h"
  #include "gromacs/trajectory/trajectoryframe.h"
  #include "gromacs/utility/smalloc.h"
  #include "gromacs/utility/strconvert.h"
  
 -#include "nbnxn_gpu_jit_support.h"
 +/*! \brief environment variable to enable GPU P2P communication */
 +static const bool c_enableGpuPmePpComms =
 +        (getenv("GMX_GPU_PME_PP_COMMS") != nullptr) && GMX_THREAD_MPI && (GMX_GPU == GMX_GPU_CUDA);
  
 -t_forcerec *mk_forcerec()
 +static real* mk_nbfp(const gmx_ffparams_t* idef, gmx_bool bBHAM)
  {
 -    t_forcerec *fr;
 -
 -    snew(fr, 1);
 -
 -    return fr;
 -}
 -
 -static real *mk_nbfp(const gmx_ffparams_t *idef, gmx_bool bBHAM)
 -{
 -    real *nbfp;
 +    real* nbfp;
      int   i, j, k, atnr;
  
      atnr = idef->atnr;
      if (bBHAM)
      {
 -        snew(nbfp, 3*atnr*atnr);
 +        snew(nbfp, 3 * atnr * atnr);
          for (i = k = 0; (i < atnr); i++)
          {
              for (j = 0; (j < atnr); j++, k++)
                  BHAMA(nbfp, atnr, i, j) = idef->iparams[k].bham.a;
                  BHAMB(nbfp, atnr, i, j) = idef->iparams[k].bham.b;
                  /* nbfp now includes the 6.0 derivative prefactor */
 -                BHAMC(nbfp, atnr, i, j) = idef->iparams[k].bham.c*6.0;
 +                BHAMC(nbfp, atnr, i, j) = idef->iparams[k].bham.c * 6.0;
              }
          }
      }
      else
      {
 -        snew(nbfp, 2*atnr*atnr);
 +        snew(nbfp, 2 * atnr * atnr);
          for (i = k = 0; (i < atnr); i++)
          {
              for (j = 0; (j < atnr); j++, k++)
              {
                  /* nbfp now includes the 6.0/12.0 derivative prefactors */
 -                C6(nbfp, atnr, i, j)   = idef->iparams[k].lj.c6*6.0;
 -                C12(nbfp, atnr, i, j)  = idef->iparams[k].lj.c12*12.0;
 +                C6(nbfp, atnr, i, j)  = idef->iparams[k].lj.c6 * 6.0;
 +                C12(nbfp, atnr, i, j) = idef->iparams[k].lj.c12 * 12.0;
              }
          }
      }
      return nbfp;
  }
  
 -static real *make_ljpme_c6grid(const gmx_ffparams_t *idef, t_forcerec *fr)
 +static real* make_ljpme_c6grid(const gmx_ffparams_t* idef, t_forcerec* fr)
  {
 -    int        i, j, k, atnr;
 -    real       c6, c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
 -    real      *grid;
 +    int   i, j, k, atnr;
 +    real  c6, c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
 +    realgrid;
  
      /* For LJ-PME simulations, we correct the energies with the reciprocal space
       * inside of the cut-off. To do this the non-bonded kernels needs to have
       */
  
      atnr = idef->atnr;
 -    snew(grid, 2*atnr*atnr);
 +    snew(grid, 2 * atnr * atnr);
      for (i = k = 0; (i < atnr); i++)
      {
          for (j = 0; (j < atnr); j++, k++)
          {
 -            c6i  = idef->iparams[i*(atnr+1)].lj.c6;
 -            c12i = idef->iparams[i*(atnr+1)].lj.c12;
 -            c6j  = idef->iparams[j*(atnr+1)].lj.c6;
 -            c12j = idef->iparams[j*(atnr+1)].lj.c12;
 +            c6i  = idef->iparams[i * (atnr + 1)].lj.c6;
 +            c12i = idef->iparams[i * (atnr + 1)].lj.c12;
 +            c6j  = idef->iparams[j * (atnr + 1)].lj.c6;
 +            c12j = idef->iparams[j * (atnr + 1)].lj.c12;
              c6   = std::sqrt(c6i * c6j);
 -            if (fr->ljpme_combination_rule == eljpmeLB
 -                && !gmx_numzero(c6) && !gmx_numzero(c12i) && !gmx_numzero(c12j))
 +            if (fr->ljpme_combination_rule == eljpmeLB && !gmx_numzero(c6) && !gmx_numzero(c12i)
 +                && !gmx_numzero(c12j))
              {
                  sigmai = gmx::sixthroot(c12i / c6i);
                  sigmaj = gmx::sixthroot(c12j / c6j);
                  epsi   = c6i * c6i / c12i;
                  epsj   = c6j * c6j / c12j;
 -                c6     = std::sqrt(epsi * epsj) * gmx::power6(0.5*(sigmai+sigmaj));
 +                c6     = std::sqrt(epsi * epsj) * gmx::power6(0.5 * (sigmai + sigmaj));
              }
              /* Store the elements at the same relative positions as C6 in nbfp in order
               * to simplify access in the kernels
               */
 -            grid[2*(atnr*i+j)] = c6*6.0;
 +            grid[2 * (atnr * i + j)] = c6 * 6.0;
          }
      }
      return grid;
  }
  
 -static real *mk_nbfp_combination_rule(const gmx_ffparams_t *idef, int comb_rule)
 -{
 -    real      *nbfp;
 -    int        i, j, atnr;
 -    real       c6i, c6j, c12i, c12j, epsi, epsj, sigmai, sigmaj;
 -    real       c6, c12;
 -
 -    atnr = idef->atnr;
 -    snew(nbfp, 2*atnr*atnr);
 -    for (i = 0; i < atnr; ++i)
 -    {
 -        for (j = 0; j < atnr; ++j)
 -        {
 -            c6i  = idef->iparams[i*(atnr+1)].lj.c6;
 -            c12i = idef->iparams[i*(atnr+1)].lj.c12;
 -            c6j  = idef->iparams[j*(atnr+1)].lj.c6;
 -            c12j = idef->iparams[j*(atnr+1)].lj.c12;
 -            c6   = std::sqrt(c6i  * c6j);
 -            c12  = std::sqrt(c12i * c12j);
 -            if (comb_rule == eCOMB_ARITHMETIC
 -                && !gmx_numzero(c6) && !gmx_numzero(c12))
 -            {
 -                sigmai = gmx::sixthroot(c12i / c6i);
 -                sigmaj = gmx::sixthroot(c12j / c6j);
 -                epsi   = c6i * c6i / c12i;
 -                epsj   = c6j * c6j / c12j;
 -                c6     = std::sqrt(epsi * epsj) * gmx::power6(0.5*(sigmai+sigmaj));
 -                c12    = std::sqrt(epsi * epsj) * gmx::power12(0.5*(sigmai+sigmaj));
 -            }
 -            C6(nbfp, atnr, i, j)   = c6*6.0;
 -            C12(nbfp, atnr, i, j)  = c12*12.0;
 -        }
 -    }
 -    return nbfp;
 -}
 -
 -/* This routine sets fr->solvent_opt to the most common solvent in the
 - * system, e.g. esolSPC or esolTIP4P. It will also mark each charge group in
 - * the fr->solvent_type array with the correct type (or esolNO).
 - *
 - * Charge groups that fulfill the conditions but are not identical to the
 - * most common one will be marked as esolNO in the solvent_type array.
 - *
 - * TIP3p is identical to SPC for these purposes, so we call it
 - * SPC in the arrays (Apologies to Bill Jorgensen ;-)
 - *
 - * NOTE: QM particle should not
 - * become an optimized solvent. Not even if there is only one charge
 - * group in the Qm
 - */
 -
 -typedef struct
 -{
 -    int    model;
 -    int    count;
 -    int    vdwtype[4];
 -    real   charge[4];
 -} solvent_parameters_t;
 -
 -static void
 -check_solvent_cg(const gmx_moltype_t    *molt,
 -                 int                     cg0,
 -                 int                     nmol,
 -                 const unsigned char    *qm_grpnr,
 -                 const t_grps           *qm_grps,
 -                 t_forcerec   *          fr,
 -                 int                    *n_solvent_parameters,
 -                 solvent_parameters_t  **solvent_parameters_p,
 -                 int                     cginfo,
 -                 int                    *cg_sp)
 -{
 -    t_atom               *atom;
 -    int                   j, k;
 -    int                   j0, j1, nj;
 -    gmx_bool              perturbed;
 -    gmx_bool              has_vdw[4];
 -    gmx_bool              match;
 -    real                  tmp_charge[4]  = { 0.0 }; /* init to zero to make gcc4.8 happy */
 -    int                   tmp_vdwtype[4] = { 0 };   /* init to zero to make gcc4.8 happy */
 -    int                   tjA;
 -    gmx_bool              qm;
 -    solvent_parameters_t *solvent_parameters;
 -
 -    /* We use a list with parameters for each solvent type.
 -     * Every time we discover a new molecule that fulfills the basic
 -     * conditions for a solvent we compare with the previous entries
 -     * in these lists. If the parameters are the same we just increment
 -     * the counter for that type, and otherwise we create a new type
 -     * based on the current molecule.
 -     *
 -     * Once we've finished going through all molecules we check which
 -     * solvent is most common, and mark all those molecules while we
 -     * clear the flag on all others.
 -     */
 -
 -    solvent_parameters = *solvent_parameters_p;
 -
 -    /* Mark the cg first as non optimized */
 -    *cg_sp = -1;
 -
 -    /* Check if this cg has no exclusions with atoms in other charge groups
 -     * and all atoms inside the charge group excluded.
 -     * We only have 3 or 4 atom solvent loops.
 -     */
 -    if (GET_CGINFO_EXCL_INTER(cginfo) ||
 -        !GET_CGINFO_EXCL_INTRA(cginfo))
 -    {
 -        return;
 -    }
 -
 -    /* Get the indices of the first atom in this charge group */
 -    j0     = molt->cgs.index[cg0];
 -    j1     = molt->cgs.index[cg0+1];
 -
 -    /* Number of atoms in our molecule */
 -    nj     = j1 - j0;
 -
 -    if (debug)
 -    {
 -        fprintf(debug,
 -                "Moltype '%s': there are %d atoms in this charge group\n",
 -                *molt->name, nj);
 -    }
 -
 -    /* Check if it could be an SPC (3 atoms) or TIP4p (4) water,
 -     * otherwise skip it.
 -     */
 -    if (nj < 3 || nj > 4)
 -    {
 -        return;
 -    }
 -
 -    /* Check if we are doing QM on this group */
 -    qm = FALSE;
 -    if (qm_grpnr != nullptr)
 -    {
 -        for (j = j0; j < j1 && !qm; j++)
 -        {
 -            qm = (qm_grpnr[j] < qm_grps->nr - 1);
 -        }
 -    }
 -    /* Cannot use solvent optimization with QM */
 -    if (qm)
 -    {
 -        return;
 -    }
 -
 -    atom = molt->atoms.atom;
 -
 -    /* Still looks like a solvent, time to check parameters */
 -
 -    /* If it is perturbed (free energy) we can't use the solvent loops,
 -     * so then we just skip to the next molecule.
 -     */
 -    perturbed = FALSE;
 -
 -    for (j = j0; j < j1 && !perturbed; j++)
 -    {
 -        perturbed = PERTURBED(atom[j]);
 -    }
 -
 -    if (perturbed)
 -    {
 -        return;
 -    }
 -
 -    /* Now it's only a question if the VdW and charge parameters
 -     * are OK. Before doing the check we compare and see if they are
 -     * identical to a possible previous solvent type.
 -     * First we assign the current types and charges.
 -     */
 -    for (j = 0; j < nj; j++)
 -    {
 -        tmp_vdwtype[j] = atom[j0+j].type;
 -        tmp_charge[j]  = atom[j0+j].q;
 -    }
 -
 -    /* Does it match any previous solvent type? */
 -    for (k = 0; k < *n_solvent_parameters; k++)
 -    {
 -        match = TRUE;
 -
 -
 -        /* We can only match SPC with 3 atoms and TIP4p with 4 atoms */
 -        if ( (solvent_parameters[k].model == esolSPC   && nj != 3)  ||
 -             (solvent_parameters[k].model == esolTIP4P && nj != 4) )
 -        {
 -            match = FALSE;
 -        }
 -
 -        /* Check that types & charges match for all atoms in molecule */
 -        for (j = 0; j < nj && match; j++)
 -        {
 -            if (tmp_vdwtype[j] != solvent_parameters[k].vdwtype[j])
 -            {
 -                match = FALSE;
 -            }
 -            if (tmp_charge[j] != solvent_parameters[k].charge[j])
 -            {
 -                match = FALSE;
 -            }
 -        }
 -        if (match)
 -        {
 -            /* Congratulations! We have a matched solvent.
 -             * Flag it with this type for later processing.
 -             */
 -            *cg_sp = k;
 -            solvent_parameters[k].count += nmol;
 -
 -            /* We are done with this charge group */
 -            return;
 -        }
 -    }
 -
 -    /* If we get here, we have a tentative new solvent type.
 -     * Before we add it we must check that it fulfills the requirements
 -     * of the solvent optimized loops. First determine which atoms have
 -     * VdW interactions.
 -     */
 -    for (j = 0; j < nj; j++)
 -    {
 -        has_vdw[j] = FALSE;
 -        tjA        = tmp_vdwtype[j];
 -
 -        /* Go through all other tpes and see if any have non-zero
 -         * VdW parameters when combined with this one.
 -         */
 -        for (k = 0; k < fr->ntype && (!has_vdw[j]); k++)
 -        {
 -            /* We already checked that the atoms weren't perturbed,
 -             * so we only need to check state A now.
 -             */
 -            if (fr->bBHAM)
 -            {
 -                has_vdw[j] = (has_vdw[j] ||
 -                              (BHAMA(fr->nbfp, fr->ntype, tjA, k) != 0.0) ||
 -                              (BHAMB(fr->nbfp, fr->ntype, tjA, k) != 0.0) ||
 -                              (BHAMC(fr->nbfp, fr->ntype, tjA, k) != 0.0));
 -            }
 -            else
 -            {
 -                /* Standard LJ */
 -                has_vdw[j] = (has_vdw[j] ||
 -                              (C6(fr->nbfp, fr->ntype, tjA, k)  != 0.0) ||
 -                              (C12(fr->nbfp, fr->ntype, tjA, k) != 0.0));
 -            }
 -        }
 -    }
 -
 -    /* Now we know all we need to make the final check and assignment. */
 -    if (nj == 3)
 -    {
 -        /* So, is it an SPC?
 -         * For this we require thatn all atoms have charge,
 -         * the charges on atom 2 & 3 should be the same, and only
 -         * atom 1 might have VdW.
 -         */
 -        if (!has_vdw[1] &&
 -            !has_vdw[2] &&
 -            tmp_charge[0]  != 0 &&
 -            tmp_charge[1]  != 0 &&
 -            tmp_charge[2]  == tmp_charge[1])
 -        {
 -            srenew(solvent_parameters, *n_solvent_parameters+1);
 -            solvent_parameters[*n_solvent_parameters].model = esolSPC;
 -            solvent_parameters[*n_solvent_parameters].count = nmol;
 -            for (k = 0; k < 3; k++)
 -            {
 -                solvent_parameters[*n_solvent_parameters].vdwtype[k] = tmp_vdwtype[k];
 -                solvent_parameters[*n_solvent_parameters].charge[k]  = tmp_charge[k];
 -            }
 -
 -            *cg_sp = *n_solvent_parameters;
 -            (*n_solvent_parameters)++;
 -        }
 -    }
 -    else if (nj == 4)
 -    {
 -        /* Or could it be a TIP4P?
 -         * For this we require thatn atoms 2,3,4 have charge, but not atom 1.
 -         * Only atom 1 mght have VdW.
 -         */
 -        if (!has_vdw[1] &&
 -            !has_vdw[2] &&
 -            !has_vdw[3] &&
 -            tmp_charge[0]  == 0 &&
 -            tmp_charge[1]  != 0 &&
 -            tmp_charge[2]  == tmp_charge[1] &&
 -            tmp_charge[3]  != 0)
 -        {
 -            srenew(solvent_parameters, *n_solvent_parameters+1);
 -            solvent_parameters[*n_solvent_parameters].model = esolTIP4P;
 -            solvent_parameters[*n_solvent_parameters].count = nmol;
 -            for (k = 0; k < 4; k++)
 -            {
 -                solvent_parameters[*n_solvent_parameters].vdwtype[k] = tmp_vdwtype[k];
 -                solvent_parameters[*n_solvent_parameters].charge[k]  = tmp_charge[k];
 -            }
 -
 -            *cg_sp = *n_solvent_parameters;
 -            (*n_solvent_parameters)++;
 -        }
 -    }
 -
 -    *solvent_parameters_p = solvent_parameters;
 -}
 -
 -static void
 -check_solvent(FILE  *                fp,
 -              const gmx_mtop_t  *    mtop,
 -              t_forcerec  *          fr,
 -              cginfo_mb_t           *cginfo_mb)
 +enum
  {
 -    const t_block     *   cgs;
 -    const gmx_moltype_t  *molt;
 -    int                   mol, cg_mol, at_offset, am, cgm, i, nmol_ch, nmol;
 -    int                   n_solvent_parameters;
 -    solvent_parameters_t *solvent_parameters;
 -    int                 **cg_sp;
 -    int                   bestsp, bestsol;
 -
 -    if (debug)
 -    {
 -        fprintf(debug, "Going to determine what solvent types we have.\n");
 -    }
 -
 -    n_solvent_parameters = 0;
 -    solvent_parameters   = nullptr;
 -    /* Allocate temporary array for solvent type */
 -    snew(cg_sp, mtop->molblock.size());
 -
 -    at_offset = 0;
 -    for (size_t mb = 0; mb < mtop->molblock.size(); mb++)
 -    {
 -        molt = &mtop->moltype[mtop->molblock[mb].type];
 -        cgs  = &molt->cgs;
 -        /* Here we have to loop over all individual molecules
 -         * because we need to check for QMMM particles.
 -         */
 -        snew(cg_sp[mb], cginfo_mb[mb].cg_mod);
 -        nmol_ch = cginfo_mb[mb].cg_mod/cgs->nr;
 -        nmol    = mtop->molblock[mb].nmol/nmol_ch;
 -        for (mol = 0; mol < nmol_ch; mol++)
 -        {
 -            cgm = mol*cgs->nr;
 -            am  = mol*cgs->index[cgs->nr];
 -            for (cg_mol = 0; cg_mol < cgs->nr; cg_mol++)
 -            {
 -                check_solvent_cg(molt, cg_mol, nmol,
 -                                 mtop->groups.grpnr[egcQMMM] ?
 -                                 mtop->groups.grpnr[egcQMMM]+at_offset+am : nullptr,
 -                                 &mtop->groups.grps[egcQMMM],
 -                                 fr,
 -                                 &n_solvent_parameters, &solvent_parameters,
 -                                 cginfo_mb[mb].cginfo[cgm+cg_mol],
 -                                 &cg_sp[mb][cgm+cg_mol]);
 -            }
 -        }
 -        at_offset += cgs->index[cgs->nr];
 -    }
 -
 -    /* Puh! We finished going through all charge groups.
 -     * Now find the most common solvent model.
 -     */
 -
 -    /* Most common solvent this far */
 -    bestsp = -2;
 -    for (i = 0; i < n_solvent_parameters; i++)
 -    {
 -        if (bestsp == -2 ||
 -            solvent_parameters[i].count > solvent_parameters[bestsp].count)
 -        {
 -            bestsp = i;
 -        }
 -    }
 -
 -    if (bestsp >= 0)
 -    {
 -        bestsol = solvent_parameters[bestsp].model;
 -    }
 -    else
 -    {
 -        bestsol = esolNO;
 -    }
 -
 -    fr->nWatMol = 0;
 -    for (size_t mb = 0; mb < mtop->molblock.size(); mb++)
 -    {
 -        cgs  = &mtop->moltype[mtop->molblock[mb].type].cgs;
 -        nmol = (mtop->molblock[mb].nmol*cgs->nr)/cginfo_mb[mb].cg_mod;
 -        for (i = 0; i < cginfo_mb[mb].cg_mod; i++)
 -        {
 -            if (cg_sp[mb][i] == bestsp)
 -            {
 -                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[i], bestsol);
 -                fr->nWatMol += nmol;
 -            }
 -            else
 -            {
 -                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[i], esolNO);
 -            }
 -        }
 -        sfree(cg_sp[mb]);
 -    }
 -    sfree(cg_sp);
 -
 -    if (bestsol != esolNO && fp != nullptr)
 -    {
 -        fprintf(fp, "\nEnabling %s-like water optimization for %d molecules.\n\n",
 -                esol_names[bestsol],
 -                solvent_parameters[bestsp].count);
 -    }
 -
 -    sfree(solvent_parameters);
 -    fr->solvent_opt = bestsol;
 -}
 -
 -enum {
 -    acNONE = 0, acCONSTRAINT, acSETTLE
 +    acNONE = 0,
 +    acCONSTRAINT,
 +    acSETTLE
  };
  
 -static cginfo_mb_t *init_cginfo_mb(FILE *fplog, const gmx_mtop_t *mtop,
 -                                   t_forcerec *fr, gmx_bool bNoSolvOpt,
 -                                   gmx_bool *bFEP_NonBonded,
 -                                   gmx_bool *bExcl_IntraCGAll_InterCGNone)
 +static cginfo_mb_t* init_cginfo_mb(const gmx_mtop_t* mtop, const t_forcerec* fr, gmx_bool* bFEP_NonBonded)
  {
 -    const t_block        *cgs;
 -    const t_blocka       *excl;
 -    const gmx_moltype_t  *molt;
 -    const gmx_molblock_t *molb;
 -    cginfo_mb_t          *cginfo_mb;
 -    gmx_bool             *type_VDW;
 -    int                  *cginfo;
 -    int                   cg_offset, a_offset;
 -    int                   m, cg, a0, a1, gid, ai, j, aj, excl_nalloc;
 -    int                  *a_con;
 -    int                   ftype;
 -    int                   ia;
 -    gmx_bool              bId, *bExcl, bExclIntraAll, bExclInter, bHaveVDW, bHaveQ, bHavePerturbedAtoms;
 +    cginfo_mb_t* cginfo_mb;
 +    gmx_bool*    type_VDW;
 +    int*         cginfo;
 +    int*         a_con;
  
      snew(cginfo_mb, mtop->molblock.size());
  
      snew(type_VDW, fr->ntype);
 -    for (ai = 0; ai < fr->ntype; ai++)
 +    for (int ai = 0; ai < fr->ntype; ai++)
      {
          type_VDW[ai] = FALSE;
 -        for (j = 0; j < fr->ntype; j++)
 +        for (int j = 0; j < fr->ntype; j++)
          {
 -            type_VDW[ai] = type_VDW[ai] ||
 -                fr->bBHAM ||
 -                C6(fr->nbfp, fr->ntype, ai, j) != 0 ||
 -                C12(fr->nbfp, fr->ntype, ai, j) != 0;
 +            type_VDW[ai] = type_VDW[ai] || fr->bBHAM || C6(fr->nbfp, fr->ntype, ai, j) != 0
 +                           || C12(fr->nbfp, fr->ntype, ai, j) != 0;
          }
      }
  
 -    *bFEP_NonBonded               = FALSE;
 -    *bExcl_IntraCGAll_InterCGNone = TRUE;
 +    *bFEP_NonBonded = FALSE;
  
 -    excl_nalloc = 10;
 -    snew(bExcl, excl_nalloc);
 -    cg_offset = 0;
 -    a_offset  = 0;
 +    int a_offset = 0;
      for (size_t mb = 0; mb < mtop->molblock.size(); mb++)
      {
 -        molb = &mtop->molblock[mb];
 -        molt = &mtop->moltype[molb->type];
 -        cgs  = &molt->cgs;
 -        excl = &molt->excls;
 +        const gmx_molblock_t& molb = mtop->molblock[mb];
 +        const gmx_moltype_t&  molt = mtop->moltype[molb.type];
 +        const t_blocka&       excl = molt.excls;
  
          /* Check if the cginfo is identical for all molecules in this block.
           * If so, we only need an array of the size of one molecule.
           * Otherwise we make an array of #mol times #cgs per molecule.
           */
 -        bId = TRUE;
 -        for (m = 0; m < molb->nmol; m++)
 +        gmx_bool bId = TRUE;
 +        for (int m = 0; m < molb.nmol; m++)
          {
 -            int am = m*cgs->index[cgs->nr];
 -            for (cg = 0; cg < cgs->nr; cg++)
 +            const int am = m * molt.atoms.nr;
 +            for (int a = 0; a < molt.atoms.nr; a++)
              {
 -                a0 = cgs->index[cg];
 -                a1 = cgs->index[cg+1];
 -                if (getGroupType(&mtop->groups, egcENER, a_offset+am+a0) !=
 -                    getGroupType(&mtop->groups, egcENER, a_offset   +a0))
 +                if (getGroupType(mtop->groups, SimulationAtomGroupType::QuantumMechanics, a_offset + am + a)
 +                    != getGroupType(mtop->groups, SimulationAtomGroupType::QuantumMechanics, a_offset + a))
                  {
                      bId = FALSE;
                  }
 -                if (mtop->groups.grpnr[egcQMMM] != nullptr)
 +                if (!mtop->groups.groupNumbers[SimulationAtomGroupType::QuantumMechanics].empty())
                  {
 -                    for (ai = a0; ai < a1; ai++)
 +                    if (mtop->groups.groupNumbers[SimulationAtomGroupType::QuantumMechanics][a_offset + am + a]
 +                        != mtop->groups.groupNumbers[SimulationAtomGroupType::QuantumMechanics][a_offset + a])
                      {
 -                        if (mtop->groups.grpnr[egcQMMM][a_offset+am+ai] !=
 -                            mtop->groups.grpnr[egcQMMM][a_offset   +ai])
 -                        {
 -                            bId = FALSE;
 -                        }
 +                        bId = FALSE;
                      }
                  }
              }
          }
  
 -        cginfo_mb[mb].cg_start = cg_offset;
 -        cginfo_mb[mb].cg_end   = cg_offset + molb->nmol*cgs->nr;
 -        cginfo_mb[mb].cg_mod   = (bId ? 1 : molb->nmol)*cgs->nr;
 +        cginfo_mb[mb].cg_start = a_offset;
 +        cginfo_mb[mb].cg_end   = a_offset + molb.nmol * molt.atoms.nr;
 +        cginfo_mb[mb].cg_mod   = (bId ? 1 : molb.nmol) * molt.atoms.nr;
          snew(cginfo_mb[mb].cginfo, cginfo_mb[mb].cg_mod);
          cginfo = cginfo_mb[mb].cginfo;
  
          /* Set constraints flags for constrained atoms */
 -        snew(a_con, molt->atoms.nr);
 -        for (ftype = 0; ftype < F_NRE; ftype++)
 +        snew(a_con, molt.atoms.nr);
 +        for (int ftype = 0; ftype < F_NRE; ftype++)
          {
              if (interaction_function[ftype].flags & IF_CONSTRAINT)
              {
 -                int nral;
 -
 -                nral = NRAL(ftype);
 -                for (ia = 0; ia < molt->ilist[ftype].size(); ia += 1+nral)
 +                const int nral = NRAL(ftype);
 +                for (int ia = 0; ia < molt.ilist[ftype].size(); ia += 1 + nral)
                  {
                      int a;
  
                      for (a = 0; a < nral; a++)
                      {
 -                        a_con[molt->ilist[ftype].iatoms[ia+1+a]] =
 -                            (ftype == F_SETTLE ? acSETTLE : acCONSTRAINT);
 +                        a_con[molt.ilist[ftype].iatoms[ia + 1 + a]] =
 +                                (ftype == F_SETTLE ? acSETTLE : acCONSTRAINT);
                      }
                  }
              }
          }
  
 -        for (m = 0; m < (bId ? 1 : molb->nmol); m++)
 +        for (int m = 0; m < (bId ? 1 : molb.nmol); m++)
          {
 -            int cgm = m*cgs->nr;
 -            int am  = m*cgs->index[cgs->nr];
 -            for (cg = 0; cg < cgs->nr; cg++)
 +            const int molculeOffsetInBlock = m * molt.atoms.nr;
 +            for (int a = 0; a < molt.atoms.nr; a++)
              {
 -                a0 = cgs->index[cg];
 -                a1 = cgs->index[cg+1];
 +                const t_atom& atom     = molt.atoms.atom[a];
 +                int&          atomInfo = cginfo[molculeOffsetInBlock + a];
  
                  /* Store the energy group in cginfo */
 -                gid = getGroupType(&mtop->groups, egcENER, a_offset+am+a0);
 -                SET_CGINFO_GID(cginfo[cgm+cg], gid);
 +                int gid = getGroupType(mtop->groups, SimulationAtomGroupType::EnergyOutput,
 +                                       a_offset + molculeOffsetInBlock + a);
 +                SET_CGINFO_GID(atomInfo, gid);
  
 -                /* Check the intra/inter charge group exclusions */
 -                if (a1-a0 > excl_nalloc)
 -                {
 -                    excl_nalloc = a1 - a0;
 -                    srenew(bExcl, excl_nalloc);
 -                }
 -                /* bExclIntraAll: all intra cg interactions excluded
 -                 * bExclInter:    any inter cg interactions excluded
 -                 */
 -                bExclIntraAll       = TRUE;
 -                bExclInter          = FALSE;
 -                bHaveVDW            = FALSE;
 -                bHaveQ              = FALSE;
 -                bHavePerturbedAtoms = FALSE;
 -                for (ai = a0; ai < a1; ai++)
 -                {
 -                    /* Check VDW and electrostatic interactions */
 -                    bHaveVDW = bHaveVDW || (type_VDW[molt->atoms.atom[ai].type] ||
 -                                            type_VDW[molt->atoms.atom[ai].typeB]);
 -                    bHaveQ  = bHaveQ    || (molt->atoms.atom[ai].q != 0 ||
 -                                            molt->atoms.atom[ai].qB != 0);
 -
 -                    bHavePerturbedAtoms = bHavePerturbedAtoms || (PERTURBED(molt->atoms.atom[ai]) != 0);
 -
 -                    /* Clear the exclusion list for atom ai */
 -                    for (aj = a0; aj < a1; aj++)
 -                    {
 -                        bExcl[aj-a0] = FALSE;
 -                    }
 -                    /* Loop over all the exclusions of atom ai */
 -                    for (j = excl->index[ai]; j < excl->index[ai+1]; j++)
 -                    {
 -                        aj = excl->a[j];
 -                        if (aj < a0 || aj >= a1)
 -                        {
 -                            bExclInter = TRUE;
 -                        }
 -                        else
 -                        {
 -                            bExcl[aj-a0] = TRUE;
 -                        }
 -                    }
 -                    /* Check if ai excludes a0 to a1 */
 -                    for (aj = a0; aj < a1; aj++)
 -                    {
 -                        if (!bExcl[aj-a0])
 -                        {
 -                            bExclIntraAll = FALSE;
 -                        }
 -                    }
 +                bool bHaveVDW = (type_VDW[atom.type] || type_VDW[atom.typeB]);
 +                bool bHaveQ   = (atom.q != 0 || atom.qB != 0);
  
 -                    switch (a_con[ai])
 +                bool haveExclusions = false;
 +                /* Loop over all the exclusions of atom ai */
 +                for (int j = excl.index[a]; j < excl.index[a + 1]; j++)
 +                {
 +                    if (excl.a[j] != a)
                      {
 -                        case acCONSTRAINT:
 -                            SET_CGINFO_CONSTR(cginfo[cgm+cg]);
 -                            break;
 -                        case acSETTLE:
 -                            SET_CGINFO_SETTLE(cginfo[cgm+cg]);
 -                            break;
 -                        default:
 -                            break;
 +                        haveExclusions = true;
 +                        break;
                      }
                  }
 -                if (bExclIntraAll)
 -                {
 -                    SET_CGINFO_EXCL_INTRA(cginfo[cgm+cg]);
 -                }
 -                if (bExclInter)
 +
 +                switch (a_con[a])
                  {
 -                    SET_CGINFO_EXCL_INTER(cginfo[cgm+cg]);
 +                    case acCONSTRAINT: SET_CGINFO_CONSTR(atomInfo); break;
 +                    case acSETTLE: SET_CGINFO_SETTLE(atomInfo); break;
 +                    default: break;
                  }
 -                if (a1 - a0 > MAX_CHARGEGROUP_SIZE)
 +                if (haveExclusions)
                  {
 -                    /* The size in cginfo is currently only read with DD */
 -                    gmx_fatal(FARGS, "A charge group has size %d which is larger than the limit of %d atoms", a1-a0, MAX_CHARGEGROUP_SIZE);
 +                    SET_CGINFO_EXCL_INTER(atomInfo);
                  }
                  if (bHaveVDW)
                  {
 -                    SET_CGINFO_HAS_VDW(cginfo[cgm+cg]);
 +                    SET_CGINFO_HAS_VDW(atomInfo);
                  }
                  if (bHaveQ)
                  {
 -                    SET_CGINFO_HAS_Q(cginfo[cgm+cg]);
 +                    SET_CGINFO_HAS_Q(atomInfo);
                  }
 -                if (bHavePerturbedAtoms && fr->efep != efepNO)
 +                if (fr->efep != efepNO && PERTURBED(atom))
                  {
 -                    SET_CGINFO_FEP(cginfo[cgm+cg]);
 +                    SET_CGINFO_FEP(atomInfo);
                      *bFEP_NonBonded = TRUE;
                  }
 -                /* Store the charge group size */
 -                SET_CGINFO_NATOMS(cginfo[cgm+cg], a1-a0);
 -
 -                if (!bExclIntraAll || bExclInter)
 -                {
 -                    *bExcl_IntraCGAll_InterCGNone = FALSE;
 -                }
              }
          }
  
          sfree(a_con);
  
 -        cg_offset += molb->nmol*cgs->nr;
 -        a_offset  += molb->nmol*cgs->index[cgs->nr];
 +        a_offset += molb.nmol * molt.atoms.nr;
      }
      sfree(type_VDW);
 -    sfree(bExcl);
 -
 -    /* the solvent optimizer is called after the QM is initialized,
 -     * because we don't want to have the QM subsystemto become an
 -     * optimized solvent
 -     */
 -
 -    check_solvent(fplog, mtop, fr, cginfo_mb);
 -
 -    if (getenv("GMX_NO_SOLV_OPT"))
 -    {
 -        if (fplog)
 -        {
 -            fprintf(fplog, "Found environment variable GMX_NO_SOLV_OPT.\n"
 -                    "Disabling all solvent optimization\n");
 -        }
 -        fr->solvent_opt = esolNO;
 -    }
 -    if (bNoSolvOpt)
 -    {
 -        fr->solvent_opt = esolNO;
 -    }
 -    if (!fr->solvent_opt)
 -    {
 -        for (size_t mb = 0; mb < mtop->molblock.size(); mb++)
 -        {
 -            for (cg = 0; cg < cginfo_mb[mb].cg_mod; cg++)
 -            {
 -                SET_CGINFO_SOLOPT(cginfo_mb[mb].cginfo[cg], esolNO);
 -            }
 -        }
 -    }
  
      return cginfo_mb;
  }
  
 -static int *cginfo_expand(int nmb, cginfo_mb_t *cgi_mb)
 +static std::vector<int> cginfo_expand(const int nmb, const cginfo_mb_t* cgi_mb)
  {
 -    int  ncg, mb, cg;
 -    int *cginfo;
 +    const int ncg = cgi_mb[nmb - 1].cg_end;
  
 -    ncg = cgi_mb[nmb-1].cg_end;
 -    snew(cginfo, ncg);
 -    mb = 0;
 -    for (cg = 0; cg < ncg; cg++)
 +    std::vector<int> cginfo(ncg);
 +
 +    int mb = 0;
 +    for (int cg = 0; cg < ncg; cg++)
      {
          while (cg >= cgi_mb[mb].cg_end)
          {
              mb++;
          }
 -        cginfo[cg] =
 -            cgi_mb[mb].cginfo[(cg - cgi_mb[mb].cg_start) % cgi_mb[mb].cg_mod];
 +        cginfo[cg] = cgi_mb[mb].cginfo[(cg - cgi_mb[mb].cg_start) % cgi_mb[mb].cg_mod];
      }
  
      return cginfo;
  }
  
 -static void done_cginfo_mb(cginfo_mb_t *cginfo_mb, int numMolBlocks)
 +static void done_cginfo_mb(cginfo_mb_tcginfo_mb, int numMolBlocks)
  {
      if (cginfo_mb == nullptr)
      {
  /* Sets the sum of charges (squared) and C6 in the system in fr.
   * Returns whether the system has a net charge.
   */
 -static bool set_chargesum(FILE *log, t_forcerec *fr, const gmx_mtop_t *mtop)
 +static bool set_chargesum(FILE* log, t_forcerec* fr, const gmx_mtop_t* mtop)
  {
      /*This now calculates sum for q and c6*/
 -    double         qsum, q2sum, q, c6sum, c6;
 +    double qsum, q2sum, q, c6sum, c6;
  
 -    qsum   = 0;
 -    q2sum  = 0;
 -    c6sum  = 0;
 -    for (const gmx_molblock_t &molb : mtop->molblock)
 +    qsum  = 0;
 +    q2sum = 0;
 +    c6sum = 0;
 +    for (const gmx_molblock_tmolb : mtop->molblock)
      {
          int            nmol  = molb.nmol;
 -        const t_atoms *atoms = &mtop->moltype[molb.type].atoms;
 +        const t_atomsatoms = &mtop->moltype[molb.type].atoms;
          for (int i = 0; i < atoms->nr; i++)
          {
 -            q       = atoms->atom[i].q;
 -            qsum   += nmol*q;
 -            q2sum  += nmol*q*q;
 -            c6      = mtop->ffparams.iparams[atoms->atom[i].type*(mtop->ffparams.atnr+1)].lj.c6;
 -            c6sum  += nmol*c6;
 +            q = atoms->atom[i].q;
 +            qsum += nmol * q;
 +            q2sum += nmol * q * q;
 +            c6 = mtop->ffparams.iparams[atoms->atom[i].type * (mtop->ffparams.atnr + 1)].lj.c6;
 +            c6sum += nmol * c6;
          }
      }
 -    fr->qsum[0]   = qsum;
 -    fr->q2sum[0]  = q2sum;
 -    fr->c6sum[0]  = c6sum;
 +    fr->qsum[0]  = qsum;
 +    fr->q2sum[0] = q2sum;
 +    fr->c6sum[0] = c6sum;
  
      if (fr->efep != efepNO)
      {
 -        qsum   = 0;
 -        q2sum  = 0;
 -        c6sum  = 0;
 -        for (const gmx_molblock_t &molb : mtop->molblock)
 +        qsum  = 0;
 +        q2sum = 0;
 +        c6sum = 0;
 +        for (const gmx_molblock_tmolb : mtop->molblock)
          {
              int            nmol  = molb.nmol;
 -            const t_atoms *atoms = &mtop->moltype[molb.type].atoms;
 +            const t_atomsatoms = &mtop->moltype[molb.type].atoms;
              for (int i = 0; i < atoms->nr; i++)
              {
 -                q       = atoms->atom[i].qB;
 -                qsum   += nmol*q;
 -                q2sum  += nmol*q*q;
 -                c6      = mtop->ffparams.iparams[atoms->atom[i].typeB*(mtop->ffparams.atnr+1)].lj.c6;
 -                c6sum  += nmol*c6;
 +                q = atoms->atom[i].qB;
 +                qsum += nmol * q;
 +                q2sum += nmol * q * q;
 +                c6 = mtop->ffparams.iparams[atoms->atom[i].typeB * (mtop->ffparams.atnr + 1)].lj.c6;
 +                c6sum += nmol * c6;
              }
 -            fr->qsum[1]   = qsum;
 -            fr->q2sum[1]  = q2sum;
 -            fr->c6sum[1]  = c6sum;
 +            fr->qsum[1]  = qsum;
 +            fr->q2sum[1] = q2sum;
 +            fr->c6sum[1] = c6sum;
          }
      }
      else
      {
 -        fr->qsum[1]   = fr->qsum[0];
 -        fr->q2sum[1]  = fr->q2sum[0];
 -        fr->c6sum[1]  = fr->c6sum[0];
 +        fr->qsum[1]  = fr->qsum[0];
 +        fr->q2sum[1] = fr->q2sum[0];
 +        fr->c6sum[1] = fr->c6sum[0];
      }
      if (log)
      {
          }
          else
          {
 -            fprintf(log, "System total charge, top. A: %.3f top. B: %.3f\n",
 -                    fr->qsum[0], fr->qsum[1]);
 +            fprintf(log, "System total charge, top. A: %.3f top. B: %.3f\n", fr->qsum[0], fr->qsum[1]);
          }
      }
  
      /* A cut-off of 1e-4 is used to catch rounding errors due to ascii input */
 -    return (std::abs(fr->qsum[0]) > 1e-4 ||
 -            std::abs(fr->qsum[1]) > 1e-4);
 -}
 -
 -void update_forcerec(t_forcerec *fr, matrix box)
 -{
 -    if (fr->ic->eeltype == eelGRF)
 -    {
 -        calc_rffac(nullptr, fr->ic->eeltype, fr->ic->epsilon_r, fr->ic->epsilon_rf,
 -                   fr->ic->rcoulomb, fr->temp, fr->zsquare, box,
 -                   &fr->ic->k_rf, &fr->ic->c_rf);
 -    }
 +    return (std::abs(fr->qsum[0]) > 1e-4 || std::abs(fr->qsum[1]) > 1e-4);
  }
  
 -void set_avcsixtwelve(FILE *fplog, t_forcerec *fr, const gmx_mtop_t *mtop)
 -{
 -    const t_atoms  *atoms, *atoms_tpi;
 -    const t_blocka *excl;
 -    int             nmolc, i, j, tpi, tpj, j1, j2, k, nexcl, q;
 -    int64_t         npair, npair_ij, tmpi, tmpj;
 -    double          csix, ctwelve;
 -    int             ntp, *typecount;
 -    gmx_bool        bBHAM;
 -    real           *nbfp;
 -    real           *nbfp_comb = nullptr;
 -
 -    ntp   = fr->ntype;
 -    bBHAM = fr->bBHAM;
 -    nbfp  = fr->nbfp;
 -
 -    /* For LJ-PME, we want to correct for the difference between the
 -     * actual C6 values and the C6 values used by the LJ-PME based on
 -     * combination rules. */
 -
 -    if (EVDW_PME(fr->ic->vdwtype))
 -    {
 -        nbfp_comb = mk_nbfp_combination_rule(&mtop->ffparams,
 -                                             (fr->ljpme_combination_rule == eljpmeLB) ? eCOMB_ARITHMETIC : eCOMB_GEOMETRIC);
 -        for (tpi = 0; tpi < ntp; ++tpi)
 -        {
 -            for (tpj = 0; tpj < ntp; ++tpj)
 -            {
 -                C6(nbfp_comb, ntp, tpi, tpj) =
 -                    C6(nbfp, ntp, tpi, tpj) - C6(nbfp_comb, ntp, tpi, tpj);
 -                C12(nbfp_comb, ntp, tpi, tpj) = C12(nbfp, ntp, tpi, tpj);
 -            }
 -        }
 -        nbfp = nbfp_comb;
 -    }
 -    for (q = 0; q < (fr->efep == efepNO ? 1 : 2); q++)
 -    {
 -        csix    = 0;
 -        ctwelve = 0;
 -        npair   = 0;
 -        nexcl   = 0;
 -        if (!fr->n_tpi)
 -        {
 -            /* Count the types so we avoid natoms^2 operations */
 -            snew(typecount, ntp);
 -            gmx_mtop_count_atomtypes(mtop, q, typecount);
 -
 -            for (tpi = 0; tpi < ntp; tpi++)
 -            {
 -                for (tpj = tpi; tpj < ntp; tpj++)
 -                {
 -                    tmpi = typecount[tpi];
 -                    tmpj = typecount[tpj];
 -                    if (tpi != tpj)
 -                    {
 -                        npair_ij = tmpi*tmpj;
 -                    }
 -                    else
 -                    {
 -                        npair_ij = tmpi*(tmpi - 1)/2;
 -                    }
 -                    if (bBHAM)
 -                    {
 -                        /* nbfp now includes the 6.0 derivative prefactor */
 -                        csix    += npair_ij*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
 -                    }
 -                    else
 -                    {
 -                        /* nbfp now includes the 6.0/12.0 derivative prefactors */
 -                        csix    += npair_ij*   C6(nbfp, ntp, tpi, tpj)/6.0;
 -                        ctwelve += npair_ij*  C12(nbfp, ntp, tpi, tpj)/12.0;
 -                    }
 -                    npair += npair_ij;
 -                }
 -            }
 -            sfree(typecount);
 -            /* Subtract the excluded pairs.
 -             * The main reason for substracting exclusions is that in some cases
 -             * some combinations might never occur and the parameters could have
 -             * any value. These unused values should not influence the dispersion
 -             * correction.
 -             */
 -            for (const gmx_molblock_t &molb : mtop->molblock)
 -            {
 -                int nmol = molb.nmol;
 -                atoms    = &mtop->moltype[molb.type].atoms;
 -                excl     = &mtop->moltype[molb.type].excls;
 -                for (int i = 0; (i < atoms->nr); i++)
 -                {
 -                    if (q == 0)
 -                    {
 -                        tpi = atoms->atom[i].type;
 -                    }
 -                    else
 -                    {
 -                        tpi = atoms->atom[i].typeB;
 -                    }
 -                    j1  = excl->index[i];
 -                    j2  = excl->index[i+1];
 -                    for (j = j1; j < j2; j++)
 -                    {
 -                        k = excl->a[j];
 -                        if (k > i)
 -                        {
 -                            if (q == 0)
 -                            {
 -                                tpj = atoms->atom[k].type;
 -                            }
 -                            else
 -                            {
 -                                tpj = atoms->atom[k].typeB;
 -                            }
 -                            if (bBHAM)
 -                            {
 -                                /* nbfp now includes the 6.0 derivative prefactor */
 -                                csix -= nmol*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
 -                            }
 -                            else
 -                            {
 -                                /* nbfp now includes the 6.0/12.0 derivative prefactors */
 -                                csix    -= nmol*C6 (nbfp, ntp, tpi, tpj)/6.0;
 -                                ctwelve -= nmol*C12(nbfp, ntp, tpi, tpj)/12.0;
 -                            }
 -                            nexcl += molb.nmol;
 -                        }
 -                    }
 -                }
 -            }
 -        }
 -        else
 -        {
 -            /* Only correct for the interaction of the test particle
 -             * with the rest of the system.
 -             */
 -            atoms_tpi =
 -                &mtop->moltype[mtop->molblock.back().type].atoms;
 -
 -            npair = 0;
 -            for (size_t mb = 0; mb < mtop->molblock.size(); mb++)
 -            {
 -                const gmx_molblock_t &molb = mtop->molblock[mb];
 -                atoms                      = &mtop->moltype[molb.type].atoms;
 -                for (j = 0; j < atoms->nr; j++)
 -                {
 -                    nmolc = molb.nmol;
 -                    /* Remove the interaction of the test charge group
 -                     * with itself.
 -                     */
 -                    if (mb == mtop->molblock.size() - 1)
 -                    {
 -                        nmolc--;
 -
 -                        if (mb == 0 && molb.nmol == 1)
 -                        {
 -                            gmx_fatal(FARGS, "Old format tpr with TPI, please generate a new tpr file");
 -                        }
 -                    }
 -                    if (q == 0)
 -                    {
 -                        tpj = atoms->atom[j].type;
 -                    }
 -                    else
 -                    {
 -                        tpj = atoms->atom[j].typeB;
 -                    }
 -                    for (i = 0; i < fr->n_tpi; i++)
 -                    {
 -                        if (q == 0)
 -                        {
 -                            tpi = atoms_tpi->atom[i].type;
 -                        }
 -                        else
 -                        {
 -                            tpi = atoms_tpi->atom[i].typeB;
 -                        }
 -                        if (bBHAM)
 -                        {
 -                            /* nbfp now includes the 6.0 derivative prefactor */
 -                            csix    += nmolc*BHAMC(nbfp, ntp, tpi, tpj)/6.0;
 -                        }
 -                        else
 -                        {
 -                            /* nbfp now includes the 6.0/12.0 derivative prefactors */
 -                            csix    += nmolc*C6 (nbfp, ntp, tpi, tpj)/6.0;
 -                            ctwelve += nmolc*C12(nbfp, ntp, tpi, tpj)/12.0;
 -                        }
 -                        npair += nmolc;
 -                    }
 -                }
 -            }
 -        }
 -        if (npair - nexcl <= 0 && fplog)
 -        {
 -            fprintf(fplog, "\nWARNING: There are no atom pairs for dispersion correction\n\n");
 -            csix     = 0;
 -            ctwelve  = 0;
 -        }
 -        else
 -        {
 -            csix    /= npair - nexcl;
 -            ctwelve /= npair - nexcl;
 -        }
 -        if (debug)
 -        {
 -            fprintf(debug, "Counted %d exclusions\n", nexcl);
 -            fprintf(debug, "Average C6 parameter is: %10g\n", csix);
 -            fprintf(debug, "Average C12 parameter is: %10g\n", ctwelve);
 -        }
 -        fr->avcsix[q]    = csix;
 -        fr->avctwelve[q] = ctwelve;
 -    }
 -
 -    if (EVDW_PME(fr->ic->vdwtype))
 -    {
 -        sfree(nbfp_comb);
 -    }
 -
 -    if (fplog != nullptr)
 -    {
 -        if (fr->eDispCorr == edispcAllEner ||
 -            fr->eDispCorr == edispcAllEnerPres)
 -        {
 -            fprintf(fplog, "Long Range LJ corr.: <C6> %10.4e, <C12> %10.4e\n",
 -                    fr->avcsix[0], fr->avctwelve[0]);
 -        }
 -        else
 -        {
 -            fprintf(fplog, "Long Range LJ corr.: <C6> %10.4e\n", fr->avcsix[0]);
 -        }
 -    }
 -}
 -
 -
 -static real calcBuckinghamBMax(FILE *fplog, const gmx_mtop_t *mtop)
 +static real calcBuckinghamBMax(FILE* fplog, const gmx_mtop_t* mtop)
  {
      const t_atoms *at1, *at2;
      int            i, j, tpi, tpj, ntypes;
                      {
                          gmx_fatal(FARGS, "Atomtype[%d] = %d, maximum = %d", j, tpj, ntypes);
                      }
 -                    b = mtop->ffparams.iparams[tpi*ntypes + tpj].bham.b;
 +                    b = mtop->ffparams.iparams[tpi * ntypes + tpj].bham.b;
                      if (b > bham_b_max)
                      {
                          bham_b_max = b;
      }
      if (fplog)
      {
 -        fprintf(fplog, "Buckingham b parameters, min: %g, max: %g\n",
 -                bmin, bham_b_max);
 +        fprintf(fplog, "Buckingham b parameters, min: %g, max: %g\n", bmin, bham_b_max);
      }
  
      return bham_b_max;
  }
  
 -static void make_nbf_tables(FILE *fp,
 -                            const interaction_const_t *ic, real rtab,
 -                            const char *tabfn, char *eg1, char *eg2,
 -                            t_nblists *nbl)
 -{
 -    char buf[STRLEN];
 -    int  i, j;
 -
 -    if (tabfn == nullptr)
 -    {
 -        if (debug)
 -        {
 -            fprintf(debug, "No table file name passed, can not read table, can not do non-bonded interactions\n");
 -        }
 -        return;
 -    }
 -
 -    sprintf(buf, "%s", tabfn);
 -    if (eg1 && eg2)
 -    {
 -        /* Append the two energy group names */
 -        sprintf(buf + strlen(tabfn) - strlen(ftp2ext(efXVG)) - 1, "_%s_%s.%s",
 -                eg1, eg2, ftp2ext(efXVG));
 -    }
 -    nbl->table_elec_vdw = make_tables(fp, ic, buf, rtab, 0);
 -    /* Copy the contents of the table to separate coulomb and LJ tables too,
 -     * to improve cache performance.
 -     */
 -    /* For performance reasons we want
 -     * the table data to be aligned to 16-byte. The pointers could be freed
 -     * but currently aren't.
 -     */
 -    snew(nbl->table_elec, 1);
 -    nbl->table_elec->interaction   = GMX_TABLE_INTERACTION_ELEC;
 -    nbl->table_elec->format        = nbl->table_elec_vdw->format;
 -    nbl->table_elec->r             = nbl->table_elec_vdw->r;
 -    nbl->table_elec->n             = nbl->table_elec_vdw->n;
 -    nbl->table_elec->scale         = nbl->table_elec_vdw->scale;
 -    nbl->table_elec->formatsize    = nbl->table_elec_vdw->formatsize;
 -    nbl->table_elec->ninteractions = 1;
 -    nbl->table_elec->stride        = nbl->table_elec->formatsize * nbl->table_elec->ninteractions;
 -    snew_aligned(nbl->table_elec->data, nbl->table_elec->stride*(nbl->table_elec->n+1), 32);
 -
 -    snew(nbl->table_vdw, 1);
 -    nbl->table_vdw->interaction   = GMX_TABLE_INTERACTION_VDWREP_VDWDISP;
 -    nbl->table_vdw->format        = nbl->table_elec_vdw->format;
 -    nbl->table_vdw->r             = nbl->table_elec_vdw->r;
 -    nbl->table_vdw->n             = nbl->table_elec_vdw->n;
 -    nbl->table_vdw->scale         = nbl->table_elec_vdw->scale;
 -    nbl->table_vdw->formatsize    = nbl->table_elec_vdw->formatsize;
 -    nbl->table_vdw->ninteractions = 2;
 -    nbl->table_vdw->stride        = nbl->table_vdw->formatsize * nbl->table_vdw->ninteractions;
 -    snew_aligned(nbl->table_vdw->data, nbl->table_vdw->stride*(nbl->table_vdw->n+1), 32);
 -
 -    /* NOTE: Using a single i-loop here leads to mix-up of data in table_vdw
 -     *       with (at least) gcc 6.2, 6.3 and 6.4 when compiled with -O3 and AVX
 -     */
 -    for (i = 0; i <= nbl->table_elec_vdw->n; i++)
 -    {
 -        for (j = 0; j < 4; j++)
 -        {
 -            nbl->table_elec->data[4*i+j] = nbl->table_elec_vdw->data[12*i+j];
 -        }
 -    }
 -    for (i = 0; i <= nbl->table_elec_vdw->n; i++)
 -    {
 -        for (j = 0; j < 8; j++)
 -        {
 -            nbl->table_vdw->data[8*i+j] = nbl->table_elec_vdw->data[12*i+4+j];
 -        }
 -    }
 -}
 -
  /*!\brief If there's bonded interactions of type \c ftype1 or \c
   * ftype2 present in the topology, build an array of the number of
   * interactions present for each bonded interaction index found in the
   * \c ncount. It will contain zero for every bonded interaction index
   * for which no interactions are present in the topology.
   */
 -static void count_tables(int ftype1, int ftype2, const gmx_mtop_t *mtop,
 -                         int *ncount, int **count)
 +static void count_tables(int ftype1, int ftype2, const gmx_mtop_t* mtop, int* ncount, int** count)
  {
 -    int                  ftype, i, j, tabnr;
 +    int ftype, i, j, tabnr;
  
      // Loop over all moleculetypes
 -    for (const gmx_moltype_t &molt : mtop->moltype)
 +    for (const gmx_moltype_tmolt : mtop->moltype)
      {
          // Loop over all interaction types
          for (ftype = 0; ftype < F_NRE; ftype++)
              // If the current interaction type is one of the types whose tables we're trying to count...
              if (ftype == ftype1 || ftype == ftype2)
              {
 -                const InteractionList &il     = molt.ilist[ftype];
 +                const InteractionListil     = molt.ilist[ftype];
                  const int              stride = 1 + NRAL(ftype);
                  // ... and there are actually some interactions for this type
                  for (i = 0; i < il.size(); i += stride)
                      // Make room for this index in the data structure
                      if (tabnr >= *ncount)
                      {
 -                        srenew(*count, tabnr+1);
 -                        for (j = *ncount; j < tabnr+1; j++)
 +                        srenew(*count, tabnr + 1);
 +                        for (j = *ncount; j < tabnr + 1; j++)
                          {
                              (*count)[j] = 0;
                          }
 -                        *ncount = tabnr+1;
 +                        *ncount = tabnr + 1;
                      }
                      // Record that this table index is used and must have a valid file
                      (*count)[tabnr]++;
   *
   * A fatal error occurs if no matching filename is found.
   */
 -static bondedtable_t *make_bonded_tables(FILE *fplog,
 -                                         int ftype1, int ftype2,
 -                                         const gmx_mtop_t *mtop,
 +static bondedtable_t* make_bonded_tables(FILE*                            fplog,
 +                                         int                              ftype1,
 +                                         int                              ftype2,
 +                                         const gmx_mtop_t*                mtop,
                                           gmx::ArrayRef<const std::string> tabbfnm,
 -                                         const char *tabext)
 +                                         const char*                      tabext)
  {
      int            ncount, *count;
 -    bondedtable_t *tab;
 +    bondedtable_ttab;
  
      tab = nullptr;
  
                  // being recognized and used for table 1.
                  std::string patternToFind = gmx::formatString("_%s%d.%s", tabext, i, ftp2ext(efXVG));
                  bool        madeTable     = false;
 -                for (gmx::index j = 0; j < tabbfnm.size() && !madeTable; ++j)
 +                for (gmx::index j = 0; j < tabbfnm.ssize() && !madeTable; ++j)
                  {
                      if (gmx::endsWith(tabbfnm[j], patternToFind))
                      {
                          // Finally read the table from the file found
 -                        tab[i]    = make_bonded_table(fplog, tabbfnm[j].c_str(), NRAL(ftype1)-2);
 +                        tab[i]    = make_bonded_table(fplog, tabbfnm[j].c_str(), NRAL(ftype1) - 2);
                          madeTable = true;
                      }
                  }
                  if (!madeTable)
                  {
                      bool isPlural = (ftype2 != -1);
 -                    gmx_fatal(FARGS, "Tabulated interaction of type '%s%s%s' with index %d cannot be used because no table file whose name matched '%s' was passed via the gmx mdrun -tableb command-line option.",
 -                              interaction_function[ftype1].longname,
 -                              isPlural ? "' or '" : "",
 -                              isPlural ? interaction_function[ftype2].longname : "",
 -                              i,
 +                    gmx_fatal(FARGS,
 +                              "Tabulated interaction of type '%s%s%s' with index %d cannot be used "
 +                              "because no table file whose name matched '%s' was passed via the "
 +                              "gmx mdrun -tableb command-line option.",
 +                              interaction_function[ftype1].longname, isPlural ? "' or '" : "",
 +                              isPlural ? interaction_function[ftype2].longname : "", i,
                                patternToFind.c_str());
                  }
              }
      return tab;
  }
  
 -void forcerec_set_ranges(t_forcerec *fr,
 -                         int ncg_home, int ncg_force,
 -                         int natoms_force,
 -                         int natoms_force_constr, int natoms_f_novirsum)
 +void forcerec_set_ranges(t_forcerec* fr, int natoms_force, int natoms_force_constr, int natoms_f_novirsum)
  {
 -    fr->cg0 = 0;
 -    fr->hcg = ncg_home;
 -
 -    /* fr->ncg_force is unused in the standard code,
 -     * but it can be useful for modified code dealing with charge groups.
 -     */
 -    fr->ncg_force           = ncg_force;
      fr->natoms_force        = natoms_force;
      fr->natoms_force_constr = natoms_force_constr;
  
  
      if (fr->haveDirectVirialContributions)
      {
 -        fr->forceBufferForDirectVirialContributions->resize(natoms_f_novirsum);
 +        fr->forceBufferForDirectVirialContributions.resize(natoms_f_novirsum);
      }
  }
  
  static real cutoff_inf(real cutoff)
 -{
 -    if (cutoff == 0)
 -    {
 -        cutoff = GMX_CUTOFF_INF;
 -    }
 -
 -    return cutoff;
 -}
 -
 -gmx_bool can_use_allvsall(const t_inputrec *ir, gmx_bool bPrintNote, const t_commrec *cr, FILE *fp)
 -{
 -    gmx_bool bAllvsAll;
 -
 -    bAllvsAll =
 -        (
 -            ir->rlist == 0            &&
 -            ir->rcoulomb == 0         &&
 -            ir->rvdw == 0             &&
 -            ir->ePBC == epbcNONE      &&
 -            ir->vdwtype == evdwCUT    &&
 -            ir->coulombtype == eelCUT &&
 -            ir->efep == efepNO        &&
 -            getenv("GMX_NO_ALLVSALL") == nullptr
 -        );
 -
 -    if (bAllvsAll && ir->opts.ngener > 1)
 -    {
 -        const char *note = "NOTE: Can not use all-vs-all force loops, because there are multiple energy monitor groups; you might get significantly higher performance when using only a single energy monitor group.\n";
 -
 -        if (bPrintNote)
 -        {
 -            if (fp != nullptr)
 -            {
 -                fprintf(fp, "\n%s\n", note);
 -            }
 -        }
 -        bAllvsAll = FALSE;
 -    }
 -
 -    if (bAllvsAll && fp && MASTER(cr))
 -    {
 -        fprintf(fp, "\nUsing SIMD all-vs-all kernels.\n\n");
 -    }
 -
 -    return bAllvsAll;
 -}
 -
 -
 -gmx_bool nbnxn_simd_supported(const gmx::MDLogger &mdlog,
 -                              const t_inputrec    *ir)
 -{
 -    if (ir->vdwtype == evdwPME && ir->ljpme_combination_rule == eljpmeLB)
 -    {
 -        /* LJ PME with LB combination rule does 7 mesh operations.
 -         * This so slow that we don't compile SIMD non-bonded kernels
 -         * for that. */
 -        GMX_LOG(mdlog.warning).asParagraph().appendText("LJ-PME with Lorentz-Berthelot is not supported with SIMD kernels, falling back to plain C kernels");
 -        return FALSE;
 -    }
 -
 -    return TRUE;
 -}
 -
 -
 -static void pick_nbnxn_kernel_cpu(const t_inputrec gmx_unused    *ir,
 -                                  int                            *kernel_type,
 -                                  int                            *ewald_excl,
 -                                  const gmx_hw_info_t gmx_unused &hardwareInfo)
 -{
 -    *kernel_type = nbnxnk4x4_PlainC;
 -    *ewald_excl  = ewaldexclTable;
 -
 -#if GMX_SIMD
 -    {
 -#ifdef GMX_NBNXN_SIMD_4XN
 -        *kernel_type = nbnxnk4xN_SIMD_4xN;
 -#endif
 -#ifdef GMX_NBNXN_SIMD_2XNN
 -        *kernel_type = nbnxnk4xN_SIMD_2xNN;
 -#endif
 -
 -#if defined GMX_NBNXN_SIMD_2XNN && defined GMX_NBNXN_SIMD_4XN
 -        /* We need to choose if we want 2x(N+N) or 4xN kernels.
 -         * This is based on the SIMD acceleration choice and CPU information
 -         * detected at runtime.
 -         *
 -         * 4xN calculates more (zero) interactions, but has less pair-search
 -         * work and much better kernel instruction scheduling.
 -         *
 -         * Up till now we have only seen that on Intel Sandy/Ivy Bridge,
 -         * which doesn't have FMA, both the analytical and tabulated Ewald
 -         * kernels have similar pair rates for 4x8 and 2x(4+4), so we choose
 -         * 2x(4+4) because it results in significantly fewer pairs.
 -         * For RF, the raw pair rate of the 4x8 kernel is higher than 2x(4+4),
 -         * 10% with HT, 50% without HT. As we currently don't detect the actual
 -         * use of HT, use 4x8 to avoid a potential performance hit.
 -         * On Intel Haswell 4x8 is always faster.
 -         */
 -        *kernel_type = nbnxnk4xN_SIMD_4xN;
 -
 -#if !GMX_SIMD_HAVE_FMA
 -        if (EEL_PME_EWALD(ir->coulombtype) ||
 -            EVDW_PME(ir->vdwtype))
 -        {
 -            /* We have Ewald kernels without FMA (Intel Sandy/Ivy Bridge).
 -             * There are enough instructions to make 2x(4+4) efficient.
 -             */
 -            *kernel_type = nbnxnk4xN_SIMD_2xNN;
 -        }
 -#endif
 -        if (hardwareInfo.haveAmdZen1Cpu)
 -        {
 -            /* One 256-bit FMA per cycle makes 2xNN faster */
 -            *kernel_type = nbnxnk4xN_SIMD_2xNN;
 -        }
 -#endif  /* GMX_NBNXN_SIMD_2XNN && GMX_NBNXN_SIMD_4XN */
 -
 -
 -        if (getenv("GMX_NBNXN_SIMD_4XN") != nullptr)
 -        {
 -#ifdef GMX_NBNXN_SIMD_4XN
 -            *kernel_type = nbnxnk4xN_SIMD_4xN;
 -#else
 -            gmx_fatal(FARGS, "SIMD 4xN kernels requested, but GROMACS has been compiled without support for these kernels");
 -#endif
 -        }
 -        if (getenv("GMX_NBNXN_SIMD_2XNN") != nullptr)
 -        {
 -#ifdef GMX_NBNXN_SIMD_2XNN
 -            *kernel_type = nbnxnk4xN_SIMD_2xNN;
 -#else
 -            gmx_fatal(FARGS, "SIMD 2x(N+N) kernels requested, but GROMACS has been compiled without support for these kernels");
 -#endif
 -        }
 -
 -        /* Analytical Ewald exclusion correction is only an option in
 -         * the SIMD kernel.
 -         * Since table lookup's don't parallelize with SIMD, analytical
 -         * will probably always be faster for a SIMD width of 8 or more.
 -         * With FMA analytical is sometimes faster for a width if 4 as well.
 -         * In single precision, this is faster on Bulldozer.
 -         */
 -#if GMX_SIMD_REAL_WIDTH >= 8 || \
 -        (GMX_SIMD_REAL_WIDTH >= 4 && GMX_SIMD_HAVE_FMA && !GMX_DOUBLE)
 -        /* On AMD Zen, tabulated Ewald kernels are faster on all 4 combinations
 -         * of single or double precision and 128 or 256-bit AVX2.
 -         */
 -        if (!hardwareInfo.haveAmdZen1Cpu)
 -        {
 -            *ewald_excl = ewaldexclAnalytical;
 -        }
 -#endif
 -        if (getenv("GMX_NBNXN_EWALD_TABLE") != nullptr)
 -        {
 -            *ewald_excl = ewaldexclTable;
 -        }
 -        if (getenv("GMX_NBNXN_EWALD_ANALYTICAL") != nullptr)
 -        {
 -            *ewald_excl = ewaldexclAnalytical;
 -        }
 -
 -    }
 -#endif // GMX_SIMD
 -}
 -
 -
 -const char *lookup_nbnxn_kernel_name(int kernel_type)
 -{
 -    const char *returnvalue = nullptr;
 -    switch (kernel_type)
 -    {
 -        case nbnxnkNotSet:
 -            returnvalue = "not set";
 -            break;
 -        case nbnxnk4x4_PlainC:
 -            returnvalue = "plain C";
 -            break;
 -        case nbnxnk4xN_SIMD_4xN:
 -        case nbnxnk4xN_SIMD_2xNN:
 -#if GMX_SIMD
 -            returnvalue = "SIMD";
 -#else  // GMX_SIMD
 -            returnvalue = "not available";
 -#endif // GMX_SIMD
 -            break;
 -        case nbnxnk8x8x8_GPU: returnvalue    = "GPU"; break;
 -        case nbnxnk8x8x8_PlainC: returnvalue = "plain C"; break;
 -
 -        case nbnxnkNR:
 -        default:
 -            gmx_fatal(FARGS, "Illegal kernel type selected");
 -    }
 -    return returnvalue;
 -};
 -
 -static void pick_nbnxn_kernel(const gmx::MDLogger &mdlog,
 -                              gmx_bool             use_simd_kernels,
 -                              const gmx_hw_info_t &hardwareInfo,
 -                              gmx_bool             bUseGPU,
 -                              EmulateGpuNonbonded  emulateGpu,
 -                              const t_inputrec    *ir,
 -                              int                 *kernel_type,
 -                              int                 *ewald_excl,
 -                              gmx_bool             bDoNonbonded)
 -{
 -    assert(kernel_type);
 -
 -    *kernel_type = nbnxnkNotSet;
 -    *ewald_excl  = ewaldexclTable;
 -
 -    if (emulateGpu == EmulateGpuNonbonded::Yes)
 -    {
 -        *kernel_type = nbnxnk8x8x8_PlainC;
 -
 -        if (bDoNonbonded)
 -        {
 -            GMX_LOG(mdlog.warning).asParagraph().appendText("Emulating a GPU run on the CPU (slow)");
 -        }
 -    }
 -    else if (bUseGPU)
 -    {
 -        *kernel_type = nbnxnk8x8x8_GPU;
 -    }
 -
 -    if (*kernel_type == nbnxnkNotSet)
 -    {
 -        if (use_simd_kernels &&
 -            nbnxn_simd_supported(mdlog, ir))
 -        {
 -            pick_nbnxn_kernel_cpu(ir, kernel_type, ewald_excl, hardwareInfo);
 -        }
 -        else
 -        {
 -            *kernel_type = nbnxnk4x4_PlainC;
 -        }
 -    }
 -
 -    if (bDoNonbonded)
 +{
 +    if (cutoff == 0)
      {
 -        GMX_LOG(mdlog.info).asParagraph().appendTextFormatted(
 -                "Using %s %dx%d nonbonded short-range kernels",
 -                lookup_nbnxn_kernel_name(*kernel_type),
 -                nbnxn_kernel_to_cluster_i_size(*kernel_type),
 -                nbnxn_kernel_to_cluster_j_size(*kernel_type));
 -
 -        if (nbnxnk4x4_PlainC == *kernel_type ||
 -            nbnxnk8x8x8_PlainC == *kernel_type)
 -        {
 -            GMX_LOG(mdlog.warning).asParagraph().appendTextFormatted(
 -                    "WARNING: Using the slow %s kernels. This should\n"
 -                    "not happen during routine usage on supported platforms.",
 -                    lookup_nbnxn_kernel_name(*kernel_type));
 -        }
 +        cutoff = GMX_CUTOFF_INF;
      }
 +
 +    return cutoff;
  }
  
  /*! \brief Print Coulomb Ewald citations and set ewald coefficients */
 -static void initCoulombEwaldParameters(FILE *fp, const t_inputrec *ir,
 -                                       bool systemHasNetCharge,
 -                                       interaction_const_t *ic)
 +static void initCoulombEwaldParameters(FILE*                fp,
 +                                       const t_inputrec*    ir,
 +                                       bool                 systemHasNetCharge,
 +                                       interaction_const_t* ic)
  {
      if (!EEL_PME_EWALD(ir->coulombtype))
      {
      ic->ewaldcoeff_q = calc_ewaldcoeff_q(ir->rcoulomb, ir->ewald_rtol);
      if (fp)
      {
 -        fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for Ewald\n",
 -                1/ic->ewaldcoeff_q);
 +        fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for Ewald\n", 1 / ic->ewaldcoeff_q);
      }
  
      if (ic->coulomb_modifier == eintmodPOTSHIFT)
      {
          GMX_RELEASE_ASSERT(ic->rcoulomb != 0, "Cutoff radius cannot be zero");
 -        ic->sh_ewald = std::erfc(ic->ewaldcoeff_q*ic->rcoulomb) / ic->rcoulomb;
 +        ic->sh_ewald = std::erfc(ic->ewaldcoeff_q * ic->rcoulomb) / ic->rcoulomb;
      }
      else
      {
  }
  
  /*! \brief Print Van der Waals Ewald citations and set ewald coefficients */
 -static void initVdwEwaldParameters(FILE *fp, const t_inputrec *ir,
 -                                   interaction_const_t *ic)
 +static void initVdwEwaldParameters(FILE* fp, const t_inputrec* ir, interaction_const_t* ic)
  {
      if (!EVDW_PME(ir->vdwtype))
      {
      ic->ewaldcoeff_lj = calc_ewaldcoeff_lj(ir->rvdw, ir->ewald_rtol_lj);
      if (fp)
      {
 -        fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for LJ Ewald\n",
 -                1/ic->ewaldcoeff_lj);
 +        fprintf(fp, "Using a Gaussian width (1/beta) of %g nm for LJ Ewald\n", 1 / ic->ewaldcoeff_lj);
      }
  
      if (ic->vdw_modifier == eintmodPOTSHIFT)
      {
 -        real crc2       = gmx::square(ic->ewaldcoeff_lj*ic->rvdw);
 -        ic->sh_lj_ewald = (std::exp(-crc2)*(1 + crc2 + 0.5*crc2*crc2) - 1)/gmx::power6(ic->rvdw);
 +        real crc2       = gmx::square(ic->ewaldcoeff_lj * ic->rvdw);
 +        ic->sh_lj_ewald = (std::exp(-crc2) * (1 + crc2 + 0.5 * crc2 * crc2) - 1) / gmx::power6(ic->rvdw);
      }
      else
      {
      }
  }
  
 -gmx_bool uses_simple_tables(int                 cutoff_scheme,
 -                            nonbonded_verlet_t *nbv,
 -                            int                 group)
 -{
 -    gmx_bool bUsesSimpleTables = TRUE;
 -    int      grp_index;
 -
 -    switch (cutoff_scheme)
 -    {
 -        case ecutsGROUP:
 -            bUsesSimpleTables = TRUE;
 -            break;
 -        case ecutsVERLET:
 -            assert(nullptr != nbv);
 -            grp_index         = (group < 0) ? 0 : (nbv->ngrp - 1);
 -            bUsesSimpleTables = nbnxn_kernel_pairlist_simple(nbv->grp[grp_index].kernel_type);
 -            break;
 -        default:
 -            gmx_incons("unimplemented");
 -    }
 -    return bUsesSimpleTables;
 -}
 -
 -static void init_ewald_f_table(interaction_const_t *ic,
 -                               real                 rtab)
 +/* Generate Coulomb and/or Van der Waals Ewald long-range correction tables
 + *
 + * Tables are generated for one or both, depending on if the pointers
 + * are non-null. The spacing for both table sets is the same and obeys
 + * both accuracy requirements, when relevant.
 + */
 +static void init_ewald_f_table(const interaction_const_t& ic,
 +                               EwaldCorrectionTables*     coulombTables,
 +                               EwaldCorrectionTables*     vdwTables)
  {
 -    real maxr;
 +    const bool useCoulombTable = (EEL_PME_EWALD(ic.eeltype) && coulombTables != nullptr);
 +    const bool useVdwTable     = (EVDW_PME(ic.vdwtype) && vdwTables != nullptr);
  
      /* Get the Ewald table spacing based on Coulomb and/or LJ
       * Ewald coefficients and rtol.
       */
 -    ic->tabq_scale = ewald_spline3_table_scale(ic);
 -
 -    if (ic->cutoff_scheme == ecutsVERLET)
 -    {
 -        maxr = ic->rcoulomb;
 -    }
 -    else
 -    {
 -        maxr = std::max(ic->rcoulomb, rtab);
 -    }
 -    ic->tabq_size  = static_cast<int>(maxr*ic->tabq_scale) + 2;
 -
 -    sfree_aligned(ic->tabq_coul_FDV0);
 -    sfree_aligned(ic->tabq_coul_F);
 -    sfree_aligned(ic->tabq_coul_V);
 +    const real tableScale = ewald_spline3_table_scale(ic, useCoulombTable, useVdwTable);
  
 -    sfree_aligned(ic->tabq_vdw_FDV0);
 -    sfree_aligned(ic->tabq_vdw_F);
 -    sfree_aligned(ic->tabq_vdw_V);
 +    const int tableSize = static_cast<int>(ic.rcoulomb * tableScale) + 2;
  
 -    if (EEL_PME_EWALD(ic->eeltype))
 +    if (useCoulombTable)
      {
 -        /* Create the original table data in FDV0 */
 -        snew_aligned(ic->tabq_coul_FDV0, ic->tabq_size*4, 32);
 -        snew_aligned(ic->tabq_coul_F, ic->tabq_size, 32);
 -        snew_aligned(ic->tabq_coul_V, ic->tabq_size, 32);
 -        table_spline3_fill_ewald_lr(ic->tabq_coul_F, ic->tabq_coul_V, ic->tabq_coul_FDV0,
 -                                    ic->tabq_size, 1/ic->tabq_scale, ic->ewaldcoeff_q, v_q_ewald_lr);
 +        *coulombTables =
 +                generateEwaldCorrectionTables(tableSize, tableScale, ic.ewaldcoeff_q, v_q_ewald_lr);
      }
  
 -    if (EVDW_PME(ic->vdwtype))
 +    if (useVdwTable)
      {
 -        snew_aligned(ic->tabq_vdw_FDV0, ic->tabq_size*4, 32);
 -        snew_aligned(ic->tabq_vdw_F, ic->tabq_size, 32);
 -        snew_aligned(ic->tabq_vdw_V, ic->tabq_size, 32);
 -        table_spline3_fill_ewald_lr(ic->tabq_vdw_F, ic->tabq_vdw_V, ic->tabq_vdw_FDV0,
 -                                    ic->tabq_size, 1/ic->tabq_scale, ic->ewaldcoeff_lj, v_lj_ewald_lr);
 +        *vdwTables = generateEwaldCorrectionTables(tableSize, tableScale, ic.ewaldcoeff_lj, v_lj_ewald_lr);
      }
  }
  
 -void init_interaction_const_tables(FILE                *fp,
 -                                   interaction_const_t *ic,
 -                                   real                 rtab)
 +void init_interaction_const_tables(FILE* fp, interaction_const_t* ic)
  {
 -    if (EEL_PME_EWALD(ic->eeltype) || EVDW_PME(ic->vdwtype))
 +    if (EEL_PME_EWALD(ic->eeltype))
      {
 -        init_ewald_f_table(ic, rtab);
 -
 +        init_ewald_f_table(*ic, ic->coulombEwaldTables.get(), nullptr);
          if (fp != nullptr)
          {
 -            fprintf(fp, "Initialized non-bonded Ewald correction tables, spacing: %.2e size: %d\n\n",
 -                    1/ic->tabq_scale, ic->tabq_size);
 +            fprintf(fp, "Initialized non-bonded Coulomb Ewald tables, spacing: %.2e size: %zu\n\n",
 +                    1 / ic->coulombEwaldTables->scale, ic->coulombEwaldTables->tableF.size());
          }
      }
  }
  
 -static void clear_force_switch_constants(shift_consts_t *sc)
 +static void clear_force_switch_constants(shift_consts_tsc)
  {
      sc->c2   = 0;
      sc->c3   = 0;
      sc->cpot = 0;
  }
  
 -static void force_switch_constants(real p,
 -                                   real rsw, real rc,
 -                                   shift_consts_t *sc)
 +static void force_switch_constants(real p, real rsw, real rc, shift_consts_t* sc)
  {
      /* Here we determine the coefficient for shifting the force to zero
       * between distance rsw and the cut-off rc.
       * force/p   = r^-(p+1) + c2*r^2 + c3*r^3
       * potential = r^-p + c2/3*r^3 + c3/4*r^4 + cpot
       */
 -    sc->c2   =  ((p + 1)*rsw - (p + 4)*rc)/(pow(rc, p + 2)*gmx::square(rc - rsw));
 -    sc->c3   = -((p + 1)*rsw - (p + 3)*rc)/(pow(rc, p + 2)*gmx::power3(rc - rsw));
 -    sc->cpot = -pow(rc, -p) + p*sc->c2/3*gmx::power3(rc - rsw) + p*sc->c3/4*gmx::power4(rc - rsw);
 +    sc->c2   = ((p + 1) * rsw - (p + 4) * rc) / (pow(rc, p + 2) * gmx::square(rc - rsw));
 +    sc->c3   = -((p + 1) * rsw - (p + 3) * rc) / (pow(rc, p + 2) * gmx::power3(rc - rsw));
 +    sc->cpot = -pow(rc, -p) + p * sc->c2 / 3 * gmx::power3(rc - rsw)
 +               + p * sc->c3 / 4 * gmx::power4(rc - rsw);
  }
  
 -static void potential_switch_constants(real rsw, real rc,
 -                                       switch_consts_t *sc)
 +static void potential_switch_constants(real rsw, real rc, switch_consts_t* sc)
  {
      /* The switch function is 1 at rsw and 0 at rc.
       * The derivative and second derivate are zero at both ends.
       * force      = force*dsw - potential*sw
       * potential *= sw
       */
 -    sc->c3 = -10/gmx::power3(rc - rsw);
 -    sc->c4 =  15/gmx::power4(rc - rsw);
 -    sc->c5 =  -6/gmx::power5(rc - rsw);
 +    sc->c3 = -10 / gmx::power3(rc - rsw);
 +    sc->c4 = 15 / gmx::power4(rc - rsw);
 +    sc->c5 = -6 / gmx::power5(rc - rsw);
  }
  
  /*! \brief Construct interaction constants
   * short-range interactions. Many of these are constant for the whole
   * simulation; some are constant only after PME tuning completes.
   */
 -static void
 -init_interaction_const(FILE                       *fp,
 -                       interaction_const_t       **interaction_const,
 -                       const t_inputrec           *ir,
 -                       const gmx_mtop_t           *mtop,
 -                       bool                        systemHasNetCharge)
 +static void init_interaction_const(FILE*                 fp,
 +                                   interaction_const_t** interaction_const,
 +                                   const t_inputrec*     ir,
 +                                   const gmx_mtop_t*     mtop,
 +                                   bool                  systemHasNetCharge)
  {
 -    interaction_const_t *ic;
 -
 -    snew(ic, 1);
 +    interaction_const_t* ic = new interaction_const_t;
  
 -    ic->cutoff_scheme   = ir->cutoff_scheme;
 +    ic->cutoff_scheme = ir->cutoff_scheme;
  
 -    /* Just allocate something so we can free it */
 -    snew_aligned(ic->tabq_coul_FDV0, 16, 32);
 -    snew_aligned(ic->tabq_coul_F, 16, 32);
 -    snew_aligned(ic->tabq_coul_V, 16, 32);
 +    ic->coulombEwaldTables = std::make_unique<EwaldCorrectionTables>();
  
      /* Lennard-Jones */
      ic->vdwtype         = ir->vdwtype;
      {
          case eintmodPOTSHIFT:
              /* Only shift the potential, don't touch the force */
 -            ic->dispersion_shift.cpot = -1.0/gmx::power6(ic->rvdw);
 -            ic->repulsion_shift.cpot  = -1.0/gmx::power12(ic->rvdw);
 +            ic->dispersion_shift.cpot = -1.0 / gmx::power6(ic->rvdw);
 +            ic->repulsion_shift.cpot  = -1.0 / gmx::power12(ic->rvdw);
              break;
          case eintmodFORCESWITCH:
              /* Switch the force, switch and shift the potential */
 -            force_switch_constants(6.0, ic->rvdw_switch, ic->rvdw,
 -                                   &ic->dispersion_shift);
 -            force_switch_constants(12.0, ic->rvdw_switch, ic->rvdw,
 -                                   &ic->repulsion_shift);
 +            force_switch_constants(6.0, ic->rvdw_switch, ic->rvdw, &ic->dispersion_shift);
 +            force_switch_constants(12.0, ic->rvdw_switch, ic->rvdw, &ic->repulsion_shift);
              break;
          case eintmodPOTSWITCH:
              /* Switch the potential and force */
 -            potential_switch_constants(ic->rvdw_switch, ic->rvdw,
 -                                       &ic->vdw_switch);
 +            potential_switch_constants(ic->rvdw_switch, ic->rvdw, &ic->vdw_switch);
              break;
          case eintmodNONE:
          case eintmodEXACTCUTOFF:
              /* Nothing to do here */
              break;
 -        default:
 -            gmx_incons("unimplemented potential modifier");
 +        default: gmx_incons("unimplemented potential modifier");
      }
  
 -    ic->sh_invrc6 = -ic->dispersion_shift.cpot;
 -
      /* Electrostatics */
      ic->eeltype          = ir->coulombtype;
      ic->coulomb_modifier = ir->coulomb_modifier;
      /* Set the Coulomb energy conversion factor */
      if (ic->epsilon_r != 0)
      {
 -        ic->epsfac = ONE_4PI_EPS0/ic->epsilon_r;
 +        ic->epsfac = ONE_4PI_EPS0 / ic->epsilon_r;
      }
      else
      {
      /* Reaction-field */
      if (EEL_RF(ic->eeltype))
      {
 +        GMX_RELEASE_ASSERT(ic->eeltype != eelGRF_NOTUSED, "GRF is no longer supported");
          ic->epsilon_rf = ir->epsilon_rf;
 -        /* Generalized reaction field parameters are updated every step */
 -        if (ic->eeltype != eelGRF)
 -        {
 -            calc_rffac(fp, ic->eeltype, ic->epsilon_r, ic->epsilon_rf,
 -                       ic->rcoulomb, 0, 0, nullptr,
 -                       &ic->k_rf, &ic->c_rf);
 -        }
  
 -        if (ir->cutoff_scheme == ecutsGROUP && ic->eeltype == eelRF_ZERO)
 -        {
 -            /* grompp should have done this, but this scheme is obsolete */
 -            ic->coulomb_modifier = eintmodEXACTCUTOFF;
 -        }
 +        calc_rffac(fp, ic->epsilon_r, ic->epsilon_rf, ic->rcoulomb, &ic->k_rf, &ic->c_rf);
      }
      else
      {
          ic->k_rf       = 0;
          if (ir->coulomb_modifier == eintmodPOTSHIFT)
          {
 -            ic->c_rf   = 1/ic->rcoulomb;
 +            ic->c_rf = 1 / ic->rcoulomb;
          }
          else
          {
 -            ic->c_rf   = 0;
 +            ic->c_rf = 0;
          }
      }
  
          {
              dispersion_shift -= ic->sh_lj_ewald;
          }
 -        fprintf(fp, "Potential shift: LJ r^-12: %.3e r^-6: %.3e",
 -                ic->repulsion_shift.cpot, dispersion_shift);
 +        fprintf(fp, "Potential shift: LJ r^-12: %.3e r^-6: %.3e", ic->repulsion_shift.cpot, dispersion_shift);
  
          if (ic->eeltype == eelCUT)
          {
      *interaction_const = ic;
  }
  
 -static void
 -done_interaction_const(interaction_const_t *interaction_const)
 -{
 -    sfree_aligned(interaction_const->tabq_coul_FDV0);
 -    sfree_aligned(interaction_const->tabq_coul_F);
 -    sfree_aligned(interaction_const->tabq_coul_V);
 -    sfree(interaction_const);
 -}
 -
 -static void init_nb_verlet(const gmx::MDLogger     &mdlog,
 -                           nonbonded_verlet_t     **nb_verlet,
 -                           gmx_bool                 bFEP_NonBonded,
 -                           const t_inputrec        *ir,
 -                           const t_forcerec        *fr,
 -                           const t_commrec         *cr,
 -                           const gmx_hw_info_t     &hardwareInfo,
 -                           const gmx_device_info_t *deviceInfo,
 -                           const gmx_mtop_t        *mtop,
 -                           matrix                   box)
 -{
 -    nonbonded_verlet_t *nbv;
 -    char               *env;
 -
 -    nbnxn_alloc_t      *nb_alloc;
 -    nbnxn_free_t       *nb_free;
 -
 -    nbv = new nonbonded_verlet_t();
 -
 -    nbv->emulateGpu = ((getenv("GMX_EMULATE_GPU") != nullptr) ? EmulateGpuNonbonded::Yes : EmulateGpuNonbonded::No);
 -    nbv->bUseGPU    = deviceInfo != nullptr;
 -
 -    GMX_RELEASE_ASSERT(!(nbv->emulateGpu == EmulateGpuNonbonded::Yes && nbv->bUseGPU), "When GPU emulation is active, there cannot be a GPU assignment");
 -
 -    nbv->nbs             = nullptr;
 -    nbv->min_ci_balanced = 0;
 -
 -    nbv->ngrp = (DOMAINDECOMP(cr) ? 2 : 1);
 -    for (int i = 0; i < nbv->ngrp; i++)
 -    {
 -        nbv->grp[i].nbl_lists.nnbl = 0;
 -        nbv->grp[i].kernel_type    = nbnxnkNotSet;
 -
 -        if (i == 0) /* local */
 -        {
 -            pick_nbnxn_kernel(mdlog, fr->use_simd_kernels, hardwareInfo,
 -                              nbv->bUseGPU, nbv->emulateGpu, ir,
 -                              &nbv->grp[i].kernel_type,
 -                              &nbv->grp[i].ewald_excl,
 -                              fr->bNonbonded);
 -        }
 -        else /* non-local */
 -        {
 -            /* Use the same kernel for local and non-local interactions */
 -            nbv->grp[i].kernel_type = nbv->grp[0].kernel_type;
 -            nbv->grp[i].ewald_excl  = nbv->grp[0].ewald_excl;
 -        }
 -    }
 -
 -    nbv->listParams = gmx::compat::make_unique<NbnxnListParameters>(ir->rlist);
 -    setupDynamicPairlistPruning(mdlog, ir, mtop, box, nbv->grp[0].kernel_type, fr->ic,
 -                                nbv->listParams.get());
 -
 -    nbv->nbs = gmx::compat::make_unique<nbnxn_search>(DOMAINDECOMP(cr) ? &cr->dd->nc : nullptr,
 -                                                      DOMAINDECOMP(cr) ? domdec_zones(cr->dd) : nullptr,
 -                                                      bFEP_NonBonded,
 -                                                      gmx_omp_nthreads_get(emntPairsearch));
 -
 -    gpu_set_host_malloc_and_free(nbv->grp[0].kernel_type == nbnxnk8x8x8_GPU,
 -                                 &nb_alloc, &nb_free);
 -
 -    for (int i = 0; i < nbv->ngrp; i++)
 -    {
 -        nbnxn_init_pairlist_set(&nbv->grp[i].nbl_lists,
 -                                nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type),
 -                                /* 8x8x8 "non-simple" lists are ATM always combined */
 -                                !nbnxn_kernel_pairlist_simple(nbv->grp[i].kernel_type),
 -                                nb_alloc, nb_free);
 -    }
 -
 -    int      enbnxninitcombrule;
 -    if (fr->ic->vdwtype == evdwCUT &&
 -        (fr->ic->vdw_modifier == eintmodNONE ||
 -         fr->ic->vdw_modifier == eintmodPOTSHIFT) &&
 -        getenv("GMX_NO_LJ_COMB_RULE") == nullptr)
 -    {
 -        /* Plain LJ cut-off: we can optimize with combination rules */
 -        enbnxninitcombrule = enbnxninitcombruleDETECT;
 -    }
 -    else if (fr->ic->vdwtype == evdwPME)
 -    {
 -        /* LJ-PME: we need to use a combination rule for the grid */
 -        if (fr->ljpme_combination_rule == eljpmeGEOM)
 -        {
 -            enbnxninitcombrule = enbnxninitcombruleGEOM;
 -        }
 -        else
 -        {
 -            enbnxninitcombrule = enbnxninitcombruleLB;
 -        }
 -    }
 -    else
 -    {
 -        /* We use a full combination matrix: no rule required */
 -        enbnxninitcombrule = enbnxninitcombruleNONE;
 -    }
 -
 -    snew(nbv->nbat, 1);
 -    int mimimumNumEnergyGroupNonbonded = ir->opts.ngener;
 -    if (ir->opts.ngener - ir->nwall == 1)
 -    {
 -        /* We have only one non-wall energy group, we do not need energy group
 -         * support in the non-bondeds kernels, since all non-bonded energy
 -         * contributions go to the first element of the energy group matrix.
 -         */
 -        mimimumNumEnergyGroupNonbonded = 1;
 -    }
 -    bool bSimpleList = nbnxn_kernel_pairlist_simple(nbv->grp[0].kernel_type);
 -    nbnxn_atomdata_init(mdlog,
 -                        nbv->nbat,
 -                        nbv->grp[0].kernel_type,
 -                        enbnxninitcombrule,
 -                        fr->ntype, fr->nbfp,
 -                        mimimumNumEnergyGroupNonbonded,
 -                        bSimpleList ? gmx_omp_nthreads_get(emntNonbonded) : 1,
 -                        nb_alloc, nb_free);
 -
 -    if (nbv->bUseGPU)
 -    {
 -        /* init the NxN GPU data; the last argument tells whether we'll have
 -         * both local and non-local NB calculation on GPU */
 -        nbnxn_gpu_init(&nbv->gpu_nbv,
 -                       deviceInfo,
 -                       fr->ic,
 -                       nbv->listParams.get(),
 -                       nbv->nbat,
 -                       cr->nodeid,
 -                       (nbv->ngrp > 1));
 -
 -        if ((env = getenv("GMX_NB_MIN_CI")) != nullptr)
 -        {
 -            char *end;
 -
 -            nbv->min_ci_balanced = strtol(env, &end, 10);
 -            if (!end || (*end != 0) || nbv->min_ci_balanced < 0)
 -            {
 -                gmx_fatal(FARGS, "Invalid value passed in GMX_NB_MIN_CI=%s, non-negative integer required", env);
 -            }
 -
 -            if (debug)
 -            {
 -                fprintf(debug, "Neighbor-list balancing parameter: %d (passed as env. var.)\n",
 -                        nbv->min_ci_balanced);
 -            }
 -        }
 -        else
 -        {
 -            nbv->min_ci_balanced = nbnxn_gpu_min_ci_balanced(nbv->gpu_nbv);
 -            if (debug)
 -            {
 -                fprintf(debug, "Neighbor-list balancing parameter: %d (auto-adjusted to the number of GPU multi-processors)\n",
 -                        nbv->min_ci_balanced);
 -            }
 -        }
 -
 -    }
 -
 -    *nb_verlet = nbv;
 -}
 -
 -gmx_bool usingGpu(nonbonded_verlet_t *nbv)
 -{
 -    return nbv != nullptr && nbv->bUseGPU;
 -}
 -
 -void init_forcerec(FILE                             *fp,
 -                   const gmx::MDLogger              &mdlog,
 -                   t_forcerec                       *fr,
 -                   t_fcdata                         *fcd,
 -                   const t_inputrec                 *ir,
 -                   const gmx_mtop_t                 *mtop,
 -                   const t_commrec                  *cr,
 -                   matrix                            box,
 -                   const char                       *tabfn,
 -                   const char                       *tabpfn,
 -                   gmx::ArrayRef<const std::string>  tabbfnm,
 -                   const gmx_hw_info_t              &hardwareInfo,
 -                   const gmx_device_info_t          *deviceInfo,
 -                   const bool                        useGpuForBonded,
 -                   gmx_bool                          bNoSolvOpt,
 -                   real                              print_force)
 +void init_forcerec(FILE*                            fp,
 +                   const gmx::MDLogger&             mdlog,
 +                   t_forcerec*                      fr,
 +                   t_fcdata*                        fcd,
 +                   const t_inputrec*                ir,
 +                   const gmx_mtop_t*                mtop,
 +                   const t_commrec*                 cr,
 +                   matrix                           box,
 +                   const char*                      tabfn,
 +                   const char*                      tabpfn,
 +                   gmx::ArrayRef<const std::string> tabbfnm,
 +                   const gmx_hw_info_t&             hardwareInfo,
 +                   const gmx_device_info_t*         deviceInfo,
 +                   const bool                       useGpuForBonded,
 +                   const bool                       pmeOnlyRankUsesGpu,
 +                   real                             print_force,
 +                   gmx_wallcycle*                   wcycle)
  {
 -    int            m, negp_pp, negptable, egi, egj;
 -    real           rtab;
 -    char          *env;
 -    double         dbl;
 -    const t_block *cgs;
 -    gmx_bool       bGenericKernelOnly;
 -    gmx_bool       needGroupSchemeTables, bSomeNormalNbListsAreInUse;
 -    gmx_bool       bFEP_NonBonded;
 -    int           *nm_ind, egp_flags;
 +    real     rtab;
 +    char*    env;
 +    double   dbl;
 +    gmx_bool bFEP_NonBonded;
  
      /* By default we turn SIMD kernels on, but it might be turned off further down... */
      fr->use_simd_kernels = TRUE;
      if (EI_TPI(ir->eI))
      {
          /* Set to the size of the molecule to be inserted (the last one) */
 -        /* Because of old style topologies, we have to use the last cg
 -         * instead of the last molecule type.
 -         */
 -        cgs       = &mtop->moltype[mtop->molblock.back().type].cgs;
 -        fr->n_tpi = cgs->index[cgs->nr] - cgs->index[cgs->nr-1];
          gmx::RangePartitioning molecules = gmx_mtop_molecules(*mtop);
 -        if (fr->n_tpi != molecules.block(molecules.numBlocks() - 1).size())
 -        {
 -            gmx_fatal(FARGS, "The molecule to insert can not consist of multiple charge groups.\nMake it a single charge group.");
 -        }
 +        fr->n_tpi                        = molecules.block(molecules.numBlocks() - 1).size();
      }
      else
      {
          fr->n_tpi = 0;
      }
  
 -    if (ir->coulombtype == eelRF_NEC_UNSUPPORTED)
 +    if (ir->coulombtype == eelRF_NEC_UNSUPPORTED || ir->coulombtype == eelGRF_NOTUSED)
      {
 -        gmx_fatal(FARGS, "%s electrostatics is no longer supported",
 -                  eel_names[ir->coulombtype]);
 +        gmx_fatal(FARGS, "%s electrostatics is no longer supported", eel_names[ir->coulombtype]);
      }
  
      if (ir->bAdress)
      {
          /* turn off non-bonded calculations */
          fr->bNonbonded = FALSE;
 -        GMX_LOG(mdlog.warning).asParagraph().appendText(
 -                "Found environment variable GMX_NO_NONBONDED.\n"
 -                "Disabling nonbonded calculations.");
 -    }
 -
 -    bGenericKernelOnly = FALSE;
 -
 -    /* We now check in the NS code whether a particular combination of interactions
 -     * can be used with water optimization, and disable it if that is not the case.
 -     */
 -
 -    if (getenv("GMX_NB_GENERIC") != nullptr)
 -    {
 -        if (fp != nullptr)
 -        {
 -            fprintf(fp,
 -                    "Found environment variable GMX_NB_GENERIC.\n"
 -                    "Disabling all interaction-specific nonbonded kernels, will only\n"
 -                    "use the slow generic ones in src/gmxlib/nonbonded/nb_generic.c\n\n");
 -        }
 -        bGenericKernelOnly = TRUE;
 -    }
 -
 -    if (bGenericKernelOnly)
 -    {
 -        bNoSolvOpt         = TRUE;
 +        GMX_LOG(mdlog.warning)
 +                .asParagraph()
 +                .appendText(
 +                        "Found environment variable GMX_NO_NONBONDED.\n"
 +                        "Disabling nonbonded calculations.");
      }
  
 -    if ( (getenv("GMX_DISABLE_SIMD_KERNELS") != nullptr) || (getenv("GMX_NOOPTIMIZEDKERNELS") != nullptr) )
 +    if ((getenv("GMX_DISABLE_SIMD_KERNELS") != nullptr) || (getenv("GMX_NOOPTIMIZEDKERNELS") != nullptr))
      {
          fr->use_simd_kernels = FALSE;
          if (fp != nullptr)
  
      fr->bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
  
 -    /* Check if we can/should do all-vs-all kernels */
 -    fr->bAllvsAll       = can_use_allvsall(ir, FALSE, nullptr, nullptr);
 -    fr->AllvsAll_work   = nullptr;
 -
 -    /* All-vs-all kernels have not been implemented in 4.6 and later.
 -     * See Redmine #1249. */
 -    if (fr->bAllvsAll)
 -    {
 -        fr->bAllvsAll            = FALSE;
 -        if (fp != nullptr)
 -        {
 -            fprintf(fp,
 -                    "\nYour simulation settings would have triggered the efficient all-vs-all\n"
 -                    "kernels in GROMACS 4.5, but these have not been implemented in GROMACS\n"
 -                    "4.6 and 5.x. If performance is important, please use GROMACS 4.5.7\n"
 -                    "or try cutoff-scheme = Verlet.\n\n");
 -        }
 -    }
 -
      /* Neighbour searching stuff */
      fr->cutoff_scheme = ir->cutoff_scheme;
 -    fr->bGrid         = (ir->ns_type == ensGRID);
      fr->ePBC          = ir->ePBC;
  
 -    if (fr->cutoff_scheme == ecutsGROUP)
 -    {
 -        const char *note = "NOTE: This file uses the deprecated 'group' cutoff_scheme. This will be\n"
 -            "removed in a future release when 'verlet' supports all interaction forms.\n";
 -
 -        if (MASTER(cr))
 -        {
 -            fprintf(stderr, "\n%s\n", note);
 -        }
 -        if (fp != nullptr)
 -        {
 -            fprintf(fp, "\n%s\n", note);
 -        }
 -    }
 -
      /* Determine if we will do PBC for distances in bonded interactions */
      if (fr->ePBC == epbcNONE)
      {
      }
      else
      {
 +        const bool useEwaldSurfaceCorrection =
 +                (EEL_PME_EWALD(ir->coulombtype) && ir->epsilon_surface != 0);
          if (!DOMAINDECOMP(cr))
          {
              gmx_bool bSHAKE;
  
 -            bSHAKE = (ir->eConstrAlg == econtSHAKE &&
 -                      (gmx_mtop_ftype_count(mtop, F_CONSTR) > 0 ||
 -                       gmx_mtop_ftype_count(mtop, F_CONSTRNC) > 0));
 +            bSHAKE = (ir->eConstrAlg == econtSHAKE
 +                      && (gmx_mtop_ftype_count(mtop, F_CONSTR) > 0
 +                          || gmx_mtop_ftype_count(mtop, F_CONSTRNC) > 0));
  
              /* The group cut-off scheme and SHAKE assume charge groups
               * are whole, but not using molpbc is faster in most cases.
               * With intermolecular interactions we need PBC for calculating
               * distances between atoms in different molecules.
               */
 -            if ((fr->cutoff_scheme == ecutsGROUP || bSHAKE) &&
 -                !mtop->bIntermolecularInteractions)
 +            if (bSHAKE && !mtop->bIntermolecularInteractions)
              {
                  fr->bMolPBC = ir->bPeriodicMols;
  
              else
              {
                  /* Not making molecules whole is faster in most cases,
 -                 * but With orientation restraints we need whole molecules.
 +                 * but with orientation restraints or non-tinfoil boundary
 +                 * conditions we need whole molecules.
                   */
 -                fr->bMolPBC = (fcd->orires.nr == 0);
 +                fr->bMolPBC = (fcd->orires.nr == 0 && !useEwaldSurfaceCorrection);
  
                  if (getenv("GMX_USE_GRAPH") != nullptr)
                  {
                      fr->bMolPBC = FALSE;
                      if (fp)
                      {
 -                        GMX_LOG(mdlog.warning).asParagraph().appendText("GMX_USE_GRAPH is set, using the graph for bonded interactions");
 +                        GMX_LOG(mdlog.warning)
 +                                .asParagraph()
 +                                .appendText(
 +                                        "GMX_USE_GRAPH is set, using the graph for bonded "
 +                                        "interactions");
                      }
  
                      if (mtop->bIntermolecularInteractions)
                      {
 -                        GMX_LOG(mdlog.warning).asParagraph().appendText("WARNING: Molecules linked by intermolecular interactions have to reside in the same periodic image, otherwise artifacts will occur!");
 +                        GMX_LOG(mdlog.warning)
 +                                .asParagraph()
 +                                .appendText(
 +                                        "WARNING: Molecules linked by intermolecular interactions "
 +                                        "have to reside in the same periodic image, otherwise "
 +                                        "artifacts will occur!");
                      }
                  }
  
 -                GMX_RELEASE_ASSERT(fr->bMolPBC || !mtop->bIntermolecularInteractions, "We need to use PBC within molecules with inter-molecular interactions");
 +                GMX_RELEASE_ASSERT(
 +                        fr->bMolPBC || !mtop->bIntermolecularInteractions,
 +                        "We need to use PBC within molecules with inter-molecular interactions");
  
                  if (bSHAKE && fr->bMolPBC)
                  {
 -                    gmx_fatal(FARGS, "SHAKE is not properly supported with intermolecular interactions. For short simulations where linked molecules remain in the same periodic image, the environment variable GMX_USE_GRAPH can be used to override this check.\n");
 +                    gmx_fatal(FARGS,
 +                              "SHAKE is not properly supported with intermolecular interactions. "
 +                              "For short simulations where linked molecules remain in the same "
 +                              "periodic image, the environment variable GMX_USE_GRAPH can be used "
 +                              "to override this check.\n");
                  }
              }
          }
          else
          {
              fr->bMolPBC = dd_bonded_molpbc(cr->dd, fr->ePBC);
 +
 +            if (useEwaldSurfaceCorrection && !dd_moleculesAreAlwaysWhole(*cr->dd))
 +            {
 +                gmx_fatal(FARGS,
 +                          "You requested dipole correction (epsilon_surface > 0), but molecules "
 +                          "are broken "
 +                          "over periodic boundary conditions by the domain decomposition. "
 +                          "Run without domain decomposition instead.");
 +            }
 +        }
 +
 +        if (useEwaldSurfaceCorrection)
 +        {
 +            GMX_RELEASE_ASSERT((!DOMAINDECOMP(cr) && !fr->bMolPBC)
 +                                       || (DOMAINDECOMP(cr) && dd_moleculesAreAlwaysWhole(*cr->dd)),
 +                               "Molecules can not be broken by PBC with epsilon_surface > 0");
          }
      }
  
      fr->rc_scaling = ir->refcoord_scaling;
      copy_rvec(ir->posres_com, fr->posres_com);
      copy_rvec(ir->posres_comB, fr->posres_comB);
 -    fr->rlist                    = cutoff_inf(ir->rlist);
 -    fr->ljpme_combination_rule   = ir->ljpme_combination_rule;
 +    fr->rlist                  = cutoff_inf(ir->rlist);
 +    fr->ljpme_combination_rule = ir->ljpme_combination_rule;
  
      /* This now calculates sum for q and c6*/
      bool systemHasNetCharge = set_chargesum(fp, fr, mtop);
  
      /* fr->ic is used both by verlet and group kernels (to some extent) now */
      init_interaction_const(fp, &fr->ic, ir, mtop, systemHasNetCharge);
 -    init_interaction_const_tables(fp, fr->ic, ir->rlist + ir->tabext);
 +    init_interaction_const_tables(fp, fr->ic);
  
 -    const interaction_const_t *ic = fr->ic;
 +    const interaction_const_tic = fr->ic;
  
      /* TODO: Replace this Ewald table or move it into interaction_const_t */
      if (ir->coulombtype == eelEWALD)
      /* Electrostatics: Translate from interaction-setting-in-mdp-file to kernel interaction format */
      switch (ic->eeltype)
      {
 -        case eelCUT:
 -            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_COULOMB;
 -            break;
 +        case eelCUT: fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_COULOMB; break;
  
          case eelRF:
 -        case eelGRF:
 -            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_REACTIONFIELD;
 -            break;
 -
 -        case eelRF_ZERO:
 -            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_REACTIONFIELD;
 -            GMX_RELEASE_ASSERT(ic->coulomb_modifier == eintmodEXACTCUTOFF, "With the group scheme RF-zero needs the exact cut-off modifier");
 -            break;
 +        case eelRF_ZERO: fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_REACTIONFIELD; break;
  
          case eelSWITCH:
          case eelSHIFT:
  
          case eelPME:
          case eelP3M_AD:
 -        case eelEWALD:
 -            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_EWALD;
 -            break;
 +        case eelEWALD: fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_EWALD; break;
  
          default:
              gmx_fatal(FARGS, "Unsupported electrostatic interaction: %s", eel_names[ic->eeltype]);
                  fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_LENNARDJONES;
              }
              break;
 -        case evdwPME:
 -            fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_LJEWALD;
 -            break;
 +        case evdwPME: fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_LJEWALD; break;
  
          case evdwSWITCH:
          case evdwSHIFT:
              fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_CUBICSPLINETABLE;
              break;
  
 -        default:
 -            gmx_fatal(FARGS, "Unsupported vdw interaction: %s", evdw_names[ic->vdwtype]);
 +        default: gmx_fatal(FARGS, "Unsupported vdw interaction: %s", evdw_names[ic->vdwtype]);
      }
      fr->nbkernel_vdw_modifier = ic->vdw_modifier;
  
 -    if (ir->cutoff_scheme == ecutsGROUP)
 -    {
 -        fr->bvdwtab    = ((ic->vdwtype != evdwCUT || !gmx_within_tol(ic->reppow, 12.0, 10*GMX_DOUBLE_EPS))
 -                          && !EVDW_PME(ic->vdwtype));
 -        /* We have special kernels for standard Ewald and PME, but the pme-switch ones are tabulated above */
 -        fr->bcoultab   = !(ic->eeltype == eelCUT ||
 -                           ic->eeltype == eelEWALD ||
 -                           ic->eeltype == eelPME ||
 -                           ic->eeltype == eelP3M_AD ||
 -                           ic->eeltype == eelRF ||
 -                           ic->eeltype == eelRF_ZERO);
 -
 -        /* If the user absolutely wants different switch/shift settings for coul/vdw, it is likely
 -         * going to be faster to tabulate the interaction than calling the generic kernel.
 -         * However, if generic kernels have been requested we keep things analytically.
 -         */
 -        if (fr->nbkernel_elec_modifier == eintmodPOTSWITCH &&
 -            fr->nbkernel_vdw_modifier == eintmodPOTSWITCH &&
 -            !bGenericKernelOnly)
 -        {
 -            if ((ic->rcoulomb_switch != ic->rvdw_switch) || (ic->rcoulomb != ic->rvdw))
 -            {
 -                fr->bcoultab = TRUE;
 -                /* Once we tabulate electrostatics, we can use the switch function for LJ,
 -                 * which would otherwise need two tables.
 -                 */
 -            }
 -        }
 -        else if ((fr->nbkernel_elec_modifier == eintmodPOTSHIFT && fr->nbkernel_vdw_modifier == eintmodPOTSHIFT) ||
 -                 ((fr->nbkernel_elec_interaction == GMX_NBKERNEL_ELEC_REACTIONFIELD &&
 -                   fr->nbkernel_elec_modifier == eintmodEXACTCUTOFF &&
 -                   (fr->nbkernel_vdw_modifier == eintmodPOTSWITCH || fr->nbkernel_vdw_modifier == eintmodPOTSHIFT))))
 -        {
 -            if ((ic->rcoulomb != ic->rvdw) && (!bGenericKernelOnly))
 -            {
 -                fr->bcoultab = TRUE;
 -            }
 -        }
 -
 -        if (fr->nbkernel_elec_modifier == eintmodFORCESWITCH)
 -        {
 -            fr->bcoultab = TRUE;
 -        }
 -        if (fr->nbkernel_vdw_modifier == eintmodFORCESWITCH)
 -        {
 -            fr->bvdwtab = TRUE;
 -        }
 -
 -        if (getenv("GMX_REQUIRE_TABLES"))
 -        {
 -            fr->bvdwtab  = TRUE;
 -            fr->bcoultab = TRUE;
 -        }
 -
 -        if (fp)
 -        {
 -            fprintf(fp, "Table routines are used for coulomb: %s\n",
 -                    gmx::boolToString(fr->bcoultab));
 -            fprintf(fp, "Table routines are used for vdw:     %s\n",
 -                    gmx::boolToString(fr->bvdwtab));
 -        }
 -
 -        if (fr->bvdwtab)
 -        {
 -            fr->nbkernel_vdw_interaction = GMX_NBKERNEL_VDW_CUBICSPLINETABLE;
 -            fr->nbkernel_vdw_modifier    = eintmodNONE;
 -        }
 -        if (fr->bcoultab)
 -        {
 -            fr->nbkernel_elec_interaction = GMX_NBKERNEL_ELEC_CUBICSPLINETABLE;
 -            fr->nbkernel_elec_modifier    = eintmodNONE;
 -        }
 -    }
 -
      if (ir->cutoff_scheme == ecutsVERLET)
      {
 -        if (!gmx_within_tol(ic->reppow, 12.0, 10*GMX_DOUBLE_EPS))
 +        if (!gmx_within_tol(ic->reppow, 12.0, 10 * GMX_DOUBLE_EPS))
          {
 -            gmx_fatal(FARGS, "Cut-off scheme %s only supports LJ repulsion power 12", ecutscheme_names[ir->cutoff_scheme]);
 +            gmx_fatal(FARGS, "Cut-off scheme %s only supports LJ repulsion power 12",
 +                      ecutscheme_names[ir->cutoff_scheme]);
          }
          /* Older tpr files can contain Coulomb user tables with the Verlet cutoff-scheme,
           * while mdrun does not (and never did) support this.
      /* 1-4 interaction electrostatics */
      fr->fudgeQQ = mtop->ffparams.fudgeQQ;
  
 -    /* Parameters for generalized RF */
 -    fr->zsquare = 0.0;
 -    fr->temp    = 0.0;
 -
 -    if (ic->eeltype == eelGRF)
 -    {
 -        init_generalized_rf(fp, mtop, ir, fr);
 -    }
 -
      fr->haveDirectVirialContributions =
 -        (EEL_FULL(ic->eeltype) || EVDW_PME(ic->vdwtype) ||
 -         fr->forceProviders->hasForceProvider() ||
 -         gmx_mtop_ftype_count(mtop, F_POSRES) > 0 ||
 -         gmx_mtop_ftype_count(mtop, F_FBPOSRES) > 0 ||
 -         ir->nwall > 0 ||
 -         ir->bPull ||
 -         ir->bRot ||
 -         ir->bIMD);
 -
 -    if (fr->haveDirectVirialContributions)
 -    {
 -        fr->forceBufferForDirectVirialContributions = new std::vector<gmx::RVec>;
 -    }
 +            (EEL_FULL(ic->eeltype) || EVDW_PME(ic->vdwtype) || fr->forceProviders->hasForceProvider()
 +             || gmx_mtop_ftype_count(mtop, F_POSRES) > 0 || gmx_mtop_ftype_count(mtop, F_FBPOSRES) > 0
 +             || ir->nwall > 0 || ir->bPull || ir->bRot || ir->bIMD);
  
 -    if (fr->cutoff_scheme == ecutsGROUP &&
 -        ncg_mtop(mtop) > fr->cg_nalloc && !DOMAINDECOMP(cr))
 -    {
 -        /* Count the total number of charge groups */
 -        fr->cg_nalloc = ncg_mtop(mtop);
 -        srenew(fr->cg_cm, fr->cg_nalloc);
 -    }
      if (fr->shift_vec == nullptr)
      {
          snew(fr->shift_vec, SHIFTS);
      }
  
 -    if (fr->fshift == nullptr)
 -    {
 -        snew(fr->fshift, SHIFTS);
 -    }
 +    fr->shiftForces.resize(SHIFTS);
  
      if (fr->nbfp == nullptr)
      {
          fr->nbfp  = mk_nbfp(&mtop->ffparams, fr->bBHAM);
          if (EVDW_PME(ic->vdwtype))
          {
 -            fr->ljpme_c6grid  = make_ljpme_c6grid(&mtop->ffparams, fr);
 +            fr->ljpme_c6grid = make_ljpme_c6grid(&mtop->ffparams, fr);
          }
      }
  
      {
          if (ic->rvdw_switch >= ic->rvdw)
          {
 -            gmx_fatal(FARGS, "rvdw_switch (%f) must be < rvdw (%f)",
 -                      ic->rvdw_switch, ic->rvdw);
 +            gmx_fatal(FARGS, "rvdw_switch (%f) must be < rvdw (%f)", ic->rvdw_switch, ic->rvdw);
          }
          if (fp)
          {
              fprintf(fp, "Using %s Lennard-Jones, switch between %g and %g nm\n",
 -                    (ic->eeltype == eelSWITCH) ? "switched" : "shifted",
 -                    ic->rvdw_switch, ic->rvdw);
 +                    (ic->eeltype == eelSWITCH) ? "switched" : "shifted", ic->rvdw_switch, ic->rvdw);
          }
      }
  
  
      if (fp && fr->cutoff_scheme == ecutsGROUP)
      {
 -        fprintf(fp, "Cut-off's:   NS: %g   Coulomb: %g   %s: %g\n",
 -                fr->rlist, ic->rcoulomb, fr->bBHAM ? "BHAM" : "LJ", ic->rvdw);
 -    }
 -
 -    fr->eDispCorr = ir->eDispCorr;
 -    fr->numAtomsForDispersionCorrection = mtop->natoms;
 -    if (ir->eDispCorr != edispcNO)
 -    {
 -        set_avcsixtwelve(fp, fr, mtop);
 +        fprintf(fp, "Cut-off's:   NS: %g   Coulomb: %g   %s: %g\n", fr->rlist, ic->rcoulomb,
 +                fr->bBHAM ? "BHAM" : "LJ", ic->rvdw);
      }
  
      if (ir->implicit_solvent)
          gmx_fatal(FARGS, "Implict solvation is no longer supported.");
      }
  
 -    /* Construct tables for the group scheme. A little unnecessary to
 -     * make both vdw and coul tables sometimes, but what the
 -     * heck. Note that both cutoff schemes construct Ewald tables in
 -     * init_interaction_const_tables. */
 -    needGroupSchemeTables = (ir->cutoff_scheme == ecutsGROUP &&
 -                             (fr->bcoultab || fr->bvdwtab));
 -
 -    negp_pp   = ir->opts.ngener - ir->nwall;
 -    negptable = 0;
 -    if (!needGroupSchemeTables)
 -    {
 -        bSomeNormalNbListsAreInUse = TRUE;
 -        fr->nnblists               = 1;
 -    }
 -    else
 -    {
 -        bSomeNormalNbListsAreInUse = FALSE;
 -        for (egi = 0; egi < negp_pp; egi++)
 -        {
 -            for (egj = egi; egj < negp_pp; egj++)
 -            {
 -                egp_flags = ir->opts.egp_flags[GID(egi, egj, ir->opts.ngener)];
 -                if (!(egp_flags & EGP_EXCL))
 -                {
 -                    if (egp_flags & EGP_TABLE)
 -                    {
 -                        negptable++;
 -                    }
 -                    else
 -                    {
 -                        bSomeNormalNbListsAreInUse = TRUE;
 -                    }
 -                }
 -            }
 -        }
 -        if (bSomeNormalNbListsAreInUse)
 -        {
 -            fr->nnblists = negptable + 1;
 -        }
 -        else
 -        {
 -            fr->nnblists = negptable;
 -        }
 -        if (fr->nnblists > 1)
 -        {
 -            snew(fr->gid2nblists, ir->opts.ngener*ir->opts.ngener);
 -        }
 -    }
 -
 -    snew(fr->nblists, fr->nnblists);
  
      /* This code automatically gives table length tabext without cut-off's,
       * in that case grompp should already have checked that we do not need
       */
      rtab = ir->rlist + ir->tabext;
  
 -    if (needGroupSchemeTables)
 -    {
 -        /* make tables for ordinary interactions */
 -        if (bSomeNormalNbListsAreInUse)
 -        {
 -            make_nbf_tables(fp, ic, rtab, tabfn, nullptr, nullptr, &fr->nblists[0]);
 -            m = 1;
 -        }
 -        else
 -        {
 -            m = 0;
 -        }
 -        if (negptable > 0)
 -        {
 -            /* Read the special tables for certain energy group pairs */
 -            nm_ind = mtop->groups.grps[egcENER].nm_ind;
 -            for (egi = 0; egi < negp_pp; egi++)
 -            {
 -                for (egj = egi; egj < negp_pp; egj++)
 -                {
 -                    egp_flags = ir->opts.egp_flags[GID(egi, egj, ir->opts.ngener)];
 -                    if ((egp_flags & EGP_TABLE) && !(egp_flags & EGP_EXCL))
 -                    {
 -                        if (fr->nnblists > 1)
 -                        {
 -                            fr->gid2nblists[GID(egi, egj, ir->opts.ngener)] = m;
 -                        }
 -                        /* Read the table file with the two energy groups names appended */
 -                        make_nbf_tables(fp, ic, rtab, tabfn,
 -                                        *mtop->groups.grpname[nm_ind[egi]],
 -                                        *mtop->groups.grpname[nm_ind[egj]],
 -                                        &fr->nblists[m]);
 -                        m++;
 -                    }
 -                    else if (fr->nnblists > 1)
 -                    {
 -                        fr->gid2nblists[GID(egi, egj, ir->opts.ngener)] = 0;
 -                    }
 -                }
 -            }
 -        }
 -    }
 -
 -    /* Tables might not be used for the potential modifier
 -     * interactions per se, but we still need them to evaluate
 -     * switch/shift dispersion corrections in this case. */
 -    if (fr->eDispCorr != edispcNO)
 -    {
 -        fr->dispersionCorrectionTable = makeDispersionCorrectionTable(fp, ic, rtab, tabfn);
 -    }
 -
      /* We want to use unmodified tables for 1-4 coulombic
       * interactions, so we must in general have an extra set of
       * tables. */
 -    if (gmx_mtop_ftype_count(mtop, F_LJ14) > 0 ||
 -        gmx_mtop_ftype_count(mtop, F_LJC14_Q) > 0 ||
 -        gmx_mtop_ftype_count(mtop, F_LJC_PAIRS_NB) > 0)
 +    if (gmx_mtop_ftype_count(mtop, F_LJ14) > 0 || gmx_mtop_ftype_count(mtop, F_LJC14_Q) > 0
 +        || gmx_mtop_ftype_count(mtop, F_LJC_PAIRS_NB) > 0)
      {
 -        fr->pairsTable = make_tables(fp, ic, tabpfn, rtab,
 -                                     GMX_MAKETABLES_14ONLY);
 +        fr->pairsTable = make_tables(fp, ic, tabpfn, rtab, GMX_MAKETABLES_14ONLY);
      }
  
      /* Wall stuff */
          // TODO Don't need to catch this here, when merging with master branch
          try
          {
 -            fcd->bondtab  = make_bonded_tables(fp,
 -                                               F_TABBONDS, F_TABBONDSNC,
 -                                               mtop, tabbfnm, "b");
 -            fcd->angletab = make_bonded_tables(fp,
 -                                               F_TABANGLES, -1,
 -                                               mtop, tabbfnm, "a");
 -            fcd->dihtab   = make_bonded_tables(fp,
 -                                               F_TABDIHS, -1,
 -                                               mtop, tabbfnm, "d");
 +            fcd->bondtab  = make_bonded_tables(fp, F_TABBONDS, F_TABBONDSNC, mtop, tabbfnm, "b");
 +            fcd->angletab = make_bonded_tables(fp, F_TABANGLES, -1, mtop, tabbfnm, "a");
 +            fcd->dihtab   = make_bonded_tables(fp, F_TABDIHS, -1, mtop, tabbfnm, "d");
          }
 -        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
 +        GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
      }
      else
      {
          if (debug)
          {
 -            fprintf(debug, "No fcdata or table file name passed, can not read table, can not do bonded interactions\n");
 +            fprintf(debug,
 +                    "No fcdata or table file name passed, can not read table, can not do bonded "
 +                    "interactions\n");
          }
      }
  
          // Initialize QM/MM if supported
          if (GMX_QMMM)
          {
 -            GMX_LOG(mdlog.info).asParagraph().
 -                appendText("Large parts of the QM/MM support is deprecated, and may be removed in a future "
 -                           "version. Please get in touch with the developers if you find the support useful, "
 -                           "as help is needed if the functionality is to continue to be available.");
 +            GMX_LOG(mdlog.info)
 +                    .asParagraph()
 +                    .appendText(
 +                            "Large parts of the QM/MM support is deprecated, and may be removed in "
 +                            "a future "
 +                            "version. Please get in touch with the developers if you find the "
 +                            "support useful, "
 +                            "as help is needed if the functionality is to continue to be "
 +                            "available.");
              fr->qr = mk_QMMMrec();
              init_QMMMrec(cr, mtop, ir, fr);
          }
          else
          {
 -            gmx_incons("QM/MM was requested, but is only available when GROMACS "
 -                       "is configured with QM/MM support");
 +            gmx_incons(
 +                    "QM/MM was requested, but is only available when GROMACS "
 +                    "is configured with QM/MM support");
          }
      }
  
      /* Set all the static charge group info */
 -    fr->cginfo_mb = init_cginfo_mb(fp, mtop, fr, bNoSolvOpt,
 -                                   &bFEP_NonBonded,
 -                                   &fr->bExcl_IntraCGAll_InterCGNone);
 -    if (DOMAINDECOMP(cr))
 -    {
 -        fr->cginfo = nullptr;
 -    }
 -    else
 +    fr->cginfo_mb = init_cginfo_mb(mtop, fr, &bFEP_NonBonded);
 +    if (!DOMAINDECOMP(cr))
      {
          fr->cginfo = cginfo_expand(mtop->molblock.size(), fr->cginfo_mb);
      }
  
      if (!DOMAINDECOMP(cr))
      {
 -        forcerec_set_ranges(fr, ncg_mtop(mtop), ncg_mtop(mtop),
 -                            mtop->natoms, mtop->natoms, mtop->natoms);
 +        forcerec_set_ranges(fr, mtop->natoms, mtop->natoms, mtop->natoms);
      }
  
      fr->print_force = print_force;
  
 -
 -    /* coarse load balancing vars */
 -    fr->t_fnbf    = 0.;
 -    fr->t_wait    = 0.;
 -    fr->timesteps = 0;
 -
 -    /* Initialize neighbor search */
 -    snew(fr->ns, 1);
 -    init_ns(fp, cr, fr->ns, fr, mtop);
 -
 -    if (thisRankHasDuty(cr, DUTY_PP))
 -    {
 -        gmx_nonbonded_setup(fr, bGenericKernelOnly);
 -    }
 -
      /* Initialize the thread working data for bonded interactions */
 -    init_bonded_threading(fp, mtop->groups.grps[egcENER].nr,
 -                          &fr->bondedThreading);
 +    fr->bondedThreading = init_bonded_threading(
 +            fp, mtop->groups.groups[SimulationAtomGroupType::EnergyOutput].size());
  
      fr->nthread_ewc = gmx_omp_nthreads_get(emntBonded);
      snew(fr->ewc_t, fr->nthread_ewc);
          // We have PME+LJcutoff kernels for rcoulomb>rvdw.
          if (EEL_PME_EWALD(ir->coulombtype) && ir->vdwtype == eelCUT)
          {
 -            GMX_RELEASE_ASSERT(ir->rcoulomb >= ir->rvdw, "With Verlet lists and PME we should have rcoulomb>=rvdw");
 +            GMX_RELEASE_ASSERT(ir->rcoulomb >= ir->rvdw,
 +                               "With Verlet lists and PME we should have rcoulomb>=rvdw");
          }
          else
          {
 -            GMX_RELEASE_ASSERT(ir->rcoulomb == ir->rvdw, "With Verlet lists and no PME rcoulomb and rvdw should be identical");
 +            GMX_RELEASE_ASSERT(
 +                    ir->rcoulomb == ir->rvdw,
 +                    "With Verlet lists and no PME rcoulomb and rvdw should be identical");
          }
  
 -        init_nb_verlet(mdlog, &fr->nbv, bFEP_NonBonded, ir, fr,
 -                       cr, hardwareInfo, deviceInfo,
 -                       mtop, box);
 +        fr->nbv = Nbnxm::init_nb_verlet(mdlog, bFEP_NonBonded, ir, fr, cr, hardwareInfo, deviceInfo,
 +                                        mtop, box, wcycle);
  
          if (useGpuForBonded)
          {
-             auto stream = DOMAINDECOMP(cr)
 -            // TODO use havePPDomainDecomposition here to simplify the code.
 -            auto stream = (DOMAINDECOMP(cr) && (cr->nnodes - cr->npmenodes > 1)) ?
 -                nbnxn_gpu_get_command_stream(fr->nbv->gpu_nbv, eintNonlocal) :
 -                nbnxn_gpu_get_command_stream(fr->nbv->gpu_nbv, eintLocal);
++            auto stream = havePPDomainDecomposition(cr)
 +                                  ? Nbnxm::gpu_get_command_stream(
 +                                            fr->nbv->gpu_nbv, gmx::InteractionLocality::NonLocal)
 +                                  : Nbnxm::gpu_get_command_stream(fr->nbv->gpu_nbv,
 +                                                                  gmx::InteractionLocality::Local);
              // TODO the heap allocation is only needed while
              // t_forcerec lacks a constructor.
 -            fr->gpuBonded = new gmx::GpuBonded(mtop->ffparams,
 -                                               stream);
 +            fr->gpuBonded = new gmx::GpuBonded(mtop->ffparams, stream, wcycle);
          }
      }
  
 +    if (ir->eDispCorr != edispcNO)
 +    {
 +        fr->dispersionCorrection = std::make_unique<DispersionCorrection>(
 +                *mtop, *ir, fr->bBHAM, fr->ntype,
 +                gmx::arrayRefFromArray(fr->nbfp, fr->ntype * fr->ntype * 2), *fr->ic, tabfn);
 +        fr->dispersionCorrection->print(mdlog);
 +    }
 +
      if (fp != nullptr)
      {
          /* Here we switch from using mdlog, which prints the newline before
          fprintf(fp, "\n");
      }
  
 -    if (ir->eDispCorr != edispcNO)
 +    if (pmeOnlyRankUsesGpu && c_enableGpuPmePpComms)
      {
 -        calc_enervirdiff(fp, ir->eDispCorr, fr);
 +        fr->pmePpCommGpu = std::make_unique<gmx::PmePpCommGpu>(cr->mpi_comm_mysim, cr->dd->pme_nodeid);
      }
  }
  
 +t_forcerec::t_forcerec() = default;
 +
 +t_forcerec::~t_forcerec() = default;
 +
  /* Frees GPU memory and sets a tMPI node barrier.
   *
   * Note that this function needs to be called even if GPUs are not used
   * \todo Remove physical node barrier from this function after making sure
   * that it's not needed anymore (with a shared GPU run).
   */
 -void free_gpu_resources(t_forcerec                          *fr,
 -                        const gmx::PhysicalNodeCommunicator &physicalNodeCommunicator)
 +void free_gpu_resources(t_forcerec*                          fr,
 +                        const gmx::PhysicalNodeCommunicator& physicalNodeCommunicator,
 +                        const gmx_gpu_info_t&                gpu_info)
  {
 -    bool isPPrankUsingGPU = (fr != nullptr) && (fr->nbv != nullptr) && fr->nbv->bUseGPU;
 +    bool isPPrankUsingGPU = (fr != nullptr) && (fr->nbv != nullptr) && fr->nbv->useGpu();
  
      /* stop the GPU profiler (only CUDA) */
 -    stopGpuProfiler();
 +    if (gpu_info.n_dev > 0)
 +    {
 +        stopGpuProfiler();
 +    }
  
      if (isPPrankUsingGPU)
      {
 -        /* free nbnxn data in GPU memory */
 -        nbnxn_gpu_free(fr->nbv->gpu_nbv);
 +        /* Free data in GPU memory and pinned memory before destroying the GPU context */
 +        fr->nbv.reset();
 +
          delete fr->gpuBonded;
          fr->gpuBonded = nullptr;
      }
      }
  }
  
 -void done_forcerec(t_forcerec *fr, int numMolBlocks, int numEnergyGroups)
 +void done_forcerec(t_forcerec* fr, int numMolBlocks)
  {
      if (fr == nullptr)
      {
      }
      done_cginfo_mb(fr->cginfo_mb, numMolBlocks);
      sfree(fr->nbfp);
 -    done_interaction_const(fr->ic);
 +    delete fr->ic;
      sfree(fr->shift_vec);
 -    sfree(fr->fshift);
 -    sfree(fr->nblists);
 -    done_ns(fr->ns, numEnergyGroups);
      sfree(fr->ewc_t);
      tear_down_bonded_threading(fr->bondedThreading);
      GMX_RELEASE_ASSERT(fr->gpuBonded == nullptr, "Should have been deleted earlier, when used");
      fr->bondedThreading = nullptr;
 -    sfree(fr);
 +    delete fr;
  }