Merge branch release-2016
authorMark Abraham <mark.j.abraham@gmail.com>
Fri, 31 Mar 2017 14:29:54 +0000 (16:29 +0200)
committerMark Abraham <mark.j.abraham@gmail.com>
Fri, 31 Mar 2017 14:29:54 +0000 (16:29 +0200)
Change-Id: I536af0bcfba93ecf3e2afeb847120dc8f637cc92

1  2 
docs/CMakeLists.txt
src/gromacs/gmxana/gmx_xpm2ps.cpp
src/gromacs/gmxpreprocess/readir.cpp

diff --combined docs/CMakeLists.txt
index bf033c4baf890506ca68b04996e4a78a0bbaa0c3,b2b665c21f4e061fa168ea568e213f45c8ceb0e6..9fe64470444d1b40deb0ce5aeb84c8470606202b
@@@ -122,6 -122,7 +122,7 @@@ if (SPHINX_FOUND
          user-guide/flow.rst
          user-guide/system-preparation.rst
          user-guide/cutoff-schemes.rst
+         user-guide/managing-simulations.rst
          user-guide/mdrun-features.rst
          user-guide/mdrun-performance.rst
          user-guide/mdp-options.rst
          EXTRA_VARS
              SPHINX_EXTENSION_PATH RELENG_PATH
              EXPECTED_DOXYGEN_VERSION
 -            GMX_CMAKE_MINIMUM_REQUIRED_VERSION REQUIRED_CUDA_VERSION
 +            CMAKE_MINIMUM_REQUIRED_VERSION REQUIRED_CUDA_VERSION
              REQUIRED_OPENCL_MIN_VERSION
              REQUIRED_CUDA_COMPUTE_CAPABILITY REGRESSIONTEST_VERSION
              SOURCE_MD5SUM REGRESSIONTEST_MD5SUM_STRING
index cec4fe8ca105cf12b85572667673967b1a332341,be80038239315c36d666740d3598a118d8cd2edf..90f0448fb10b20cbd37b0ada91a8fbb96de5b94b
@@@ -43,6 -43,7 +43,7 @@@
  #include <cstring>
  
  #include <algorithm>
+ #include <string>
  
  #include "gromacs/commandline/pargs.h"
  #include "gromacs/commandline/viewit.h"
  #include "gromacs/gmxana/gmx_ana.h"
  #include "gromacs/utility/arraysize.h"
  #include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/exceptions.h"
  #include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/filestream.h"
  #include "gromacs/utility/futil.h"
  #include "gromacs/utility/gmxassert.h"
  #include "gromacs/utility/smalloc.h"
+ #include "gromacs/utility/stringutil.h"
  
  #define FUDGE 1.2
  #define DDD   2
@@@ -114,9 -114,9 +116,9 @@@ enum 
  
  void get_params(const char *mpin, const char *mpout, t_psrec *psr)
  {
 -    static const char *gmx_bools[BOOL_NR+1]  = { "no", "yes", NULL };
 +    static const char *gmx_bools[BOOL_NR+1]  = { "no", "yes", nullptr };
      /* this must correspond to t_rgb *linecolors[] below */
 -    static const char *colors[] = { "none", "black", "white", NULL };
 +    static const char *colors[] = { "none", "black", "white", nullptr };
      warninp_t          wi;
      t_inpfile         *inp;
      const char        *tmp;
  
      wi = init_warning(FALSE, 0);
  
 -    if (mpin != NULL)
 +    if (mpin != nullptr)
      {
 -        inp = read_inpfile(mpin, &ninp, wi);
 +        gmx::TextInputFile stream(mpin);
 +        inp = read_inpfile(&stream, mpin, &ninp, wi);
      }
      else
      {
 -        inp = NULL;
 +        inp = nullptr;
      }
      ETYPE("black&white",    psr->bw,             gmx_bools);
      RTYPE("linewidth",      psr->linewidth,      1.0);
  
      check_warning_error(wi, FARGS);
  
 -    if (mpout != NULL)
 +    if (mpout != nullptr)
      {
 -        write_inpfile(mpout, ninp, inp, TRUE, wi);
 +        gmx::TextOutputFile stream(mpout);
 +        write_inpfile(&stream, mpout, ninp, inp, TRUE, WriteMdpHeader::yes, wi);
 +        stream.close();
      }
  
      done_warning(wi, FARGS);
@@@ -193,7 -190,7 +195,7 @@@ t_rgb red   = { 1, 0, 0 }
  t_rgb blue  = { 0, 0, 1 };
  #define BLACK (&black)
  /* this must correspond to *colors[] in get_params */
 -t_rgb *linecolors[] = { NULL, &black, &white, NULL };
 +t_rgb *linecolors[] = { nullptr, &black, &white, nullptr };
  
  gmx_bool diff_maps(int nmap1, t_mapping *map1, int nmap2, t_mapping *map2)
  {
@@@ -283,7 -280,7 +285,7 @@@ void leg_continuous(t_psdata ps, real x
          boxxh = fontsize;
      }
  
 -    GMX_RELEASE_ASSERT(map != NULL, "NULL map array provided to leg_continuous()");
 +    GMX_RELEASE_ASSERT(map != nullptr, "NULL map array provided to leg_continuous()");
  
      /* LANDSCAPE */
      xx0 = x0-((nmap-mapoffset)*boxxh)/2.0;
@@@ -687,7 -684,7 +689,7 @@@ void xpm_mat(const char *outf, int nmat
      FILE      *out;
      int        i, x, y, col;
      int        nmap;
 -    t_mapping *map = NULL;
 +    t_mapping *map = nullptr;
  
      out = gmx_ffopen(outf, "w");
  
@@@ -791,7 -788,7 +793,7 @@@ void ps_mat(const char *outf, int nmat
              int mapoffset)
  {
      const char   *libm2p;
-     char          buf[256], *legend;
+     char         *legend;
      t_psdata      out;
      t_psrec       psrec, *psr;
      int           W, H;
      real          x0, y0, xx;
      real          w, h, dw, dh;
      int           nmap1 = 0, nmap2 = 0, leg_nmap;
 -    t_mapping    *map1  = NULL, *map2 = NULL, *leg_map;
 +    t_mapping    *map1  = nullptr, *map2 = nullptr, *leg_map;
      gmx_bool      bMap1, bNextMap1, bDiscrete;
  
      /* memory leak: */
      libm2p = m2p ? gmxlibfn(m2p) : m2p;
 -    get_params(libm2p, m2pout, &psrec);
 +    try
 +    {
 +        get_params(libm2p, m2pout, &psrec);
 +    }
 +    GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
  
      psr = &psrec;
  
              /* Print title, if any */
              ps_rgb(out, BLACK);
              ps_strfont(out, psr->titfont, psr->titfontsize);
+             std::string buf;
              if (!mat2 || (std::strcmp(mat[i].title, mat2[i].title) == 0))
              {
-                 std::strcpy(buf, mat[i].title);
+                 buf = mat[i].title;
              }
              else
              {
-                 sprintf(buf, "%s / %s", mat[i].title, mat2[i].title);
+                 buf = gmx::formatString("%s / %s", mat[i].title, mat2[i].title);
              }
              ps_ctext(out, x0+w/2, y0+box_height(&(mat[i]), psr)+psr->titfontsize,
-                      buf, eXCenter);
+                      buf.c_str(), eXCenter);
          }
-         sprintf(buf, "Here starts the filling of box #%d", i);
-         ps_comment(out, buf);
+         ps_comment(out, gmx::formatString("Here starts the filling of box #%d", i).c_str());
          for (x = 0; (x < mat[i].nx); x++)
          {
              int nexty;
      {
          ps_comment(out, "Now it's legend time!");
          ps_linewidth(out, static_cast<int>(psr->linewidth));
 -        if (mat2 == NULL || elegend != elSecond)
 +        if (mat2 == nullptr || elegend != elSecond)
          {
              bDiscrete = mat[0].bDiscrete;
              legend    = mat[0].legend;
@@@ -1061,7 -1054,7 +1063,7 @@@ void make_axis_labels(int nmat, t_matri
      for (i = 0; (i < nmat); i++)
      {
          /* Make labels for x axis */
 -        if (mat[i].axis_x == NULL)
 +        if (mat[i].axis_x == nullptr)
          {
              snew(mat[i].axis_x, mat[i].nx);
              for (j = 0; (j < mat[i].nx); j++)
              }
          }
          /* Make labels for y axis */
 -        if (mat[i].axis_y == NULL)
 +        if (mat[i].axis_y == nullptr)
          {
              snew(mat[i].axis_y, mat[i].ny);
              for (j = 0; (j < mat[i].ny); j++)
@@@ -1195,13 -1188,13 +1197,13 @@@ void write_combined_matrix(int ecombine
                        " not match.\n", k, mat1[k].nx, mat1[k].ny, mat2[k].nx, mat2[k].ny);
          }
          printf("Combining two %dx%d matrices\n", mat1[k].nx, mat1[k].ny);
 -        rmat1 = matrix2real(&mat1[k], NULL);
 -        rmat2 = matrix2real(&mat2[k], NULL);
 -        if (NULL == rmat1 || NULL == rmat2)
 +        rmat1 = matrix2real(&mat1[k], nullptr);
 +        rmat2 = matrix2real(&mat2[k], nullptr);
 +        if (nullptr == rmat1 || nullptr == rmat2)
          {
              gmx_fatal(FARGS, "Could not extract real data from %s xpm matrices. Note that, e.g.,\n"
                        "g_rms and g_mdmat provide such data, but not do_dssp.\n",
 -                      (NULL == rmat1 && NULL == rmat2) ? "both" : "one of the" );
 +                      (nullptr == rmat1 && nullptr == rmat2) ? "both" : "one of the" );
          }
          rlo = 1e38;
          rhi = -1e38;
@@@ -1304,13 -1297,13 +1306,13 @@@ void do_mat(int nmat, t_matrix *mat, t_
          zero_lines(nmat, mat, mat);
      }
  
 -    if (epsfile != NULL)
 +    if (epsfile != nullptr)
      {
          ps_mat(epsfile, nmat, mat, mat2, bFrame, bDiag, bFirstDiag,
                 bTitle, bTitleOnce, bYonce, elegend,
                 size, boxx, boxy, m2p, m2pout, mapoffset);
      }
 -    if (xpmfile != NULL)
 +    if (xpmfile != nullptr)
      {
          xpm_mat(xpmfile, nmat, mat, mat2, bDiag, bFirstDiag);
      }
@@@ -1442,9 -1435,9 +1444,9 @@@ int gmx_xpm2ps(int argc, char *argv[]
      };
  
      gmx_output_env_t *oenv;
 -    const char       *fn, *epsfile = NULL, *xpmfile = NULL;
 +    const char       *fn, *epsfile = nullptr, *xpmfile = nullptr;
      int               i, nmat, nmat2, etitle, elegend, ediag, erainbow, ecombine;
 -    t_matrix         *mat = NULL, *mat2 = NULL;
 +    t_matrix         *mat = nullptr, *mat2 = nullptr;
      gmx_bool          bTitle, bTitleOnce, bDiag, bFirstDiag, bGrad;
      static gmx_bool   bFrame = TRUE, bZeroLine = FALSE, bYonce = FALSE;
      static real       size   = 400, boxx = 0, boxy = 0, cmin = 0, cmax = 0;
      enum                    {
          etSel, etTop, etOnce, etYlabel, etNone, etNR
      };
 -    const char *title[]   = { NULL, "top", "once", "ylabel", "none", NULL };
 +    const char *title[]   = { nullptr, "top", "once", "ylabel", "none", nullptr };
      /* MUST correspond to enum elXxx as defined at top of file */
 -    const char *legend[]  = { NULL, "both", "first", "second", "none", NULL };
 +    const char *legend[]  = { nullptr, "both", "first", "second", "none", nullptr };
      enum                    {
          edSel, edFirst, edSecond, edNone, edNR
      };
 -    const char *diag[]    = { NULL, "first", "second", "none", NULL };
 +    const char *diag[]    = { nullptr, "first", "second", "none", nullptr };
      enum                    {
          erSel, erNo, erBlue, erRed, erNR
      };
 -    const char *rainbow[] = { NULL, "no", "blue", "red", NULL };
 +    const char *rainbow[] = { nullptr, "no", "blue", "red", nullptr };
      /* MUST correspond to enum ecXxx as defined at top of file */
      const char *combine[] = {
 -        NULL, "halves", "add", "sub", "mult", "div", NULL
 +        nullptr, "halves", "add", "sub", "mult", "div", nullptr
      };
      static int  skip = 1, mapoffset = 0;
      t_pargs     pa[] = {
      };
  #define NPA asize(pa)
      t_filenm    fnm[] = {
 -        { efXPM, "-f",  NULL,      ffREAD },
 +        { efXPM, "-f",  nullptr,      ffREAD },
          { efXPM, "-f2", "root2",   ffOPTRD },
 -        { efM2P, "-di", NULL,      ffLIBOPTRD },
 +        { efM2P, "-di", nullptr,      ffLIBOPTRD },
          { efM2P, "-do", "out",     ffOPTWR },
 -        { efEPS, "-o",  NULL,      ffOPTWR },
 -        { efXPM, "-xpm", NULL,      ffOPTWR }
 +        { efEPS, "-o",  nullptr,      ffOPTWR },
 +        { efXPM, "-xpm", nullptr,      ffOPTWR }
      };
  #define NFILE asize(fnm)
  
      if (!parse_common_args(&argc, argv, PCA_CAN_VIEW,
                             NFILE, fnm, NPA, pa,
 -                           asize(desc), desc, 0, NULL, &oenv))
 +                           asize(desc), desc, 0, nullptr, &oenv))
      {
          return 0;
      }
  
      epsfile = ftp2fn_null(efEPS, NFILE, fnm);
      xpmfile = opt2fn_null("-xpm", NFILE, fnm);
 -    if (epsfile == NULL && xpmfile == NULL)
 +    if (epsfile == nullptr && xpmfile == nullptr)
      {
          if (ecombine != ecHalves)
          {
                  "WARNING: can only write result of arithmetic combination "
                  "of two matrices to .xpm file\n"
                  "         file %s will not be written\n", epsfile);
 -        epsfile = NULL;
 +        epsfile = nullptr;
      }
  
      bDiag      = ediag != edNone;
          }
      }
  
 -    if ((mat2 == NULL) && (elegend != elNone))
 +    if ((mat2 == nullptr) && (elegend != elNone))
      {
          elegend = elFirst;
      }
      if (ecombine && ecombine != ecHalves)
      {
          write_combined_matrix(ecombine, xpmfile, nmat, mat, mat2,
 -                              opt2parg_bSet("-cmin", NPA, pa) ? &cmin : NULL,
 -                              opt2parg_bSet("-cmax", NPA, pa) ? &cmax : NULL);
 +                              opt2parg_bSet("-cmin", NPA, pa) ? &cmin : nullptr,
 +                              opt2parg_bSet("-cmax", NPA, pa) ? &cmax : nullptr);
      }
      else
      {
index c90f0dfec62723e0ea3f961dc049e2447786b3e0,6bbab36893f5d8ff45ad220be24febf8c4f1f65d..a2952f296e24ba68efad0df35f5f8ea5bc9f2eb5
@@@ -45,6 -45,7 +45,7 @@@
  #include <cmath>
  
  #include <algorithm>
+ #include <string>
  
  #include "gromacs/fileio/readinp.h"
  #include "gromacs/fileio/warninp.h"
  #include "gromacs/math/units.h"
  #include "gromacs/math/vec.h"
  #include "gromacs/mdlib/calc_verletbuf.h"
 +#include "gromacs/mdrunutility/mdmodules.h"
  #include "gromacs/mdtypes/inputrec.h"
  #include "gromacs/mdtypes/md_enums.h"
  #include "gromacs/mdtypes/pull-params.h"
 +#include "gromacs/options/options.h"
 +#include "gromacs/options/treesupport.h"
  #include "gromacs/pbcutil/pbc.h"
  #include "gromacs/topology/block.h"
  #include "gromacs/topology/ifunc.h"
  #include "gromacs/topology/symtab.h"
  #include "gromacs/topology/topology.h"
  #include "gromacs/utility/cstringutil.h"
 +#include "gromacs/utility/exceptions.h"
  #include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/filestream.h"
 +#include "gromacs/utility/gmxassert.h"
 +#include "gromacs/utility/ikeyvaluetreeerror.h"
 +#include "gromacs/utility/keyvaluetree.h"
 +#include "gromacs/utility/keyvaluetreetransform.h"
  #include "gromacs/utility/smalloc.h"
 +#include "gromacs/utility/stringcompare.h"
  #include "gromacs/utility/stringutil.h"
  
  #define MAXPTR 254
@@@ -107,10 -98,12 +108,10 @@@ typedef struct t_inputrec_string
      char   QMmethod[STRLEN], QMbasis[STRLEN], QMcharge[STRLEN], QMmult[STRLEN],
             bSH[STRLEN], CASorbitals[STRLEN], CASelectrons[STRLEN], SAon[STRLEN],
             SAoff[STRLEN], SAsteps[STRLEN], bTS[STRLEN], bOPT[STRLEN];
 -    char efield_x[STRLEN], efield_xt[STRLEN], efield_y[STRLEN],
 -         efield_yt[STRLEN], efield_z[STRLEN], efield_zt[STRLEN];
  
  } gmx_inputrec_strings;
  
 -static gmx_inputrec_strings *is = NULL;
 +static gmx_inputrec_strings *is = nullptr;
  
  void init_inputrec_strings()
  {
  void done_inputrec_strings()
  {
      sfree(is);
 -    is = NULL;
 +    is = nullptr;
  }
  
  
@@@ -139,13 -132,22 +140,13 @@@ enum 
  };
  
  static const char *constraints[eshNR+1]    = {
 -    "none", "h-bonds", "all-bonds", "h-angles", "all-angles", NULL
 +    "none", "h-bonds", "all-bonds", "h-angles", "all-angles", nullptr
  };
  
  static const char *couple_lam[ecouplamNR+1]    = {
 -    "vdw-q", "vdw", "q", "none", NULL
 +    "vdw-q", "vdw", "q", "none", nullptr
  };
  
 -void init_ir(t_inputrec *ir, t_gromppopts *opts)
 -{
 -    snew(opts->include, STRLEN);
 -    snew(opts->define, STRLEN);
 -    snew(ir->fepvals, 1);
 -    snew(ir->expandedvals, 1);
 -    snew(ir->simtempvals, 1);
 -}
 -
  static void GetSimTemps(int ntemps, t_simtemp *simtemp, double *temperature_lambdas)
  {
  
@@@ -1060,7 -1062,7 +1061,7 @@@ void check_ir(const char *mdparin, t_in
          CHECK(EEL_FULL(ir->coulombtype) || ir->implicit_solvent == eisGBSA);
      }
  
 -    if (getenv("GMX_DO_GALACTIC_DYNAMICS") == NULL)
 +    if (getenv("GMX_DO_GALACTIC_DYNAMICS") == nullptr)
      {
          sprintf(err_buf, "epsilon-r must be >= 0 instead of %g\n", ir->epsilon_r);
          CHECK(ir->epsilon_r < 0);
  
      if (EEL_PME(ir->coulombtype) || EVDW_PME(ir->vdwtype))
      {
 -        if (ir->pme_order < 3)
 +        // TODO: Move these checks into the ewald module with the options class
 +        int orderMin = 3;
 +        int orderMax = (ir->coulombtype == eelP3M_AD ? 8 : 12);
 +
 +        if (ir->pme_order < orderMin || ir->pme_order > orderMax)
          {
 -            warning_error(wi, "pme-order can not be smaller than 3");
 +            sprintf(warn_buf, "With coulombtype = %s, you should have %d <= pme-order <= %d", eel_names[ir->coulombtype], orderMin, orderMax);
 +            warning_error(wi, warn_buf);
          }
      }
  
@@@ -1430,7 -1427,7 +1431,7 @@@ int str_nelem(const char *str, int maxp
          }
          ltrim(copy);
      }
 -    if (ptr == NULL)
 +    if (ptr == nullptr)
      {
          sfree(copy0);
      }
@@@ -1647,8 -1644,8 +1648,8 @@@ static void do_wall_params(t_inputrec *
      char  *names[MAXPTR];
      double dbl;
  
 -    opts->wall_atomtype[0] = NULL;
 -    opts->wall_atomtype[1] = NULL;
 +    opts->wall_atomtype[0] = nullptr;
 +    opts->wall_atomtype[1] = nullptr;
  
      ir->wall_atomtype[0] = -1;
      ir->wall_atomtype[1] = -1;
@@@ -1764,53 -1761,9 +1765,53 @@@ static gmx_bool couple_lambda_has_vdw_o
              couple_lambda_value == ecouplamVDWQ);
  }
  
 +namespace
 +{
 +
 +class MdpErrorHandler : public gmx::IKeyValueTreeErrorHandler
 +{
 +    public:
 +        explicit MdpErrorHandler(warninp_t wi)
 +            : wi_(wi), mapping_(nullptr)
 +        {
 +        }
 +
 +        void setBackMapping(const gmx::IKeyValueTreeBackMapping &mapping)
 +        {
 +            mapping_ = &mapping;
 +        }
 +
 +        virtual bool onError(gmx::UserInputError *ex, const gmx::KeyValueTreePath &context)
 +        {
 +            ex->prependContext(gmx::formatString("Error in mdp option \"%s\":",
 +                                                 getOptionName(context).c_str()));
 +            std::string message = gmx::formatExceptionMessageToString(*ex);
 +            warning_error(wi_, message.c_str());
 +            return true;
 +        }
 +
 +    private:
 +        std::string getOptionName(const gmx::KeyValueTreePath &context)
 +        {
 +            if (mapping_ != nullptr)
 +            {
 +                gmx::KeyValueTreePath path = mapping_->originalPath(context);
 +                GMX_ASSERT(path.size() == 1, "Inconsistent mapping back to mdp options");
 +                return path[0];
 +            }
 +            GMX_ASSERT(context.size() == 1, "Inconsistent context for mdp option parsing");
 +            return context[0];
 +        }
 +
 +        warninp_t                            wi_;
 +        const gmx::IKeyValueTreeBackMapping *mapping_;
 +};
 +
 +} // namespace
 +
  void get_ir(const char *mdparin, const char *mdparout,
 -            t_inputrec *ir, t_gromppopts *opts,
 -            warninp_t wi)
 +            gmx::MDModules *mdModules, t_inputrec *ir, t_gromppopts *opts,
 +            WriteMdpHeader writeMdpHeader, warninp_t wi)
  {
      char       *dumstr[2];
      double      dumdub[2][6];
      t_expanded *expand = ir->expandedvals;
  
      init_inputrec_strings();
 -    inp = read_inpfile(mdparin, &ninp, wi);
 +    gmx::TextInputFile stream(mdparin);
 +    inp = read_inpfile(&stream, mdparin, &ninp, wi);
  
      snew(dumstr[0], STRLEN);
      snew(dumstr[1], STRLEN);
      CCTYPE ("VARIOUS PREPROCESSING OPTIONS");
      CTYPE ("Preprocessor information: use cpp syntax.");
      CTYPE ("e.g.: -I/home/joe/doe -I/home/mary/roe");
 -    STYPE ("include", opts->include,  NULL);
 +    STYPE ("include", opts->include,  nullptr);
      CTYPE ("e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)");
 -    STYPE ("define",  opts->define,   NULL);
 +    STYPE ("define",  opts->define,   nullptr);
  
      CCTYPE ("RUN CONTROL PARAMETERS");
      EETYPE("integrator",  ir->eI,         ei_names);
      CTYPE ("number of steps for center of mass motion removal");
      ITYPE ("nstcomm", ir->nstcomm,    100);
      CTYPE ("group(s) for center of mass motion removal");
 -    STYPE ("comm-grps",   is->vcm,            NULL);
 +    STYPE ("comm-grps",   is->vcm,            nullptr);
  
      CCTYPE ("LANGEVIN DYNAMICS OPTIONS");
      CTYPE ("Friction coefficient (amu/ps) and random seed");
      CTYPE ("This selects the subset of atoms for the compressed");
      CTYPE ("trajectory file. You can select multiple groups. By");
      CTYPE ("default, all atoms will be written.");
 -    STYPE ("compressed-x-grps", is->x_compressed_groups, NULL);
 +    STYPE ("compressed-x-grps", is->x_compressed_groups, nullptr);
      CTYPE ("Selection of energy groups");
 -    STYPE ("energygrps",  is->energy,         NULL);
 +    STYPE ("energygrps",  is->energy,         nullptr);
  
      /* Neighbor searching */
      CCTYPE ("NEIGHBORSEARCHING PARAMETERS");
      CTYPE ("Extension of the potential lookup tables beyond the cut-off");
      RTYPE ("table-extension", ir->tabext, 1.0);
      CTYPE ("Separate tables between energy group pairs");
 -    STYPE ("energygrp-table", is->egptable,   NULL);
 +    STYPE ("energygrp-table", is->egptable,   nullptr);
      CTYPE ("Spacing for the PME/PPPM FFT grid");
      RTYPE ("fourierspacing", ir->fourier_spacing, 0.12);
      CTYPE ("FFT grid size, when a value is 0 fourierspacing will be used");
      ITYPE("nh-chain-length",     ir->opts.nhchainlength, 10);
      EETYPE("print-nose-hoover-chain-variables", ir->bPrintNHChains, yesno_names);
      CTYPE ("Groups to couple separately");
 -    STYPE ("tc-grps",     is->tcgrps,         NULL);
 +    STYPE ("tc-grps",     is->tcgrps,         nullptr);
      CTYPE ("Time constant (ps) and reference temperature (K)");
 -    STYPE ("tau-t",   is->tau_t,      NULL);
 -    STYPE ("ref-t",   is->ref_t,      NULL);
 +    STYPE ("tau-t",   is->tau_t,      nullptr);
 +    STYPE ("ref-t",   is->ref_t,      nullptr);
      CTYPE ("pressure coupling");
      EETYPE("pcoupl",  ir->epc,        epcoupl_names);
      EETYPE("pcoupltype",  ir->epct,       epcoupltype_names);
      ITYPE ("nstpcouple", ir->nstpcouple,  -1);
      CTYPE ("Time constant (ps), compressibility (1/bar) and reference P (bar)");
      RTYPE ("tau-p",   ir->tau_p,  1.0);
 -    STYPE ("compressibility", dumstr[0],  NULL);
 -    STYPE ("ref-p",       dumstr[1],      NULL);
 +    STYPE ("compressibility", dumstr[0],  nullptr);
 +    STYPE ("ref-p",       dumstr[1],      nullptr);
      CTYPE ("Scaling of reference coordinates, No, All or COM");
      EETYPE ("refcoord-scaling", ir->refcoord_scaling, erefscaling_names);
  
      CCTYPE ("OPTIONS FOR QMMM calculations");
      EETYPE("QMMM", ir->bQMMM, yesno_names);
      CTYPE ("Groups treated Quantum Mechanically");
 -    STYPE ("QMMM-grps",  is->QMMM,          NULL);
 +    STYPE ("QMMM-grps",  is->QMMM,          nullptr);
      CTYPE ("QM method");
 -    STYPE("QMmethod",     is->QMmethod, NULL);
 +    STYPE("QMmethod",     is->QMmethod, nullptr);
      CTYPE ("QMMM scheme");
      EETYPE("QMMMscheme",  ir->QMMMscheme,    eQMMMscheme_names);
      CTYPE ("QM basisset");
 -    STYPE("QMbasis",      is->QMbasis, NULL);
 +    STYPE("QMbasis",      is->QMbasis, nullptr);
      CTYPE ("QM charge");
 -    STYPE ("QMcharge",    is->QMcharge, NULL);
 +    STYPE ("QMcharge",    is->QMcharge, nullptr);
      CTYPE ("QM multiplicity");
 -    STYPE ("QMmult",      is->QMmult, NULL);
 +    STYPE ("QMmult",      is->QMmult, nullptr);
      CTYPE ("Surface Hopping");
 -    STYPE ("SH",          is->bSH, NULL);
 +    STYPE ("SH",          is->bSH, nullptr);
      CTYPE ("CAS space options");
 -    STYPE ("CASorbitals",      is->CASorbitals,   NULL);
 -    STYPE ("CASelectrons",     is->CASelectrons,  NULL);
 -    STYPE ("SAon", is->SAon, NULL);
 -    STYPE ("SAoff", is->SAoff, NULL);
 -    STYPE ("SAsteps", is->SAsteps, NULL);
 +    STYPE ("CASorbitals",      is->CASorbitals,   nullptr);
 +    STYPE ("CASelectrons",     is->CASelectrons,  nullptr);
 +    STYPE ("SAon", is->SAon, nullptr);
 +    STYPE ("SAoff", is->SAoff, nullptr);
 +    STYPE ("SAsteps", is->SAsteps, nullptr);
      CTYPE ("Scale factor for MM charges");
      RTYPE ("MMChargeScaleFactor", ir->scalefactor, 1.0);
      CTYPE ("Optimization of QM subsystem");
 -    STYPE ("bOPT",          is->bOPT, NULL);
 -    STYPE ("bTS",          is->bTS, NULL);
 +    STYPE ("bOPT",          is->bOPT, nullptr);
 +    STYPE ("bTS",          is->bTS, nullptr);
  
      /* Simulated annealing */
      CCTYPE("SIMULATED ANNEALING");
      CTYPE ("Type of annealing for each temperature group (no/single/periodic)");
 -    STYPE ("annealing",   is->anneal,      NULL);
 +    STYPE ("annealing",   is->anneal,      nullptr);
      CTYPE ("Number of time points to use for specifying annealing in each group");
 -    STYPE ("annealing-npoints", is->anneal_npoints, NULL);
 +    STYPE ("annealing-npoints", is->anneal_npoints, nullptr);
      CTYPE ("List of times at the annealing points for each group");
 -    STYPE ("annealing-time",       is->anneal_time,       NULL);
 +    STYPE ("annealing-time",       is->anneal_time,       nullptr);
      CTYPE ("Temp. at each annealing point, for each group.");
 -    STYPE ("annealing-temp",  is->anneal_temp,  NULL);
 +    STYPE ("annealing-temp",  is->anneal_temp,  nullptr);
  
      /* Startup run */
      CCTYPE ("GENERATE VELOCITIES FOR STARTUP RUN");
      /* Energy group exclusions */
      CCTYPE ("ENERGY GROUP EXCLUSIONS");
      CTYPE ("Pairs of energy groups for which all non-bonded interactions are excluded");
 -    STYPE ("energygrp-excl", is->egpexcl,     NULL);
 +    STYPE ("energygrp-excl", is->egpexcl,     nullptr);
  
      /* Walls */
      CCTYPE ("WALLS");
      ITYPE ("nwall", ir->nwall, 0);
      EETYPE("wall-type",     ir->wall_type,   ewt_names);
      RTYPE ("wall-r-linpot", ir->wall_r_linpot, -1);
 -    STYPE ("wall-atomtype", is->wall_atomtype, NULL);
 -    STYPE ("wall-density",  is->wall_density,  NULL);
 +    STYPE ("wall-atomtype", is->wall_atomtype, nullptr);
 +    STYPE ("wall-density",  is->wall_density,  nullptr);
      RTYPE ("wall-ewald-zfac", ir->wall_ewald_zfac, 3);
  
      /* COM pulling */
      /* Interactive MD */
      ir->bIMD = FALSE;
      CCTYPE("Group to display and/or manipulate in interactive MD session");
 -    STYPE ("IMD-group", is->imd_grp, NULL);
 +    STYPE ("IMD-group", is->imd_grp, nullptr);
      if (is->imd_grp[0] != '\0')
      {
          snew(ir->imd, 1);
      CTYPE ("Orientation restraints force constant and tau for time averaging");
      RTYPE ("orire-fc",    ir->orires_fc,  0.0);
      RTYPE ("orire-tau",   ir->orires_tau, 0.0);
 -    STYPE ("orire-fitgrp", is->orirefitgrp,    NULL);
 +    STYPE ("orire-fitgrp", is->orirefitgrp,    nullptr);
      CTYPE ("Output frequency for trace(SD) and S to energy file");
      ITYPE ("nstorireout", ir->nstorireout, 100);
  
      /* free energy variables */
      CCTYPE ("Free energy variables");
      EETYPE("free-energy", ir->efep, efep_names);
 -    STYPE ("couple-moltype",  is->couple_moltype,  NULL);
 +    STYPE ("couple-moltype",  is->couple_moltype,  nullptr);
      EETYPE("couple-lambda0", opts->couple_lam0, couple_lam);
      EETYPE("couple-lambda1", opts->couple_lam1, couple_lam);
      EETYPE("couple-intramol", opts->bCoupleIntra, yesno_names);
      ITYPE ("init-lambda-state", fep->init_fep_state, -1);
      RTYPE ("delta-lambda", fep->delta_lambda, 0.0);
      ITYPE ("nstdhdl", fep->nstdhdl, 50);
 -    STYPE ("fep-lambdas", is->fep_lambda[efptFEP], NULL);
 -    STYPE ("mass-lambdas", is->fep_lambda[efptMASS], NULL);
 -    STYPE ("coul-lambdas", is->fep_lambda[efptCOUL], NULL);
 -    STYPE ("vdw-lambdas", is->fep_lambda[efptVDW], NULL);
 -    STYPE ("bonded-lambdas", is->fep_lambda[efptBONDED], NULL);
 -    STYPE ("restraint-lambdas", is->fep_lambda[efptRESTRAINT], NULL);
 -    STYPE ("temperature-lambdas", is->fep_lambda[efptTEMPERATURE], NULL);
 +    STYPE ("fep-lambdas", is->fep_lambda[efptFEP], nullptr);
 +    STYPE ("mass-lambdas", is->fep_lambda[efptMASS], nullptr);
 +    STYPE ("coul-lambdas", is->fep_lambda[efptCOUL], nullptr);
 +    STYPE ("vdw-lambdas", is->fep_lambda[efptVDW], nullptr);
 +    STYPE ("bonded-lambdas", is->fep_lambda[efptBONDED], nullptr);
 +    STYPE ("restraint-lambdas", is->fep_lambda[efptRESTRAINT], nullptr);
 +    STYPE ("temperature-lambdas", is->fep_lambda[efptTEMPERATURE], nullptr);
      ITYPE ("calc-lambda-neighbors", fep->lambda_neighbors, 1);
 -    STYPE ("init-lambda-weights", is->lambda_weights, NULL);
 +    STYPE ("init-lambda-weights", is->lambda_weights, nullptr);
      EETYPE("dhdl-print-energy", fep->edHdLPrintEnergy, edHdLPrintEnergy_names);
      RTYPE ("sc-alpha", fep->sc_alpha, 0.0);
      ITYPE ("sc-power", fep->sc_power, 1);
  
      /* Non-equilibrium MD stuff */
      CCTYPE("Non-equilibrium MD stuff");
 -    STYPE ("acc-grps",    is->accgrps,        NULL);
 -    STYPE ("accelerate",  is->acc,            NULL);
 -    STYPE ("freezegrps",  is->freeze,         NULL);
 -    STYPE ("freezedim",   is->frdim,          NULL);
 +    STYPE ("acc-grps",    is->accgrps,        nullptr);
 +    STYPE ("accelerate",  is->acc,            nullptr);
 +    STYPE ("freezegrps",  is->freeze,         nullptr);
 +    STYPE ("freezedim",   is->frdim,          nullptr);
      RTYPE ("cos-acceleration", ir->cos_accel, 0);
 -    STYPE ("deform",      is->deform,         NULL);
 +    STYPE ("deform",      is->deform,         nullptr);
  
      /* simulated tempering variables */
      CCTYPE("simulated tempering variables");
      }
  
      /* Electric fields */
 -    CCTYPE("Electric fields");
 -    CTYPE ("Format is number of terms (int) and for all terms an amplitude (real)");
 -    CTYPE ("and a phase angle (real)");
 -    STYPE ("E-x",     is->efield_x,   NULL);
 -    CTYPE ("Time dependent (pulsed) electric field. Format is omega, time for pulse");
 -    CTYPE ("peak, and sigma (width) for pulse. Sigma = 0 removes pulse, leaving");
 -    CTYPE ("the field to be a cosine function.");
 -    STYPE ("E-xt",    is->efield_xt,  NULL);
 -    STYPE ("E-y",     is->efield_y,   NULL);
 -    STYPE ("E-yt",    is->efield_yt,  NULL);
 -    STYPE ("E-z",     is->efield_z,   NULL);
 -    STYPE ("E-zt",    is->efield_zt,  NULL);
 +    {
 +        gmx::KeyValueTreeObject      convertedValues = flatKeyValueTreeFromInpFile(ninp, inp);
 +        gmx::KeyValueTreeTransformer transform;
 +        transform.rules()->addRule()
 +            .keyMatchType("/", gmx::StringCompareType::CaseAndDashInsensitive);
 +        mdModules->initMdpTransform(transform.rules());
 +        for (const auto &path : transform.mappedPaths())
 +        {
 +            GMX_ASSERT(path.size() == 1, "Inconsistent mapping back to mdp options");
 +            mark_einp_set(ninp, inp, path[0].c_str());
 +        }
 +        MdpErrorHandler              errorHandler(wi);
 +        auto                         result
 +                   = transform.transform(convertedValues, &errorHandler);
 +        ir->params = new gmx::KeyValueTreeObject(result.object());
 +        mdModules->adjustInputrecBasedOnModules(ir);
 +        errorHandler.setBackMapping(result.backMapping());
 +        mdModules->assignOptionsToModules(*ir->params, &errorHandler);
 +    }
  
      /* Ion/water position swapping ("computational electrophysiology") */
      CCTYPE("Ion/water position swapping for computational electrophysiology setups");
              snew(ir->swap->grp[i].molname, STRLEN);
          }
          CTYPE("Two index groups that contain the compartment-partitioning atoms");
 -        STYPE("split-group0", ir->swap->grp[eGrpSplit0].molname, NULL);
 -        STYPE("split-group1", ir->swap->grp[eGrpSplit1].molname, NULL);
 +        STYPE("split-group0", ir->swap->grp[eGrpSplit0].molname, nullptr);
 +        STYPE("split-group1", ir->swap->grp[eGrpSplit1].molname, nullptr);
          CTYPE("Use center of mass of split groups (yes/no), otherwise center of geometry is used");
          EETYPE("massw-split0", ir->swap->massw_split[0], yesno_names);
          EETYPE("massw-split1", ir->swap->massw_split[1], yesno_names);
  
          CTYPE("Name of solvent molecules");
 -        STYPE("solvent-group", ir->swap->grp[eGrpSolvent].molname, NULL);
 +        STYPE("solvent-group", ir->swap->grp[eGrpSolvent].molname, nullptr);
  
          CTYPE("Split cylinder: radius, upper and lower extension (nm) (this will define the channels)");
          CTYPE("Note that the split cylinder settings do not have an influence on the swapping protocol,");
              int ig = eSwapFixedGrpNR + i;
  
              sprintf(buf, "iontype%d-name", i);
 -            STYPE(buf, ir->swap->grp[ig].molname, NULL);
 +            STYPE(buf, ir->swap->grp[ig].molname, nullptr);
              sprintf(buf, "iontype%d-in-A", i);
              ITYPE(buf, ir->swap->grp[ig].nmolReq[0], -1);
              sprintf(buf, "iontype%d-in-B", i);
  
      /* User defined thingies */
      CCTYPE ("User defined thingies");
 -    STYPE ("user1-grps",  is->user1,          NULL);
 -    STYPE ("user2-grps",  is->user2,          NULL);
 +    STYPE ("user1-grps",  is->user1,          nullptr);
 +    STYPE ("user2-grps",  is->user2,          nullptr);
      ITYPE ("userint1",    ir->userint1,   0);
      ITYPE ("userint2",    ir->userint2,   0);
      ITYPE ("userint3",    ir->userint3,   0);
      RTYPE ("userreal4",   ir->userreal4,  0);
  #undef CTYPE
  
 -    write_inpfile(mdparout, ninp, inp, FALSE, wi);
 +    {
 +        gmx::TextOutputFile stream(mdparout);
 +        write_inpfile(&stream, mdparout, ninp, inp, FALSE, writeMdpHeader, wi);
 +        stream.close();
 +    }
 +
      for (i = 0; (i < ninp); i++)
      {
          sfree(inp[i].name);
          ir->nstcomm = 0;
      }
  
 -    opts->couple_moltype = NULL;
 +    opts->couple_moltype = nullptr;
      if (strlen(is->couple_moltype) > 0)
      {
          if (ir->efep != efepNO)
  
      /* ORIENTATION RESTRAINT PARAMETERS */
  
 -    if (opts->bOrire && str_nelem(is->orirefitgrp, MAXPTR, NULL) != 1)
 +    if (opts->bOrire && str_nelem(is->orirefitgrp, MAXPTR, nullptr) != 1)
      {
          warning_error(wi, "ERROR: Need one orientation restraint fit group\n");
      }
  
      if (strlen(is->deform) > 0 && ndeform != 6)
      {
-         sprintf(warn_buf, "Cannot parse exactly 6 box deformation velocities from string '%s'", is->deform);
-         warning_error(wi, warn_buf);
+         warning_error(wi, gmx::formatString("Cannot parse exactly 6 box deformation velocities from string '%s'", is->deform).c_str());
      }
      for (i = 0; i < 3; i++)
      {
@@@ -2782,7 -2721,7 +2782,7 @@@ static gmx_bool do_numbering(int natoms
      {
          /* All atoms are part of one (or no) group, no index required */
          groups->ngrpnr[gtype] = 0;
 -        groups->grpnr[gtype]  = NULL;
 +        groups->grpnr[gtype]  = nullptr;
      }
      else
      {
@@@ -2810,6 -2749,7 +2810,6 @@@ static void calc_nrdf(gmx_mtop_t *mtop
      double                 *nrdf_tc, *nrdf_vcm, nrdf_uc, *nrdf_vcm_sub;
      ivec                   *dof_vcm;
      gmx_mtop_atomloop_all_t aloop;
 -    t_atom                 *atom;
      int                     mb, mol, ftype, as;
      gmx_molblock_t         *molb;
      gmx_moltype_t          *molt;
  
      snew(nrdf2, natoms);
      aloop = gmx_mtop_atomloop_all_init(mtop);
 +    const t_atom *atom;
      while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
      {
          nrdf2[i] = 0;
      sfree(nrdf_vcm_sub);
  }
  
 -static void decode_cos(char *s, t_cosines *cosine)
 -{
 -    char              *t;
 -    char               format[STRLEN], f1[STRLEN];
 -    double             a, phi;
 -    int                i;
 -
 -    t = gmx_strdup(s);
 -    trim(t);
 -
 -    cosine->n   = 0;
 -    cosine->a   = NULL;
 -    cosine->phi = NULL;
 -    if (strlen(t))
 -    {
 -        if (sscanf(t, "%d", &(cosine->n)) != 1)
 -        {
 -            gmx_fatal(FARGS, "Cannot parse cosine multiplicity from string '%s'", t);
 -        }
 -        if (cosine->n <= 0)
 -        {
 -            cosine->n = 0;
 -        }
 -        else
 -        {
 -            snew(cosine->a, cosine->n);
 -            snew(cosine->phi, cosine->n);
 -
 -            sprintf(format, "%%*d");
 -            for (i = 0; (i < cosine->n); i++)
 -            {
 -                double  gmx_unused canary;
 -
 -                strcpy(f1, format);
 -                strcat(f1, "%lf%lf%lf");
 -                if (sscanf(t, f1, &a, &phi, &canary) != 2)
 -                {
 -                    gmx_fatal(FARGS, "Invalid input for electric field shift: '%s'", t);
 -                }
 -                cosine->a[i]   = a;
 -                cosine->phi[i] = phi;
 -                strcat(format, "%*lf%*lf");
 -            }
 -        }
 -    }
 -    sfree(t);
 -}
 -
  static gmx_bool do_egp_flag(t_inputrec *ir, gmx_groups_t *groups,
                              const char *option, const char *val, int flag)
  {
@@@ -3232,7 -3219,7 +3232,7 @@@ void do_index(const char* mdparin, cons
      {
          fprintf(stderr, "processing index file...\n");
      }
 -    if (ndx == NULL)
 +    if (ndx == nullptr)
      {
          snew(grps, 1);
          snew(grps->index, 1);
          {
              ir->opts.annealing[i]      = eannNO;
              ir->opts.anneal_npoints[i] = 0;
 -            ir->opts.anneal_time[i]    = NULL;
 -            ir->opts.anneal_temp[i]    = NULL;
 +            ir->opts.anneal_time[i]    = nullptr;
 +            ir->opts.anneal_temp[i]    = nullptr;
          }
          if (nSA > 0)
          {
          gmx_fatal(FARGS, "Can only have energy group pair tables in combination with user tables for VdW and/or Coulomb");
      }
  
 -    decode_cos(is->efield_x, &(ir->ex[XX]));
 -    decode_cos(is->efield_xt, &(ir->et[XX]));
 -    decode_cos(is->efield_y, &(ir->ex[YY]));
 -    decode_cos(is->efield_yt, &(ir->et[YY]));
 -    decode_cos(is->efield_z, &(ir->ex[ZZ]));
 -    decode_cos(is->efield_zt, &(ir->et[ZZ]));
 -
      for (i = 0; (i < grps->nr); i++)
      {
          sfree(gnames[i]);
@@@ -3964,7 -3958,7 +3964,7 @@@ check_combination_rule_differences(cons
       */
      tol = 1e-5;
      ptr = getenv("GMX_LJCOMB_TOL");
 -    if (ptr != NULL)
 +    if (ptr != nullptr)
      {
          double            dbl;
          double gmx_unused canary;
@@@ -4082,6 -4076,7 +4082,6 @@@ void triple_check(const char *mdparin, 
      rvec                      acc;
      gmx_mtop_atomloop_block_t aloopb;
      gmx_mtop_atomloop_all_t   aloop;
 -    t_atom                   *atom;
      ivec                      AbsRef;
      char                      warn_buf[STRLEN];
  
  
      bCharge = FALSE;
      aloopb  = gmx_mtop_atomloop_block_init(sys);
 +    const t_atom *atom;
      while (gmx_mtop_atomloop_block_next(aloopb, &atom, &nmol))
      {
          if (atom->q != 0 || atom->qB != 0)
          clear_rvec(acc);
          snew(mgrp, sys->groups.grps[egcACC].nr);
          aloop = gmx_mtop_atomloop_all_init(sys);
 +        const t_atom *atom;
          while (gmx_mtop_atomloop_all_next(aloop, &i, &atom))
          {
              mgrp[ggrpnr(&sys->groups, egcACC, i)] += atom->m;