Implemented grompp -po for options-style mdp handling
authorMark Abraham <mark.j.abraham@gmail.com>
Mon, 2 Jan 2017 09:50:23 +0000 (09:50 +0000)
committerMark Abraham <mark.j.abraham@gmail.com>
Fri, 14 Jul 2017 14:51:04 +0000 (16:51 +0200)
Previously options that were handled via a key-value tree (currently
only electric field options) were not written to the .mdp output.
Restoring this functionality will permit more widespread use of the
key-value tree handling during the transition period.

Restored mdp file comments for electric-field options, but the method
for writing them is hacky. This is probably good enough for
functionality that will disappear when we shift to key-value input.

Change-Id: I488b37ff72f70a6e145338fd9d57daa1c9e4de7e

16 files changed:
docs/doxygen/lib/mdmodules.md
src/gromacs/applied-forces/electricfield.cpp
src/gromacs/fileio/readinp.cpp
src/gromacs/fileio/readinp.h
src/gromacs/gmxpreprocess/keyvaluetreemdpwriter.cpp [new file with mode: 0644]
src/gromacs/gmxpreprocess/keyvaluetreemdpwriter.h [new file with mode: 0644]
src/gromacs/gmxpreprocess/readir.cpp
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_EmptyInputWorks.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_HandlesDifferentKindsOfMdpLines.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_HandlesOnlyCutoffScheme.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_ProducesOutputFromElectricField.xml
src/gromacs/gmxpreprocess/tests/refdata/GetIrTest_UserErrorsSilentlyTolerated.xml
src/gromacs/mdrunutility/mdmodules.cpp
src/gromacs/mdrunutility/mdmodules.h
src/gromacs/mdtypes/imdpoptionprovider.h
src/gromacs/utility/keyvaluetreebuilder.h

index 761329d48fbdd3d35562ee15d368f507fe57b36c..b0fa0b17478fc6908b622bc092ab41f5c8659e5a 100644 (file)
@@ -93,9 +93,6 @@ the modules) will do the following things to make mdp input work:
   defined conversions in the process.  This transformation is one-way only,
   although the framework keeps track of the origin of each value to provide
   sensible error messages that have the original mdp option name included.
-  Because the mapping is not two-way, there is some loss of functionality
-  related to mdp file handling in grompp (in particular, the `-po` option) for
-  options belonging to the new-style modules.
 
   It calls initMdpOptions() for the module(s), initializing a single Options
   object that has the input options.
@@ -117,6 +114,12 @@ the modules) will do the following things to make mdp input work:
   by `gmx grompp -po` cannot produce different behavior because of set/not-set
   differences.
 
+* grompp -po writes an mdp file that was equivalent to the input,
+  which is implemented by calling buildMdpOutput() for each module, to
+  prepare a builder object that is used with writeKeyValueTreeAsMdp().
+  As with the old flat tree, the values given by the user's input are
+  preserved, but not the ordering of options, or their formatting.
+
 * When grompp writes the tpr file, it writes the structured tree (after the
   default value and native value conversion) into the tpr file.
 
index e0d8dfa918e7fcd8a8fb0b27c58d5c224bf13e13..b5328ad7a9ea7025d19ae22ff1439bdd4bd587e8 100644 (file)
@@ -45,6 +45,8 @@
 
 #include <cmath>
 
+#include <string>
+
 #include "gromacs/commandline/filenm.h"
 #include "gromacs/fileio/gmxfio.h"
 #include "gromacs/fileio/xvgr.h"
@@ -94,6 +96,14 @@ class ElectricFieldData
             section.addOption(RealOption("t0").store(&t0_));
             section.addOption(RealOption("sigma").store(&sigma_));
         }
+        /*! \brief
+         * Creates mdp parameters for this field component.
+         */
+        void buildMdpOutput(KeyValueTreeObjectBuilder *builder, const std::string &name) const
+        {
+            builder->addUniformArray<real>("E-" + name, {1, a_, -1});
+            builder->addUniformArray<real>("E-" + name + "t", {omega_, t0_, sigma_});
+        }
 
         /*! \brief Evaluates this field component at given time.
          *
@@ -176,6 +186,7 @@ class ElectricField final : public IMDModule,
         // From IMdpOptionProvider
         void initMdpTransform(IKeyValueTreeTransformRules *transform) override;
         void initMdpOptions(IOptionsContainerWithSections *options) override;
+        void buildMdpOutput(KeyValueTreeObjectBuilder *builder) const override;
 
         // From IMDOutputProvider
         void initOutput(FILE *fplog, int nfile, const t_filenm fnm[],
@@ -318,13 +329,29 @@ void ElectricField::initMdpTransform(IKeyValueTreeTransformRules *rules)
 
 void ElectricField::initMdpOptions(IOptionsContainerWithSections *options)
 {
-    //CTYPE ("Format is E0 (V/nm), omega (1/ps), t0 (ps), sigma (ps) ");
     auto section = options->addSection(OptionSection("electric-field"));
     efield_[XX].initMdpOptions(&section, "x");
     efield_[YY].initMdpOptions(&section, "y");
     efield_[ZZ].initMdpOptions(&section, "z");
 }
 
+void ElectricField::buildMdpOutput(KeyValueTreeObjectBuilder *builder) const
+{
+    const char *const comment[] = {
+        "; Electric fields",
+        "; Format for E-x, etc. is: number of cosines (int; only 1 is supported),",
+        "; amplitude (real; V/nm), and phase (real; value is meaningless",
+        "; for a cosine of frequency 0.",
+        "; Format for E-xt, etc. is: omega (1/ps), time for the pulse peak (ps),",
+        "; and sigma (ps) width of the pulse. Sigma = 0 removes the pulse,",
+        "; leaving the field to be a cosine function."
+    };
+    builder->addValue<std::string>("comment-electric-field", joinStrings(comment, "\n"));
+    efield_[XX].buildMdpOutput(builder, "x");
+    efield_[YY].buildMdpOutput(builder, "y");
+    efield_[ZZ].buildMdpOutput(builder, "z");
+}
+
 void ElectricField::initOutput(FILE *fplog, int nfile, const t_filenm fnm[],
                                bool bAppendFiles, const gmx_output_env_t *oenv)
 {
index 9784114e83389ced4aa6e1640b2b12e7415e395a..76cea0683d952b92b2d8c55f2968c2fefb894cf9 100644 (file)
@@ -128,12 +128,13 @@ t_inpfile *read_inpfile(gmx::TextInputStream *stream, const char *fn, int *ninp,
         {
             /* add a new item */
             srenew(inp, ++countOfUniqueKeysFound);
-            inp[countOfUniqueKeysFound-1].inp_count  = 1;
-            inp[countOfUniqueKeysFound-1].count      = 0;
-            inp[countOfUniqueKeysFound-1].bObsolete  = FALSE;
-            inp[countOfUniqueKeysFound-1].bSet       = FALSE;
-            inp[countOfUniqueKeysFound-1].name       = gmx_strdup(tokens[0].c_str());
-            inp[countOfUniqueKeysFound-1].value      = gmx_strdup(tokens[1].c_str());
+            inp[countOfUniqueKeysFound-1].inp_count              = 1;
+            inp[countOfUniqueKeysFound-1].count                  = 0;
+            inp[countOfUniqueKeysFound-1].bObsolete              = FALSE;
+            inp[countOfUniqueKeysFound-1].bHandledAsKeyValueTree = FALSE;
+            inp[countOfUniqueKeysFound-1].bSet                   = FALSE;
+            inp[countOfUniqueKeysFound-1].name                   = gmx_strdup(tokens[0].c_str());
+            inp[countOfUniqueKeysFound-1].value                  = gmx_strdup(tokens[1].c_str());
         }
         else
         {
@@ -218,7 +219,10 @@ void write_inpfile(gmx::TextOutputStream *stream, const char *fn, int ninp, t_in
 
     for (int i = 0; (i < ninp); i++)
     {
-        if (inp[i].bSet)
+        if (inp[i].bHandledAsKeyValueTree)
+        {
+        }
+        else if (inp[i].bSet)
         {
             if (inp[i].name[0] == ';' || (strlen(inp[i].name) > 2 && inp[i].name[1] == ';'))
             {
@@ -297,6 +301,10 @@ void mark_einp_set(int ninp, t_inpfile *inp, const char *name)
     {
         inp[i].count = inp[0].inp_count++;
         inp[i].bSet  = TRUE;
+        /* Prevent mdp lines being written twice for
+           options that are handled via key-value trees. */
+        inp[i].bHandledAsKeyValueTree = TRUE;
+
     }
 }
 
@@ -311,8 +319,9 @@ static int get_einp(int *ninp, t_inpfile **inp, const char *name)
         notfound = TRUE;
         i        = (*ninp)++;
         srenew(*inp, (*ninp));
-        (*inp)[i].name = gmx_strdup(name);
-        (*inp)[i].bSet = TRUE;
+        (*inp)[i].name                   = gmx_strdup(name);
+        (*inp)[i].bSet                   = TRUE;
+        (*inp)[i].bHandledAsKeyValueTree = FALSE;
         if (i == 0)
         {
             (*inp)[i].inp_count = 1;
index 623f6dcc325e0eeffc7ea3bc2f154bdd492fd3c2..346cc9cd715258ad27a21509ddb709202547e36a 100644 (file)
@@ -52,13 +52,14 @@ class TextOutputStream;
 }
 
 typedef struct t_inpfile {
-    int      count;     /* sort order for output  */
-    gmx_bool bObsolete; /* whether it is an obsolete param value */
-    gmx_bool bSet;      /* whether it it has been read out */
-    char    *name;      /* name of the parameter */
-    char    *value;     /* parameter value string */
-    int      inp_count; /* number of einps read. Only valid for the first item
-                                                 in the inpfile list. */
+    int      count;                  /* sort order for output  */
+    gmx_bool bObsolete;              /* whether it is an obsolete param value */
+    gmx_bool bSet;                   /* whether it it has been read out */
+    gmx_bool bHandledAsKeyValueTree; /* whether it it has been handled with key-value machinery */
+    char    *name;                   /* name of the parameter */
+    char    *value;                  /* parameter value string */
+    int      inp_count;              /* number of einps read. Only valid for the first item
+                                                              in the inpfile list. */
 } t_inpfile;
 /* entry in input files (like .mdp files).
    Initally read in with read_inpfile, then filled in with missing values
@@ -108,6 +109,7 @@ void replace_inp_entry(int ninp, t_inpfile *inp,
 int search_einp(int ninp, const t_inpfile *inp, const char *name);
 /* Return the index of an .mdp field with the given name within the
  * inp array, if it exists. Return -1 if it does not exist. */
+
 void mark_einp_set(int ninp, t_inpfile *inp, const char *name);
 
 int get_eint(int *ninp, t_inpfile **inp, const char *name, int def,
diff --git a/src/gromacs/gmxpreprocess/keyvaluetreemdpwriter.cpp b/src/gromacs/gmxpreprocess/keyvaluetreemdpwriter.cpp
new file mode 100644 (file)
index 0000000..5285d3c
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017, 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.
+ */
+/*! \internal \file
+ * \brief
+ * Defines a function to write a flat key-value tree to look like
+ * old-style mdp output.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_gmxpreprocess
+ */
+#include "gmxpre.h"
+
+#include "keyvaluetreemdpwriter.h"
+
+#include <string>
+
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/keyvaluetree.h"
+#include "gromacs/utility/strconvert.h"
+#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
+
+namespace gmx
+{
+
+void writeKeyValueTreeAsMdp(TextWriter               *writer,
+                            const KeyValueTreeObject &tree)
+{
+    for (const auto &prop : tree.properties())
+    {
+        const auto &value = prop.value();
+        GMX_RELEASE_ASSERT(!value.isObject(), "Only flat key-value trees can be written as mdp");
+
+        // Recognize a special key prefix that identifies comment
+        // lines. This mechanism is not pretty, but our plan is to
+        // write key-value trees, rather than old-style mdp, and
+        // comments will need different handling then.
+        if (prop.key().compare(0, 7, "comment") == 0)
+        {
+            GMX_RELEASE_ASSERT(prop.value().isType<std::string>(), "Comments must have string-typed values");
+            auto comment = prop.value().cast<std::string>();
+            // TODO Consider implementing an MdpTextWriter that can
+            // format an array of strings suitably, e.g. by prefixing
+            // each line like "; %s". We'd need to implement arrays of
+            // objects, such a comment key to be able to refer to an
+            // array of strings. Also, we'd want to have a plan to
+            // write such comments in whatever replaces the mdp
+            // format.
+            writer->writeLine(comment);
+        }
+        else
+        {
+            writer->writeString(formatString("%-24s = ", prop.key().c_str()));
+            if (value.isArray())
+            {
+                bool first = true;
+                for (const auto &elem : value.asArray().values())
+                {
+                    GMX_RELEASE_ASSERT(!elem.isObject() && !elem.isArray(),
+                                       "Arrays of objects not currently implemented");
+                    if (!first)
+                    {
+                        writer->writeString(" ");
+                    }
+                    writer->writeString(simpleValueToString(elem));
+                    first = false;
+                }
+            }
+            else
+            {
+                writer->writeString(simpleValueToString(value));
+            }
+            writer->writeLine();
+        }
+    }
+}
+
+} // namespace gmx
diff --git a/src/gromacs/gmxpreprocess/keyvaluetreemdpwriter.h b/src/gromacs/gmxpreprocess/keyvaluetreemdpwriter.h
new file mode 100644 (file)
index 0000000..745e8db
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017, 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.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares a function to write a flat key-value tree to look like
+ * old-style mdp output.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_gmxpreprocess
+ */
+#ifndef GMX_GMXPREPROCESS_KEYVALUETREEMDPWRITER_H
+#define GMX_GMXPREPROCESS_KEYVALUETREEMDPWRITER_H
+
+namespace gmx
+{
+
+class KeyValueTreeObject;
+class TextWriter;
+
+/*! \brief Write a flat key-value \c tree to \c writer in mdp style.
+ *
+ * Sub-objects will output nothing, so they can be used to
+ * contain a special key-value pair to create a comment, as
+ * well as the normal key and value. The comment pair will
+ * have a key of "comment", and the value will be used as a
+ * comment (if non-empty). */
+void writeKeyValueTreeAsMdp(TextWriter               *writer,
+                            const KeyValueTreeObject &tree);
+
+} // namespace gmx
+
+#endif
index fe78463b0babc40a86b581d00053d28294a969a5..0af04f7cc3641543fe071d8af0a415f1ab289157 100644 (file)
@@ -51,6 +51,7 @@
 #include "gromacs/fileio/warninp.h"
 #include "gromacs/gmxlib/chargegroup.h"
 #include "gromacs/gmxlib/network.h"
+#include "gromacs/gmxpreprocess/keyvaluetreemdpwriter.h"
 #include "gromacs/gmxpreprocess/toputil.h"
 #include "gromacs/math/functions.h"
 #include "gromacs/math/units.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/ikeyvaluetreeerror.h"
 #include "gromacs/utility/keyvaluetree.h"
+#include "gromacs/utility/keyvaluetreebuilder.h"
 #include "gromacs/utility/keyvaluetreetransform.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/stringcompare.h"
 #include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/textwriter.h"
 
 #define MAXPTR 254
 #define NOGID  255
@@ -2358,6 +2361,15 @@ void get_ir(const char *mdparin, const char *mdparout,
     {
         gmx::TextOutputFile stream(mdparout);
         write_inpfile(&stream, mdparout, ninp, inp, FALSE, writeMdpHeader, wi);
+
+        // Transform module data into a flat key-value tree for output.
+        gmx::KeyValueTreeBuilder       builder;
+        gmx::KeyValueTreeObjectBuilder builderObject = builder.rootObject();
+        mdModules->buildMdpOutput(&builderObject);
+        {
+            gmx::TextWriter writer(&stream);
+            writeKeyValueTreeAsMdp(&writer, builder.build());
+        }
         stream.close();
     }
 
index d6441ce728f23944b627ba20627857c24f23e6af..ec270fffb3685fe21369832c49331decf83f0fb9 100644 (file)
@@ -333,5 +333,18 @@ userreal1                = 0
 userreal2                = 0
 userreal3                = 0
 userreal4                = 0
+; Electric fields
+; Format for E-x, etc. is: number of cosines (int; only 1 is supported),
+; amplitude (real; V/nm), and phase (real; value is meaningless
+; for a cosine of frequency 0.
+; Format for E-xt, etc. is: omega (1/ps), time for the pulse peak (ps),
+; and sigma (ps) width of the pulse. Sigma = 0 removes the pulse,
+; leaving the field to be a cosine function.
+E-x                      = 1 0 -1
+E-xt                     = 0 0 0
+E-y                      = 1 0 -1
+E-yt                     = 0 0 0
+E-z                      = 1 0 -1
+E-zt                     = 0 0 0
 </String>
 </ReferenceData>
index da5d9ac60bbdf415e4b5adc21627050cf964ec79..40156a2313873bd8c0d6400ac94c7398374aab9a 100644 (file)
@@ -333,5 +333,18 @@ userreal1                = 0
 userreal2                = 0
 userreal3                = 0
 userreal4                = 0
+; Electric fields
+; Format for E-x, etc. is: number of cosines (int; only 1 is supported),
+; amplitude (real; V/nm), and phase (real; value is meaningless
+; for a cosine of frequency 0.
+; Format for E-xt, etc. is: omega (1/ps), time for the pulse peak (ps),
+; and sigma (ps) width of the pulse. Sigma = 0 removes the pulse,
+; leaving the field to be a cosine function.
+E-x                      = 1 0 -1
+E-xt                     = 0 0 0
+E-y                      = 1 0 -1
+E-yt                     = 0 0 0
+E-z                      = 1 0 -1
+E-zt                     = 0 0 0
 </String>
 </ReferenceData>
index eafe954be0f81648786f942b9059dfbfba1137d0..7770c96d12fafb46b840f3dd4ac616bda5abb0a8 100644 (file)
@@ -333,5 +333,18 @@ userreal1                = 0
 userreal2                = 0
 userreal3                = 0
 userreal4                = 0
+; Electric fields
+; Format for E-x, etc. is: number of cosines (int; only 1 is supported),
+; amplitude (real; V/nm), and phase (real; value is meaningless
+; for a cosine of frequency 0.
+; Format for E-xt, etc. is: omega (1/ps), time for the pulse peak (ps),
+; and sigma (ps) width of the pulse. Sigma = 0 removes the pulse,
+; leaving the field to be a cosine function.
+E-x                      = 1 0 -1
+E-xt                     = 0 0 0
+E-y                      = 1 0 -1
+E-yt                     = 0 0 0
+E-z                      = 1 0 -1
+E-zt                     = 0 0 0
 </String>
 </ReferenceData>
index 8920095924840f7559de2782e51f41d177e8e126..5122199144dd5c66d55fd4459f030def2cdcffd1 100644 (file)
@@ -316,7 +316,6 @@ simulated-tempering      = no
 simulated-tempering-scaling = geometric
 sim-temp-low             = 300
 sim-temp-high            = 300
-E-x                      = 1 1.2 -1
 
 ; Ion/water position swapping for computational electrophysiology setups
 ; Swap positions along direction: no, X, Y, Z
@@ -334,5 +333,18 @@ userreal1                = 0
 userreal2                = 0
 userreal3                = 0
 userreal4                = 0
+; Electric fields
+; Format for E-x, etc. is: number of cosines (int; only 1 is supported),
+; amplitude (real; V/nm), and phase (real; value is meaningless
+; for a cosine of frequency 0.
+; Format for E-xt, etc. is: omega (1/ps), time for the pulse peak (ps),
+; and sigma (ps) width of the pulse. Sigma = 0 removes the pulse,
+; leaving the field to be a cosine function.
+E-x                      = 1 1.2 -1
+E-xt                     = 0 0 0
+E-y                      = 1 0 -1
+E-yt                     = 0 0 0
+E-z                      = 1 0 -1
+E-zt                     = 0 0 0
 </String>
 </ReferenceData>
index d6441ce728f23944b627ba20627857c24f23e6af..ec270fffb3685fe21369832c49331decf83f0fb9 100644 (file)
@@ -333,5 +333,18 @@ userreal1                = 0
 userreal2                = 0
 userreal3                = 0
 userreal4                = 0
+; Electric fields
+; Format for E-x, etc. is: number of cosines (int; only 1 is supported),
+; amplitude (real; V/nm), and phase (real; value is meaningless
+; for a cosine of frequency 0.
+; Format for E-xt, etc. is: omega (1/ps), time for the pulse peak (ps),
+; and sigma (ps) width of the pulse. Sigma = 0 removes the pulse,
+; leaving the field to be a cosine function.
+E-x                      = 1 0 -1
+E-xt                     = 0 0 0
+E-y                      = 1 0 -1
+E-yt                     = 0 0 0
+E-z                      = 1 0 -1
+E-zt                     = 0 0 0
 </String>
 </ReferenceData>
index c74726b9e5a1e5ec0a41fd24ed47a755d6b11e66..2ee50c8928047a6021bcae1d4601a3ac9a6c82f0 100644 (file)
@@ -48,6 +48,7 @@
 #include "gromacs/options/optionsection.h"
 #include "gromacs/options/treesupport.h"
 #include "gromacs/utility/keyvaluetree.h"
+#include "gromacs/utility/keyvaluetreebuilder.h"
 #include "gromacs/utility/keyvaluetreetransform.h"
 #include "gromacs/utility/smalloc.h"
 
@@ -100,6 +101,11 @@ void MDModules::initMdpTransform(IKeyValueTreeTransformRules *rules)
     impl_->field_->mdpOptionProvider()->initMdpTransform(appliedForcesScope.rules());
 }
 
+void MDModules::buildMdpOutput(KeyValueTreeObjectBuilder *builder)
+{
+    impl_->field_->mdpOptionProvider()->buildMdpOutput(builder);
+}
+
 void MDModules::assignOptionsToModules(const KeyValueTreeObject  &params,
                                        IKeyValueTreeErrorHandler *errorHandler)
 {
index 0fbdf88b62f468edcf685c0b38255b578c351635..fe0b75fc058511526929d9e8e472729b83d09c7e 100644 (file)
@@ -52,6 +52,8 @@ struct t_inputrec;
 namespace gmx
 {
 
+class KeyValueTreeObjectBuilder;
+class KeyValueTreeObject;
 class IKeyValueTreeErrorHandler;
 class IKeyValueTreeTransformRules;
 class IMDOutputProvider;
@@ -101,6 +103,18 @@ class MDModules
          */
         void initMdpTransform(IKeyValueTreeTransformRules *rules);
 
+        /*! \brief Initializes a builder of flat mdp-style key-value pairs
+         * suitable for output.
+         *
+         * If used as input to initMdpTransform(), the key-value pairs
+         * resulting from this function would leave the module
+         * settings unchanged.
+         *
+         * Once the transition from mdp to key-value input is
+         * complete, this method will probably not exist.
+         */
+        void buildMdpOutput(KeyValueTreeObjectBuilder *builder);
+
         /*! \brief
          * Sets input parameters from `params` for each module.
          *
index 5f33027e58e6b7d052b4071cc448bb93b18f17e7..d61916610b9e64b965fdcb2c5ce09f95be49734e 100644 (file)
@@ -49,6 +49,7 @@ namespace gmx
 
 class IKeyValueTreeTransformRules;
 class IOptionsContainerWithSections;
+class KeyValueTreeObjectBuilder;
 
 /*! \libinternal \brief
  * Interface for handling mdp/tpr input to a mdrun module.
@@ -98,6 +99,8 @@ class IMdpOptionProvider
          * module.
          */
         virtual void initMdpOptions(IOptionsContainerWithSections *options) = 0;
+        //! Prepares to write a flat key-value tree like an mdp file.
+        virtual void buildMdpOutput(KeyValueTreeObjectBuilder *builder) const = 0;
 
     protected:
         ~IMdpOptionProvider() {}
index be52edb47e2c8e3969de864b5b4cc95f4acd6886..145f4935d266c6af4faa407c8781e8eeabb5c619 100644 (file)
@@ -51,6 +51,7 @@
 #ifndef GMX_UTILITY_KEYVALUETREEBUILDER_H
 #define GMX_UTILITY_KEYVALUETREEBUILDER_H
 
+#include <initializer_list>
 #include <string>
 #include <utility>
 #include <vector>
@@ -332,6 +333,23 @@ class KeyValueTreeObjectBuilder
             auto iter = addProperty(key, KeyValueTreeBuilder::createValue<KeyValueTreeArray>());
             return KeyValueTreeUniformArrayBuilder<T>(&iter->second.asArray());
         }
+        /*! \brief
+         * Adds an array-valued property with uniform value types with given
+         * key and values.
+         *
+         * \tparam T  Type for all values in the array.
+         *
+         * The array is created to contain the values from `values`.
+         */
+        template <typename T>
+        void addUniformArray(const std::string &key, std::initializer_list<T> values)
+        {
+            auto builder = addUniformArray<T>(key);
+            for (const auto &value : values)
+            {
+                builder.addValue(value);
+            }
+        }
         /*! \brief
          * Adds an array-valued property with objects in the array with given
          * key.