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.
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.
#include <cmath>
+#include <string>
+
#include "gromacs/commandline/filenm.h"
#include "gromacs/fileio/gmxfio.h"
#include "gromacs/fileio/xvgr.h"
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.
*
// 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[],
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(§ion, "x");
efield_[YY].initMdpOptions(§ion, "y");
efield_[ZZ].initMdpOptions(§ion, "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)
{
{
/* 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
{
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] == ';'))
{
{
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;
+
}
}
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;
}
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
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,
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
#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
{
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();
}
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>
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>
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>
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
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>
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>
#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"
impl_->field_->mdpOptionProvider()->initMdpTransform(appliedForcesScope.rules());
}
+void MDModules::buildMdpOutput(KeyValueTreeObjectBuilder *builder)
+{
+ impl_->field_->mdpOptionProvider()->buildMdpOutput(builder);
+}
+
void MDModules::assignOptionsToModules(const KeyValueTreeObject ¶ms,
IKeyValueTreeErrorHandler *errorHandler)
{
namespace gmx
{
+class KeyValueTreeObjectBuilder;
+class KeyValueTreeObject;
class IKeyValueTreeErrorHandler;
class IKeyValueTreeTransformRules;
class IMDOutputProvider;
*/
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.
*
class IKeyValueTreeTransformRules;
class IOptionsContainerWithSections;
+class KeyValueTreeObjectBuilder;
/*! \libinternal \brief
* Interface for handling mdp/tpr input to a mdrun module.
* 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() {}
#ifndef GMX_UTILITY_KEYVALUETREEBUILDER_H
#define GMX_UTILITY_KEYVALUETREEBUILDER_H
+#include <initializer_list>
#include <string>
#include <utility>
#include <vector>
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.