gmxapi/export_context.cpp
gmxapi/export_exceptions.cpp
gmxapi/export_system.cpp
+ gmxapi/export_tprfile.cpp
gmxapi/pycontext.cpp
gmxapi/pysystem.cpp
+ gmxapi/tprfile.cpp
)
pybind11_add_module(_gmxapi
--- /dev/null
+/*
+ * 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.
+ */
+/*! \file
+ * \brief C++ exceptions for the gmxapi compatibility tools.
+ *
+ * Used internally for the gmxapi compatibility helpers that manage type
+ * mappings of older GROMACS structures. The long-term disposition of this
+ * code is uncertain, but the headers are not likely to be public. If they
+ * do persist in some form, we can integrate the exception heirarchy into
+ * whatever module takes ownership of this code.
+ *
+ * Exceptions defined here should only be caught by code that understands the
+ * implementation details of these compatibility tools. Exposure of these
+ * exceptions outside of the installed object files should be treated as a bug.
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ * \ingroup module_python
+ */
+
+#ifndef GMXAPICOMPAT_EXCEPTIONS_H
+#define GMXAPICOMPAT_EXCEPTIONS_H
+
+#include <exception>
+#include <string>
+
+namespace gmxapicompat
+{
+
+/*!
+ * \brief Generic exception class for gmxapicompat.
+ */
+class Exception : public std::exception
+{
+ public:
+ using std::exception::exception;
+
+ explicit Exception(const std::string &message) :
+ message_ {message}
+ {}
+ explicit Exception(const char* message) : Exception(std::string(message)) {}
+
+ const char *what() const noexcept override
+ {
+ return message_.c_str();
+ }
+
+ private:
+ std::string message_;
+};
+
+/*!
+ * \brief The key name provided for a key-value mapping is invalid.
+ */
+class KeyError : public Exception
+{
+ using Exception::Exception;
+};
+
+/*!
+ * \brief The value provided for a key-value mapping is invalid.
+ */
+class ValueError : public Exception
+{
+ using Exception::Exception;
+};
+
+/*!
+ * \brief Value provided for a key-value mapping is of an incompatible type.
+ */
+class TypeError : public Exception
+{
+ using Exception::Exception;
+};
+
+} // end namespace gmxapicompat
+#endif //GMXAPICOMPAT_EXCEPTIONS_H
--- /dev/null
+/*
+ * 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.
+ */
+/*! \file
+ * \brief Exports TPR I/O tools during Python module initialization.
+ *
+ * Provides _gmxapi.SimulationParameters and _gmxapi.TprFile classes, as well
+ * as module functions read_tprfile, write_tprfile, copy_tprfile, and rewrite_tprfile.
+ *
+ * TprFile is a Python object that holds a gmxapicompat::TprReadHandle.
+ *
+ * SimulationParameters is the Python type for data sources providing the
+ * simulation parameters aspect of input to simulation operations.
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ * \ingroup module_python
+ */
+
+#include "gmxapi/exceptions.h"
+
+#include "module.h"
+#include "mdparams.h"
+#include "tprfile.h"
+
+namespace gmxpy
+{
+
+
+void detail::export_tprfile(pybind11::module &module)
+{
+ namespace py = pybind11;
+ using gmxapicompat::GmxMdParams;
+ using gmxapicompat::TprReadHandle;
+ using gmxapicompat::readTprFile;
+
+ py::class_<GmxMdParams> mdparams(module, "SimulationParameters");
+ // We don't want Python users to create invalid params objects, so don't
+ // export a constructor until we can default initialize a valid one.
+ // mdparams.def(py::init());
+ mdparams.def("extract",
+ [](const GmxMdParams &self)
+ {
+ py::dict dictionary;
+ for (const auto &key: gmxapicompat::keys(self))
+ {
+ try
+ {
+ // TODO: More complete typing and dispatching.
+ // This only handles the two types described in the initial implementation.
+ // Less trivial types (strings, maps, arrays) warrant additional
+ // design discussion before being exposed through an interface
+ // like this one.
+ // Also reference https://redmine.gromacs.org/issues/2993
+
+ // We can use templates and/or tag dispatch in a more complete
+ // future implementation.
+ const auto ¶mType = gmxapicompat::mdParamToType(key);
+ if (gmxapicompat::isFloat(paramType))
+ {
+ dictionary[key.c_str()] = extractParam(self, key, double());
+ }
+ else if (gmxapicompat::isInt(paramType))
+ {
+ dictionary[key.c_str()] = extractParam(self, key, int64_t());
+ }
+ }
+ catch (const gmxapicompat::ValueError &e)
+ {
+ throw gmxapi::ProtocolError(std::string("Unknown parameter: ") + key);
+ }
+ }
+ return dictionary;
+ },
+ "Get a dictionary of the parameters.");
+
+ // Overload a setter for each known type and None
+ mdparams.def("set",
+ [](GmxMdParams* self, const std::string &key, py::int_ value)
+ {
+ gmxapicompat::setParam(self, key, py::cast<int64_t>(value));
+ },
+ py::arg("key").none(false),
+ py::arg("value").none(false),
+ "Use a dictionary to update simulation parameters.");
+ mdparams.def("set",
+ [](GmxMdParams* self, const std::string &key, py::float_ value)
+ {
+ gmxapicompat::setParam(self, key, py::cast<double>(value));
+ },
+ py::arg("key").none(false),
+ py::arg("value").none(false),
+ "Use a dictionary to update simulation parameters.");
+ mdparams.def("set",
+ [](GmxMdParams* self, const std::string &key, py::none)
+ {
+ // unsetParam(self, key);
+ },
+ py::arg("key").none(false),
+ py::arg("value"),
+ "Use a dictionary to update simulation parameters.");
+
+
+ py::class_<TprReadHandle> tprfile(module, "TprFile");
+ tprfile.def("params",
+ [](const TprReadHandle &self)
+ {
+ auto params = gmxapicompat::getMdParams(self);
+ return params;
+ });
+
+ module.def("read_tprfile",
+ &readTprFile,
+ py::arg("filename"),
+ "Get a handle to a TPR file resource for a given file name.");
+
+ module.def("write_tprfile",
+ [](std::string filename, const GmxMdParams ¶meterObject)
+ {
+ auto tprReadHandle = gmxapicompat::getSourceFileHandle(parameterObject);
+ auto params = gmxapicompat::getMdParams(tprReadHandle);
+ auto structure = gmxapicompat::getStructureSource(tprReadHandle);
+ auto state = gmxapicompat::getSimulationState(tprReadHandle);
+ auto topology = gmxapicompat::getTopologySource(tprReadHandle);
+ gmxapicompat::writeTprFile(filename, params, structure, state, topology);
+ },
+ py::arg("filename").none(false),
+ py::arg("parameters"),
+ "Write a new TPR file with the provided data.");
+
+ module.def("copy_tprfile",
+ [](const gmxapicompat::TprReadHandle &input, std::string outFile)
+ {
+ return gmxpy::copy_tprfile(input, outFile);
+ },
+ py::arg("source"),
+ py::arg("destination"),
+ "Copy a TPR file from `source` to `destination`."
+ );
+
+ module.def("rewrite_tprfile",
+ [](std::string input, std::string output, double end_time)
+ {
+ return gmxpy::rewrite_tprfile(input, output, end_time);
+ },
+ py::arg("source"),
+ py::arg("destination"),
+ py::arg("end_time"),
+ "Copy a TPR file from `source` to `destination`, replacing `nsteps` with `end_time`.");
+}
+
+} // end namespace gmxpy
--- /dev/null
+/*
+ * 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.
+ */
+#ifndef GMXPY_MDPARAMS_H
+#define GMXPY_MDPARAMS_H
+
+/*! \file
+ * \brief Compatibility header for functionality differences in gmxapi releases.
+ *
+ * Also handle the transitioning installed headers from GROMACS 2019 moving forward.
+ *
+ * \todo Configure for gmxapi 0.0.7, 0.0.8, GROMACS 2019, GROMACS master...
+ *
+ * \defgroup gmxapi_compat
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ * \ingroup gmxapi_compat
+ */
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "compat_exceptions.h"
+
+struct t_inputrec;
+
+/*!
+ * \brief Compatibility code for features that may not be in gmxapi yet.
+ */
+namespace gmxapicompat
+{
+
+
+/*!
+ * \brief Label the types recognized by gmxapi.
+ *
+ * Provide an enumeration to aid in translating data between languages, APIs,
+ * and storage formats.
+ *
+ * \todo The spec should explicitly map these to types in APIs already used.
+ * e.g. MPI, Python, numpy, GROMACS, JSON, etc.
+ * \todo Actually check the size of the types.
+ *
+ * \see https://redmine.gromacs.org/issues/2993 for discussion.
+ */
+enum class GmxapiType
+{
+ NULLTYPE, //! Reserved
+ MAP, //! Mapping of key name (string) to a value of some MdParamType
+ BOOL, //! Boolean logical type
+ INT64, //! 64-bit integer type
+ FLOAT64, //! 64-bit float type
+ STRING, //! string with metadata
+ NDARRAY, //! multi-dimensional array with metadata
+};
+
+
+/*!
+ * \brief Static map of GROMACS MDP user input to normalized "type".
+ *
+ * Note that only fields present in the TPR file are named. Additional names
+ * may be accepted as mdp file entries, but we cannot discern which parameter
+ * name was used from inspection of the TPR file and this is an interim solution
+ * that does not need to support a complete MDP file converter.
+ */
+const std::map<std::string, GmxapiType> simulationParameterTypeMap();
+
+const std::map<std::string, bool t_inputrec::*> boolParams();
+const std::map<std::string, int t_inputrec::*> int32Params();
+const std::map<std::string, float t_inputrec::*> float32Params();
+const std::map<std::string, double t_inputrec::*> float64Params();
+const std::map<std::string, int64_t t_inputrec::*> int64Params();
+
+/*!
+ * \brief Static mapping of parameter names to gmxapi types for GROMACS.
+ *
+ * \param name MDP entry name.
+ * \return enumeration value for known parameters.
+ *
+ * \throws gmxapi_compat::ValueError for parameters with no mapping.
+ */
+GmxapiType mdParamToType(const std::string &name);
+
+// Forward declaration for private implementation class for GmxMdParams
+class GmxMdParamsImpl;
+
+/*!
+ * \brief Handle / manager for GROMACS molecular computation input parameters.
+ *
+ * Interface should be consistent with MDP file entries, but data maps to TPR
+ * file interface. For type safety and simplicity, we don't have generic operator
+ * accessors. Instead, we have templated accessors that throw exceptions when
+ * there is trouble.
+ *
+ * When MDP input is entirely stored in a key-value tree, this class can be a
+ * simple adapter or wrapper. Until then, we need a manually maintained mapping
+ * of MDP entries to TPR data.
+ *
+ * Alternatively, we could update the infrastructure used by list_tpx to provide
+ * more generic output, but our efforts may be better spent in updating the
+ * infrastructure for the key-value tree input system.
+ */
+class GmxMdParams
+{
+ public:
+ GmxMdParams();
+ ~GmxMdParams();
+ GmxMdParams(const GmxMdParams &) = delete;
+ GmxMdParams &operator=(const GmxMdParams &) = delete;
+ GmxMdParams(GmxMdParams &&) noexcept;
+ GmxMdParams &operator=(GmxMdParams &&) noexcept;
+
+ std::unique_ptr<GmxMdParamsImpl> params_;
+};
+
+/*!
+ * \brief A set of overloaded functions to fetch parameters of the indicated type, if possible.
+ *
+ * \param params Handle to a parameters structure from which to extract.
+ * \param name Parameter name
+ * \param (tag) type for dispatch
+ *
+ * Could be used for dispatch and/or some sort of templating in the future, but
+ * invoked directly for now.
+ */
+int extractParam(const gmxapicompat::GmxMdParams ¶ms, const std::string &name, int);
+int64_t extractParam(const gmxapicompat::GmxMdParams& params, const std::string& name, int64_t);
+float extractParam(const gmxapicompat::GmxMdParams ¶ms, const std::string &name, float);
+double extractParam(const gmxapicompat::GmxMdParams ¶ms, const std::string &name, double);
+
+void setParam(gmxapicompat::GmxMdParams* params, const std::string &name, double value);
+void setParam(gmxapicompat::GmxMdParams* params, const std::string &name, int64_t value);
+// TODO: unsetParam
+
+
+// Anonymous namespace to confine helper function definitions to file scope.
+namespace
+{
+
+bool isFloat(GmxapiType dataType)
+{
+ return (dataType == GmxapiType::FLOAT64);
+}
+
+bool isInt(GmxapiType dataType)
+{
+ return (dataType == GmxapiType::INT64);
+}
+
+} // end anonymous namespace
+
+} // end namespace gmxapicompat
+
+#endif //GMXPY_MDPARAMS_H
// Get bindings exported by the various components.
export_context(m);
export_system(m);
+ export_tprfile(m);
} // end pybind11 module
* \brief Declares symbols to be exported to gmxapi._gmxapi Python module.
*
* Declares namespace gmxpy, used internally in the C++ extension.
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
* \ingroup module_python
*/
#ifndef GMXPY_MODULE_H
void export_context(pybind11::module &m);
void export_exceptions(pybind11::module &m);
void export_system(pybind11::module &m);
+void export_tprfile(pybind11::module &module);
+
+// Forward declaration for the module initialization.
+// TODO: is there a better way to avoid the warning generated by the pybind macro?
+// e.g. no previous prototype for function 'PyInit__gmxapi' [-Wmissing-prototypes]
+extern "C" __attribute__((visibility("default"))) PyObject* PyInit__gmxapi();
+void pybind11_init__gmxapi(pybind11::module &m);
} // end namespace gmxpy::detail
--- /dev/null
+#
+# 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.
--- /dev/null
+#
+# 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.
+
+"""Provide the high-level interface to the file i/o behaviors in the package.
+"""
+# The submodule is named "fileio" instead of "io" to avoid a
+# namespace collision with a standard Python module on the default path.
+
+import typing
+
+__all__ = ['TprFile', 'read_tpr', 'write_tpr_file']
+
+import os
+
+from gmxapi import exceptions
+from gmxapi import _gmxapi
+
+
+class TprFile(object):
+ """Handle to a GROMACS simulation run input file.
+
+ TprFile objects do not have a public interface. The class is used internally
+ to manage simulation input data structures.
+
+ Attributes:
+ filename (str): Name of the file with which the object was initialized.
+ mode: File access mode from object creation.
+
+ """
+
+ def __init__(self, filename: str = None, mode: str = 'r'):
+ """Open a TPR file.
+
+ File access mode is indicated by 'r' for read-only access.
+
+ Args:
+ filename (str): Path to a run input file (e.g. 'myfile.tpr')
+ mode (str): File access mode.
+
+ Note:
+ Currently, TPR files are read-only from the Python interface.
+
+ Example:
+
+ >>> import gmxapi as gmx
+ >>> filehandle = gmx.TprFile(filename, 'r')
+
+ """
+ if filename is None:
+ raise exceptions.UsageError("TprFile objects must be associated with a file.")
+ if mode != 'r':
+ raise exceptions.UsageError("TPR files only support read-only access.")
+ self.mode = mode
+ self.filename = filename
+ self._tprFileHandle = None
+
+ def close(self):
+ # self._tprFileHandle.close()
+ self._tprFileHandle = None
+
+ def __repr__(self):
+ return "gmx.fileio.TprFile('{}', '{}')".format(self.filename, self.mode)
+
+ def __enter__(self):
+ self._tprFileHandle = _gmxapi.read_tprfile(self.filename)
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.close()
+ return
+
+
+class _NodeOutput(object):
+ """Implement the `output` attribute of a simulation input node.
+
+ Attributes:
+ parameters: Simulation parameters for (re)written TPR file.
+ structure: Atomic details (not yet implemented)
+ topology: Molecular force field details (not yet implemented)
+ state: Simulation state information (not yet implemented)
+
+ """
+
+ def __init__(self, parameters=None, structure=None, topology=None, state=None):
+ """Initialize getters for output ports."""
+ self.__tprfile = parameters
+
+ @property
+ def parameters(self):
+ with self.__tprfile as fh:
+ params = fh._tprFileHandle.params()
+ return params
+
+ @property
+ def structure(self):
+ raise exceptions.ApiError("property not implemented.")
+
+ @property
+ def topology(self):
+ raise exceptions.ApiError("property not implemented.")
+
+ @property
+ def state(self):
+ raise exceptions.ApiError("property not implemented.")
+
+
+class _SimulationInput(object):
+ """
+ Simulation input interface for a TPR file read by gmx.fileio.read_tpr()
+
+ Attributes:
+ parameters: Simulation parameters for (re)written TPR file.
+ structure: Atomic details (not yet implemented)
+ topology: Molecular force field details (not yet implemented)
+ state: Simulation state information (not yet implemented)
+
+ """
+
+ def __init__(self, tprfile: typing.Union[str, TprFile]):
+ if not isinstance(tprfile, TprFile):
+ try:
+ tprfile = TprFile(tprfile)
+ except Exception as e:
+ # This class is an implementation detail of TPR file I/O...
+ raise exceptions.ApiError("Must be initialized from a TprFile.") from e
+ assert isinstance(tprfile, TprFile)
+ self.__tprfile = tprfile
+ self.__parameters = None
+
+ @property
+ def parameters(self):
+ if self.__parameters is None:
+ with self.__tprfile as fh:
+ self.__parameters = fh._tprFileHandle.params()
+ return self.__parameters
+
+ @property
+ def structure(self):
+ raise exceptions.ApiError("property not implemented.")
+
+ @property
+ def topology(self):
+ raise exceptions.ApiError("property not implemented.")
+
+ @property
+ def state(self):
+ raise exceptions.ApiError("property not implemented.")
+
+
+def read_tpr(tprfile: typing.Union[str, TprFile]):
+ """
+ Get a simulation input object from a TPR run input file.
+
+ Arguments:
+ tprfile : TPR input object or filename
+
+ Returns:
+ simulation input object
+
+ The returned object may be inspected by the user. Simulation input parameters
+ may be extracted through the `parameters` attribute.
+
+ Example:
+ >>> sim_input = gmx.fileio.read_tpr(tprfile=tprfilename)
+ >>> params = sim_input.parameters.extract()
+ >>> print(params['init-step'])
+ 0
+
+ Supports the `read_tpr` gmxapi work graph operation. (not yet implemented)
+ """
+ if not isinstance(tprfile, TprFile):
+ try:
+ tprfile = TprFile(os.fsencode(tprfile), mode='r')
+ except Exception as e:
+ raise exceptions.UsageError("TPR object or file name is required.") from e
+
+ return _SimulationInput(tprfile)
+
+
+# In initial implementation, we extract the entire TPR file contents through the
+# TPR-backed GmxMdParams implementation.
+# Note: this function is not consistent with a gmxapi operation.
+def write_tpr_file(output, input=None):
+ """
+ Create a new TPR file, combining user-provided input.
+
+ .. versionadded:: 0.0.8
+ Initial version of this tool does not know how to generate a valid simulation
+ run input file from scratch, so it requires input derived from an already valid
+ TPR file.
+
+ The simulation input object should provide the gmx simulation_input interface,
+ with output ports for `parameters`, `structure`, `topology`, and `state`, such
+ as a TprFileHandle
+
+ Arguments:
+ output : TPR file name to write.
+ input : simulation input data from which to write a simulation run input file.
+
+ Use this function to write a new TPR file with data updated from an
+ existing TPR file. Keyword arguments are objects that can provide gmxapi
+ compatible access to the necessary simulation input data.
+
+ In the initial version, data must originate from an existing TPR file, and
+ only simulation parameters may be rewritten. See gmx.fileio.read_tpr()
+
+ Example:
+ >>> sim_input = gmx.fileio.read_tpr(tprfile=tprfilename)
+ >>> sim_input.parameters.set('init-step', 1)
+ >>> gmx.fileio.write_tpr_file(newfilename, input=sim_input)
+
+ Warning:
+ The interface is in flux.
+
+ TODO:
+ Be consistent about terminology for "simulation state".
+ We are currently using "simulation state" to refer both to the aggregate of
+ data (superset) necessary to launch or continue a simulation _and_ to the
+ extra data (subset) necessary to capture full program state, beyond the
+ model/method input parameters and current phase space coordinates. Maybe we
+ shouldn't expose that as a separate user-accessible object and should instead
+ make it an implementation detail of a wrapper object that has standard
+ interfaces for the non-implementation-dependent encapsulated data.
+
+ Returns:
+ TBD : possibly a completion condition of some sort and/or handle to the new File
+ """
+
+ # TODO: (Data model) Decide how to find output data sources.
+ if not hasattr(input, 'parameters'):
+ if hasattr(input, 'output'):
+ if hasattr(input.output, 'parameters'):
+ parameters = input.output.parameters
+ else:
+ raise ValueError("Need output.parameters")
+ else:
+ raise ValueError("Need output.parameters")
+ else:
+ parameters = input.parameters
+
+ if not isinstance(parameters, _gmxapi.SimulationParameters):
+ raise exceptions.TypeError(
+ "You must provide a gmx.core.SimulationParameters object to `parameters` as input.")
+ _gmxapi.write_tprfile(output, parameters)
--- /dev/null
+/*
+ * 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.
+ */
+/*! \file
+ * \brief Helper code for TPR file access.
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ * \ingroup module_python
+ */
+
+#include "tprfile.h"
+
+#include <cassert>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/topology/topology.h"
+#include "gromacs/mdtypes/state.h"
+#include "gromacs/fileio/oenv.h"
+#include "gromacs/fileio/tpxio.h"
+#include "gromacs/options/timeunitmanager.h"
+#include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/programcontext.h"
+
+#include "compat_exceptions.h"
+#include "mdparams.h"
+
+namespace gmxapicompat
+{
+
+class TprContents
+{
+ public:
+ explicit TprContents(const std::string &infile) :
+ irInstance_ {std::make_unique<t_inputrec>()},
+ mtop_ {std::make_unique<gmx_mtop_t>()},
+ state_ {std::make_unique<t_state>()}
+ {
+ read_tpx_state(infile.c_str(), irInstance_.get(), state_.get(), mtop_.get());
+ }
+ ~TprContents() = default;
+ TprContents(TprContents &&source) noexcept = default;
+ TprContents &operator=(TprContents &&) noexcept = default;
+
+ /*!
+ * \brief Get a reference to the input record in the TPR file.
+ *
+ * Note that this implementation allows different objects to share ownership
+ * of the TprFile and does not provide access restrictions to prevent multiple
+ * code blocks writing to the input record. This should be resolved with a
+ * combination of managed access controlled handles and through better
+ * management of the data structures in the TPR file. I.e. the t_inputrec is
+ * not copyable, moveable, nor default constructable (at least, to produce a
+ * valid record), and it does not necessarily make sense to map the library
+ * data structure to the file data structure (except that we don't have another
+ * way of constructing a complete and valid input record).
+ *
+ * \todo We can't play fast and loose with the irInstance for long...
+ *
+ * \return
+ */
+ t_inputrec &inputRecord() const
+ {
+ assert(irInstance_);
+ return *irInstance_;
+ }
+
+ gmx_mtop_t &molecularTopology() const
+ {
+ assert(mtop_);
+ return *mtop_;
+ }
+
+ t_state &state() const
+ {
+ assert(state_);
+ return *state_;
+ }
+ private:
+ // These types are not moveable in GROMACS 2019, so we use unique_ptr as a
+ // moveable wrapper to let TprContents be moveable.
+ std::unique_ptr<t_inputrec> irInstance_;
+ std::unique_ptr<gmx_mtop_t> mtop_;
+ std::unique_ptr<t_state> state_;
+
+};
+
+// Note: This mapping is incomplete. Hopefully we can replace it before more mapping is necessary.
+// TODO: (#2993) Replace with GROMACS library header resources when available.
+const std::map<std::string, GmxapiType> simulationParameterTypeMap()
+{
+ return {
+ {
+ "integrator", GmxapiType::STRING
+ },
+ {
+ "tinit", GmxapiType::FLOAT64
+ },
+ {
+ "dt", GmxapiType::FLOAT64
+ },
+ {
+ "nsteps", GmxapiType::INT64
+ },
+ {
+ "init-step", GmxapiType::INT64
+ },
+ {
+ "simulation-part", GmxapiType::INT64
+ },
+ {
+ "comm-mode", GmxapiType::STRING
+ },
+ {
+ "nstcomm", GmxapiType::INT64
+ },
+ {
+ "comm-grps", GmxapiType::NDARRAY
+ }, // Note: we do not have processing for this yet.
+ {
+ "bd-fric", GmxapiType::FLOAT64
+ },
+ {
+ "ld-seed", GmxapiType::INT64
+ },
+ {
+ "emtol", GmxapiType::FLOAT64
+ },
+ {
+ "emstep", GmxapiType::FLOAT64
+ },
+ {
+ "niter", GmxapiType::INT64
+ },
+ {
+ "fcstep", GmxapiType::FLOAT64
+ },
+ {
+ "nstcgsteep", GmxapiType::INT64
+ },
+ {
+ "nbfgscorr", GmxapiType::INT64
+ },
+ {
+ "rtpi", GmxapiType::FLOAT64
+ },
+ {
+ "nstxout", GmxapiType::INT64
+ },
+ {
+ "nstvout", GmxapiType::INT64
+ },
+ {
+ "nstfout", GmxapiType::INT64
+ },
+ {
+ "nstlog", GmxapiType::INT64
+ },
+ {
+ "nstcalcenergy", GmxapiType::INT64
+ },
+ {
+ "nstenergy", GmxapiType::INT64
+ },
+ {
+ "nstxout-compressed", GmxapiType::INT64
+ },
+ {
+ "compressed-x-precision", GmxapiType::FLOAT64
+ },
+ {
+ "cutoff-scheme", GmxapiType::STRING
+ },
+ {
+ "nstlist", GmxapiType::INT64
+ },
+ {
+ "ns-type", GmxapiType::STRING
+ },
+ {
+ "pbc", GmxapiType::STRING
+ },
+ {
+ "periodic-molecules", GmxapiType::BOOL
+ },
+// TBD
+
+ };
+};
+
+/*
+ * Visitor for predetermined known types.
+ *
+ * Development sequence:
+ * 1. map pointers
+ * 2. map setters ()
+ * 3. template the Visitor setter for compile-time extensibility of type and to prune incompatible types.
+ * 4. switch to Variant type for handling (setter templated on caller input)
+ * 5. switch to Variant type for input as well? (Variant in public API?)
+ */
+
+const std::map<std::string, bool t_inputrec::*> boolParams()
+{
+ return {
+ {
+ "periodic-molecules", &t_inputrec::bPeriodicMols
+ },
+// ...
+ };
+}
+
+const std::map<std::string, int t_inputrec::*> int32Params()
+{
+ return {
+ {
+ "simulation-part", &t_inputrec::simulation_part
+ },
+ {
+ "nstcomm", &t_inputrec::nstcomm
+ },
+ {
+ "niter", &t_inputrec::niter
+ },
+ {
+ "nstcgsteep", &t_inputrec::nstcgsteep
+ },
+ {
+ "nbfgscorr", &t_inputrec::nbfgscorr
+ },
+ {
+ "nstxout", &t_inputrec::nstxout
+ },
+ {
+ "nstvout", &t_inputrec::nstvout
+ },
+ {
+ "nstfout", &t_inputrec::nstfout
+ },
+ {
+ "nstlog", &t_inputrec::nstlog
+ },
+ {
+ "nstcalcenergy", &t_inputrec::nstcalcenergy
+ },
+ {
+ "nstenergy", &t_inputrec::nstenergy
+ },
+ {
+ "nstxout-compressed", &t_inputrec::nstxout_compressed
+ },
+ {
+ "nstlist", &t_inputrec::nstlist
+ },
+// ...
+ };
+}
+
+const std::map<std::string, float t_inputrec::*> float32Params()
+{
+ return {
+ {
+ "bd-fric", &t_inputrec::bd_fric
+ },
+ {
+ "emtol", &t_inputrec::em_tol
+ },
+ {
+ "emstep", &t_inputrec::em_stepsize
+ },
+ {
+ "fcstep", &t_inputrec::fc_stepsize
+ },
+ {
+ "rtpi", &t_inputrec::rtpi
+ },
+ {
+ "compressed-x-precision", &t_inputrec::x_compression_precision
+ },
+// ...
+
+ };
+}
+const std::map<std::string, double t_inputrec::*> float64Params()
+{
+ return {
+ {
+ "dt", &t_inputrec::delta_t
+ },
+ {
+ "tinit", &t_inputrec::init_t
+ },
+// ...
+
+ };
+}
+const std::map<std::string, int64_t t_inputrec::*> int64Params()
+{
+ return {
+ {
+ "nsteps", &t_inputrec::nsteps
+ },
+ {
+ "init-step", &t_inputrec::init_step
+ },
+ {
+ "ld-seed", &t_inputrec::ld_seed
+ },
+// ...
+
+ };
+}
+
+/*!
+ * \brief Static mapping of parameter names to gmxapi types for GROMACS 2019.
+ *
+ * \param name MDP entry name.
+ * \return enumeration value for known parameters.
+ *
+ * \throws gmxapi_compat::ValueError for parameters with no mapping.
+ */
+GmxapiType mdParamToType(const std::string &name)
+{
+ const auto staticMap = simulationParameterTypeMap();
+ auto entry = staticMap.find(name);
+ if (entry == staticMap.end())
+ {
+ throw ValueError("Named parameter has unknown type mapping.");
+ }
+ return entry->second;
+};
+
+
+/*!
+ * \brief Handle / manager for GROMACS molecular computation input parameters.
+ *
+ * Interface should be consistent with MDP file entries, but data maps to TPR
+ * file interface. For type safety and simplicity, we don't have generic operator
+ * accessors. Instead, we have templated accessors that throw exceptions when
+ * there is trouble.
+ *
+ * When MDP input is entirely stored in a key-value tree, this class can be a
+ * simple adapter or wrapper. Until then, we need a manually maintained mapping
+ * of MDP entries to TPR data.
+ *
+ * Alternatively, we could update the infrastructure used by list_tpx to provide
+ * more generic output, but our efforts may be better spent in updating the
+ * infrastructure for the key-value tree input system.
+ */
+class GmxMdParamsImpl final
+{
+ public:
+ /*!
+ * \brief Create an initialized but empty parameters structure.
+ *
+ * Parameter keys are set at construction, but all values are empty. This
+ * allows the caller to check for valid parameter names or their types,
+ * while allowing the consuming code to know which parameters were explicitly
+ * set by the caller.
+ *
+ * To load values from a TPR file, see getMdParams().
+ */
+ GmxMdParamsImpl();
+
+ explicit GmxMdParamsImpl(std::shared_ptr<TprContents> tprContents);
+
+ /*!
+ * \brief Get the current list of keys.
+ *
+ * \return
+ */
+ std::vector<std::string> keys() const
+ {
+ std::vector<std::string> keyList;
+ for (auto && entry : int64Params_)
+ {
+ keyList.emplace_back(entry.first);
+ }
+ for (auto && entry : intParams_)
+ {
+ keyList.emplace_back(entry.first);
+ }
+ for (auto && entry : floatParams_)
+ {
+ keyList.emplace_back(entry.first);
+ }
+ for (auto && entry : float64Params_)
+ {
+ keyList.emplace_back(entry.first);
+ }
+ return keyList;
+ };
+
+ template<typename T> T extract(const std::string &key) const
+ {
+ auto value = T();
+ // should be an APIError
+ throw TypeError("unhandled type");
+ }
+
+ void set(const std::string &key, const int64_t &value)
+ {
+ if (int64Params_.find(key) != int64Params_.end())
+ {
+ int64Params_[key] = std::make_pair(value, true);
+
+ if (source_)
+ {
+ auto memberPointer = int64Params().at(key);
+ source_->inputRecord().*memberPointer = value;
+ }
+ }
+ else if (intParams_.find(key) != intParams_.end())
+ {
+ // TODO: check whether value is too large?
+ intParams_[key] = std::make_pair(static_cast<int>(value), true);
+
+ if (source_)
+ {
+ auto memberPointer = int32Params().at(key);
+ source_->inputRecord().*memberPointer = value;
+ }
+
+ }
+ else
+ {
+ throw KeyError("Named parameter is incompatible with integer type value.");
+ }
+ };
+
+ void set(const std::string &key, const double &value)
+ {
+ if (float64Params_.find(key) != float64Params_.end())
+ {
+ float64Params_[key] = std::make_pair(value, true);
+
+ if (source_)
+ {
+ auto memberPointer = float64Params().at(key);
+ source_->inputRecord().*memberPointer = value;
+ }
+
+ }
+ else if (floatParams_.find(key) != floatParams_.end())
+ {
+ // TODO: check whether value is too large?
+ floatParams_[key] = std::make_pair(static_cast<float>(value), true);
+
+ if (source_)
+ {
+ auto memberPointer = float32Params().at(key);
+ source_->inputRecord().*memberPointer = value;
+ }
+
+ }
+ else
+ {
+ throw KeyError("Named parameter is incompatible with floating point type value.");
+ }
+ };
+//
+// // Uses expression SFINAE of the return type to be sure of the right overload
+// // at template instantiation. Causes compile error if a setter is not available
+// // for the parameter type T.
+// template<typename T> auto set(const std::string& key, const T& value) -> decltype(setParam(key, value), void())
+// {
+// this->set(key, value);
+// }
+
+ TprReadHandle getSource() const
+ {
+ // Note: might return a null handle. Need to decide what that means and how to address it.
+ return TprReadHandle(source_);
+ }
+
+ private:
+
+ // Hold the settable parameters and whether or not they have been set.
+ // TODO: update to gmxapi named types?
+ std::map < std::string, std::pair < int64_t, bool>> int64Params_;
+ std::map < std::string, std::pair < int, bool>> intParams_;
+ std::map < std::string, std::pair < float, bool>> floatParams_;
+ std::map < std::string, std::pair < double, bool>> float64Params_;
+
+ /*! \brief Shared ownership of a pack of TPR data.
+ *
+ * This is a non-normative way to retain access to gmxapi resources.
+ * \todo Subscribe to a Context-managed resource.
+ */
+ std::shared_ptr<TprContents> source_;
+};
+
+void setParam(gmxapicompat::GmxMdParams *params, const std::string &name, double value)
+{
+ assert(params != nullptr);
+ assert(params->params_ != nullptr);
+ params->params_->set(name, value);
+}
+
+void setParam(gmxapicompat::GmxMdParams *params, const std::string &name, int64_t value)
+{
+ assert(params != nullptr);
+ assert(params->params_ != nullptr);
+ params->params_->set(name, value);
+}
+
+template<typename ParamsContainerT, typename Mapping>
+static void setParam(ParamsContainerT* params, const TprContents &source, const Mapping &map)
+{
+ for (const auto &definition : map)
+ {
+ const auto &key = definition.first;
+ auto memberPointer = definition.second;
+ auto &irInstance = source.inputRecord();
+ auto fileValue = irInstance.*memberPointer;
+ (*params)[key] = std::make_pair(fileValue, true);
+ }
+}
+
+/*!
+ * \brief A GmxMdParams implementation that depends on TPR files.
+ *
+ * \param tprContents
+ */
+GmxMdParamsImpl::GmxMdParamsImpl(std::shared_ptr<gmxapicompat::TprContents> tprContents) :
+ source_ {std::move(tprContents)}
+{
+ if (source_)
+ {
+ setParam(&int64Params_, *source_, int64Params());
+ setParam(&intParams_, *source_, int32Params());
+ setParam(&float64Params_, *source_, float32Params());
+ setParam(&float64Params_, *source_, float64Params());
+ }
+}
+
+GmxMdParamsImpl::GmxMdParamsImpl() :
+ GmxMdParamsImpl(nullptr)
+{}
+
+template<>
+int GmxMdParamsImpl::extract<int>(const std::string &key) const
+{
+ const auto ¶ms = intParams_;
+ const auto &entry = params.find(key);
+ if (entry == params.cend())
+ {
+ throw KeyError("Parameter of the requested name and type not defined.");
+ }
+ else if (!entry->second.second)
+ {
+ // TODO: handle invalid and unset parameters differently.
+ throw KeyError("Parameter of the requested name not set.");
+ }
+ else
+ {
+ return entry->second.first;
+ }
+}
+
+template<>
+int64_t GmxMdParamsImpl::extract<int64_t>(const std::string &key) const
+{
+ const auto ¶ms = int64Params_;
+ const auto &entry = params.find(key);
+ if (entry == params.cend())
+ {
+ throw KeyError("Parameter of the requested name and type not defined.");
+ }
+ else if (!entry->second.second)
+ {
+ // TODO: handle invalid and unset parameters differently.
+ throw KeyError("Parameter of the requested name not set.");
+ }
+ else
+ {
+ return entry->second.first;
+ }
+}
+template<>
+float GmxMdParamsImpl::extract<float>(const std::string &key) const
+{
+ const auto ¶ms = floatParams_;
+ const auto &entry = params.find(key);
+ if (entry == params.cend())
+ {
+ throw KeyError("Parameter of the requested name and type not defined.");
+ }
+ else if (!entry->second.second)
+ {
+ // TODO: handle invalid and unset parameters differently.
+ throw KeyError("Parameter of the requested name not set.");
+ }
+ else
+ {
+ return entry->second.first;
+ }
+}
+template<>
+double GmxMdParamsImpl::extract<double>(const std::string &key) const
+{
+ const auto ¶ms = float64Params_;
+ const auto &entry = params.find(key);
+ if (entry == params.cend())
+ {
+ throw KeyError("Parameter of the requested name and type not defined.");
+ }
+ else if (!entry->second.second)
+ {
+ // TODO: handle invalid and unset parameters differently.
+ throw KeyError("Parameter of the requested name not set.");
+ }
+ else
+ {
+ return entry->second.first;
+ }
+}
+
+
+int extractParam(const GmxMdParams ¶ms, const std::string &name, int)
+{
+ assert(params.params_);
+ return params.params_->extract<int>(name);
+}
+
+int64_t extractParam(const GmxMdParams ¶ms, const std::string &name, int64_t)
+{
+ assert(params.params_);
+ int64_t value {};
+ // Allow fetching both known integer types.
+ try
+ {
+ value = params.params_->extract<int>(name);
+ }
+ catch (const KeyError &error)
+ {
+ // If not found as a regular int, check for int64.
+ try
+ {
+ value = params.params_->extract<int64_t >(name);
+ }
+ catch (const KeyError &error64)
+ {
+ throw KeyError("Parameter of the requested name not set.");
+ }
+ }
+ // Any other exceptions propagate out.
+ return value;
+}
+
+float extractParam(const GmxMdParams ¶ms, const std::string &name, float)
+{
+ assert(params.params_);
+ return params.params_->extract<float>(name);
+}
+
+double extractParam(const GmxMdParams ¶ms, const std::string &name, double)
+{
+ assert(params.params_);
+ double value {};
+ // Allow fetching both single and double precision.
+ try
+ {
+ value = params.params_->extract<double>(name);
+ }
+ catch (const KeyError &errorDouble)
+ {
+ // If not found as a double precision value, check for single-precision.
+ try
+ {
+ value = params.params_->extract<float>(name);
+ }
+ catch (const KeyError &errorFloat)
+ {
+ throw KeyError("Parameter of the requested name not set.");
+ }
+ }
+ // Any other exceptions propagate out.
+ return value;
+}
+
+std::vector<std::string> keys(const GmxMdParams ¶ms)
+{
+ return params.params_->keys();
+}
+
+
+TprReadHandle readTprFile(const std::string &filename)
+{
+ auto tprfile = gmxapicompat::TprContents(filename);
+ auto handle = gmxapicompat::TprReadHandle(std::move(tprfile));
+ return handle;
+}
+
+GmxMdParams getMdParams(const TprReadHandle &handle)
+{
+ auto tprfile = handle.get();
+ // TODO: convert to exception / decide whether null handles are allowed.
+ assert(tprfile);
+ GmxMdParams params;
+ params.params_ = std::make_unique<GmxMdParamsImpl>(tprfile);
+ return params;
+}
+
+TopologySource getTopologySource(const TprReadHandle &handle)
+{
+ TopologySource source;
+ source.tprFile_ = handle.get();
+ return source;
+}
+
+SimulationState getSimulationState(const TprReadHandle &handle)
+{
+ SimulationState source;
+ source.tprFile_ = handle.get();
+ return source;
+}
+
+StructureSource getStructureSource(const TprReadHandle &handle)
+{
+ StructureSource source;
+ source.tprFile_ = handle.get();
+ return source;
+}
+
+TprReadHandle::TprReadHandle(std::shared_ptr<TprContents> tprFile) :
+ tprContents_ {std::move(tprFile)}
+{
+}
+
+TprReadHandle getSourceFileHandle(const GmxMdParams ¶ms)
+{
+ return params.params_->getSource();
+}
+
+void writeTprFile(const std::string &filename,
+ const GmxMdParams ¶ms,
+ const StructureSource &structure,
+ const SimulationState &state,
+ const TopologySource &topology)
+{
+ assert(params.params_);
+ // The only way we can check for consistent input right now is to make sure
+ // it all comes from the same file.
+ if (structure.tprFile_.get() != state.tprFile_.get() ||
+ state.tprFile_.get() != topology.tprFile_.get() ||
+ topology.tprFile_.get() != params.params_->getSource().get().get() ||
+ params.params_->getSource().get().get() != structure.tprFile_.get()
+ )
+ {
+ throw ValueError("writeTprFile does not yet know how to reconcile data from different TPR file sources.");
+ }
+
+ const auto tprFileHandle = params.params_->getSource();
+ const auto tprFile = tprFileHandle.get();
+ assert(tprFile);
+ const auto &inputRecord = tprFile->inputRecord();
+ const auto &writeState = tprFile->state();
+ const auto &writeTopology = tprFile->molecularTopology();
+ write_tpx_state(filename.c_str(), &inputRecord, &writeState, &writeTopology);
+
+}
+
+TprReadHandle::TprReadHandle(TprContents &&tprFile) :
+ TprReadHandle {std::make_shared<TprContents>(std::move(tprFile))}
+{
+}
+
+std::shared_ptr<TprContents> TprReadHandle::get() const
+{
+ return tprContents_;
+}
+
+// defaulted here to delay definition until after member types are defined.
+TprReadHandle::~TprReadHandle() = default;
+
+GmxMdParams::~GmxMdParams() = default;
+
+GmxMdParams::GmxMdParams() :
+ params_ {std::make_unique<GmxMdParamsImpl>()}
+{}
+
+GmxMdParams::GmxMdParams(GmxMdParams &&) noexcept = default;
+
+GmxMdParams &GmxMdParams::operator=(GmxMdParams &&) noexcept = default;
+
+} // end namespace gmxapicompat
+
+namespace gmxpy
+{
+
+// maybe this should return a handle to the new file?
+bool copy_tprfile(const gmxapicompat::TprReadHandle &input, std::string outFile)
+{
+ if (!input.get())
+ {
+ return false;
+ }
+ gmxapicompat::writeTprFile(outFile,
+ gmxapicompat::getMdParams(input),
+ gmxapicompat::getStructureSource(input),
+ gmxapicompat::getSimulationState(input),
+ gmxapicompat::getTopologySource(input));
+ return true;
+}
+
+bool rewrite_tprfile(std::string inFile, std::string outFile, double endTime)
+{
+ bool success = false;
+
+ const char * top_fn = inFile.c_str();
+
+ t_inputrec irInstance;
+ gmx_mtop_t mtop;
+ t_state state;
+ read_tpx_state(top_fn, &irInstance, &state, &mtop);
+
+ /* set program name, command line, and default values for output options */
+ gmx_output_env_t *oenv;
+ gmx::TimeUnit timeUnit = gmx::TimeUnit_Default;
+ bool bView {
+ false
+ }; // argument that says we don't want to view graphs.
+ int xvgFormat {
+ 0
+ };
+ output_env_init(&oenv, gmx::getProgramContext(),
+ static_cast<time_unit_t>(timeUnit + 1), bView, // NOLINT(misc-misplaced-widening-cast)
+ static_cast<xvg_format_t>(xvgFormat + 1), 0);
+
+ double run_t = irInstance.init_step*irInstance.delta_t + irInstance.init_t;
+
+ irInstance.nsteps = static_cast<int64_t>((endTime - run_t) / irInstance.delta_t + 0.5);
+
+ write_tpx_state(outFile.c_str(), &irInstance, &state, &mtop);
+
+ success = true;
+ return success;
+}
+
+} // end namespace gmxpy
--- /dev/null
+/*
+ * 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.
+ */
+/*! \file
+ * \brief Declare TPR file helpers.
+ *
+ * \ingroup module_python
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ */
+
+#ifndef GMXPY_TPRFILE_H
+#define GMXPY_TPRFILE_H
+
+#include <string>
+#include <vector>
+
+#include "compat_exceptions.h"
+#include "mdparams.h"
+
+namespace gmxapicompat
+{
+
+/*!
+ * \brief Facade for objects that can provide atomic data for a configuration.
+ */
+class StructureSource;
+
+/*!
+ * \brief Facade for objects that can provide molecular topology information for a structure.
+ */
+class TopologySource;
+
+/*!
+ * \brief Proxy to simulation state data.
+ */
+class SimulationState;
+
+/*!
+ * \brief Manager for TPR file resources.
+ *
+ * To avoid copies, this resource-owning object is shared by consumers of its
+ * resources, even when different resources are consumed.
+ *
+ * Multiple read-only handles may be issued if there are no write-handles.
+ * One write handle may be issued if there are no other open handles.
+ *
+ * A const TprFile may only issue read file-handles, allowing handles to be
+ * issued more quickly by avoiding atomic resource locking.
+ *
+ * \note Shared ownership of file manager could be avoided if owned by a Context.
+ * It is appropriate for a Context to own and mediate access to the manager because
+ * the Context should provide the filesystem abstraction to more intelligently
+ * map named file paths to resources. For now, handles and other consumers share ownership
+ * of the TprContents manager object via shared_ptr.
+ */
+class TprContents;
+
+/*!
+ * \brief Handle for a TPR data resource.
+ *
+ * Can provide StructureSource, TopologySource, GmxMdParams, and SimulationState.
+ *
+ * This is the type of object we allow Python clients to hold references to, though
+ * we don't expose any methods to Python. Python clients should acquire access
+ * to TPR file contents with read_tpr().
+ *
+ * \todo gmxapi C++ API should provide mechanisms for subscribing to simulation
+ * input data from various sources.
+ */
+class TprReadHandle
+{
+ public:
+ explicit TprReadHandle(std::shared_ptr<TprContents> tprFile);
+ explicit TprReadHandle(TprContents &&tprFile);
+ ~TprReadHandle();
+
+ /*!
+ * \brief Allow API functions to access data resources.
+ *
+ * Used internally. The entire TPR contents are never extracted to the
+ * client, but API implementation details need to be
+ * able to access some or all entire contents in later operations.
+ *
+ * \return Reference-counted handle to data container.
+ */
+ std::shared_ptr<TprContents> get() const;
+ private:
+ std::shared_ptr<TprContents> tprContents_;
+};
+
+/*!
+ * \brief Open a TPR file and retrieve a handle.
+ *
+ * \param filename Path of file to read.
+ * \return handle that may share ownership of TPR file resource.
+ */
+TprReadHandle readTprFile(const std::string &filename);
+
+/*!
+ * \brief Write a new TPR file to the filesystem with the provided contents.
+ *
+ * \param filename output file path
+ * \param params simulation parameters
+ * \param structure system structure (atomic configuration)
+ * \param state simulation state
+ * \param topology molecular topology
+ */
+void writeTprFile(const std::string &filename,
+ const GmxMdParams ¶ms,
+ const StructureSource &structure,
+ const SimulationState &state,
+ const TopologySource &topology);
+
+/*!
+ * \brief Helper function for early implementation.
+ *
+ * Allows extraction of TPR file information from special params objects.
+ *
+ * \todo This is a very temporary shim! Find a better way to construct simulation input.
+ */
+TprReadHandle getSourceFileHandle(const GmxMdParams ¶ms);
+
+/*!
+ * \brief Get a topology source from the TPR contents collection.
+ * \param handle
+ * \return
+ *
+ * \todo replace with a helper template on T::topologySource() member function existence.
+ */
+
+TopologySource getTopologySource(const TprReadHandle &handle);
+
+/*!
+ * \brief Get a source of simulation state from the TPR contents collection.
+ * \param handle
+ * \return
+ *
+ * \todo template on T::simulationState() member function existence.
+ */
+SimulationState getSimulationState(const TprReadHandle &handle);
+
+/*!
+ * \brief Get a source of atomic structure from the TPR contents collection.
+ * \param handle
+ * \return
+ */
+StructureSource getStructureSource(const TprReadHandle &handle);
+
+/*!
+ * \brief Get an initialized parameters structure.
+ * \param handle
+ * \return
+ */
+GmxMdParams getMdParams(const TprReadHandle &handle);
+
+std::vector<std::string> keys(const GmxMdParams ¶ms);
+
+class StructureSource
+{
+ public:
+ std::shared_ptr<TprContents> tprFile_;
+};
+
+class TopologySource
+{
+ public:
+ std::shared_ptr<TprContents> tprFile_;
+};
+
+class SimulationState
+{
+ public:
+ std::shared_ptr<TprContents> tprFile_;
+};
+
+} // end namespace gmxapicompat
+
+namespace gmxpy
+{
+
+/*!
+ * \brief Copy TPR file.
+ *
+ * \param input TPR source to copy from
+ * \param outFile output TPR file name
+ * \return true if successful. else false.
+ */
+bool copy_tprfile(const gmxapicompat::TprReadHandle &input, std::string outFile);
+
+/*!
+ * \brief Copy and possibly update TPR file by name.
+ *
+ * \param inFile Input file name
+ * \param outFile Output file name
+ * \param endTime Replace `nsteps` in infile with `endTime/dt`
+ * \return true if successful, else false
+ */
+bool rewrite_tprfile(std::string inFile, std::string outFile, double endTime);
+
+} // end namespace gmxpy
+
+#endif //GMXPY_TPRFILE_H
--- /dev/null
+/*
+ * 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.
+ */
+/*! \file
+ * \brief Tools for managing mappings of gmxapi data types.
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ */
+
+#ifndef GMXPY_TYPETEMPLATES_H
+#define GMXPY_TYPETEMPLATES_H
+
+
+#include <type_traits>
+
+#include "mdparams.h"
+
+namespace gmxapicompat
+{
+
+namespace traits
+{
+
+// These can be more than traits. We might as well make them named types.
+struct gmxNull {
+ static const GmxapiType value = GmxapiType::gmxNull;
+};
+struct gmxMap {
+ static const GmxapiType value = GmxapiType::gmxMap;
+};
+struct gmxInt32 {
+ static const GmxapiType value = GmxapiType::gmxInt32;
+};
+struct gmxInt64 {
+ static const GmxapiType value = GmxapiType::gmxInt64;
+};
+struct gmxFloat32 {
+ static const GmxapiType value = GmxapiType::gmxFloat32;
+};
+struct gmxFloat64 {
+ static const GmxapiType value = GmxapiType::gmxFloat64;
+};
+struct gmxBool {
+ static const GmxapiType value = GmxapiType::gmxBool;
+};
+struct gmxString {
+ static const GmxapiType value = GmxapiType::gmxString;
+};
+struct gmxMDArray {
+ static const GmxapiType value = GmxapiType::gmxMDArray;
+};
+//struct gmxFloat32Vector3 {
+// static const GmxapiType value = GmxapiType::gmxFloat32Vector3;
+//};
+//struct gmxFloat32SquareMatrix3 {
+// static const GmxapiType value = GmxapiType::gmxFloat32SquareMatrix3;
+//};
+
+} // end namespace traits
+
+// Use an anonymous namespace to restrict these template definitions to file scope.
+namespace
+{
+// Partial specialization of functions is not allowed, which makes the following tedious.
+// To-do: switch to type-based logic, struct templates, etc.
+template<typename T, size_t s>
+GmxapiType mapCppType()
+{
+ return GmxapiType::gmxNull;
+}
+
+template<typename T>
+GmxapiType mapCppType()
+{
+ return mapCppType<T, sizeof(T)>();
+};
+
+template<>
+GmxapiType mapCppType<bool>()
+{
+ return GmxapiType::gmxBool;
+}
+
+template<>
+GmxapiType mapCppType<int, 4>()
+{
+ return GmxapiType::gmxInt32;
+}
+
+template<>
+GmxapiType mapCppType<int, 8>()
+{
+ return GmxapiType::gmxInt64;
+};
+
+
+template<>
+GmxapiType mapCppType<float, 4>()
+{
+ return GmxapiType::gmxFloat32;
+}
+
+template<>
+GmxapiType mapCppType<double, 8>()
+{
+ return GmxapiType::gmxFloat64;
+};
+
+} // end anonymous namespace
+
+} // end namespace gmxapicompat
+
+#endif //GMXPY_TYPETEMPLATES_H
--- /dev/null
+#
+# 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.
+
+"""Test gmx.fileio submodule"""
+import os
+import tempfile
+
+import gmxapi
+import pytest
+from gmxapi.simulation.fileio import TprFile
+from gmxapi.simulation.fileio import read_tpr
+from gmxapi.exceptions import UsageError
+
+
+@pytest.mark.usefixtures('cleandir')
+def test_tprfile_read_old(spc_water_box):
+ tpr_filename = spc_water_box
+ with pytest.raises(UsageError):
+ TprFile(tpr_filename, 'x')
+ with pytest.raises(UsageError):
+ TprFile()
+ tprfile = TprFile(tpr_filename, 'r')
+ with tprfile as fh:
+ cpp_object = fh._tprFileHandle
+ assert cpp_object is not None
+ params = cpp_object.params().extract()
+ assert "nsteps" in params
+ assert "foo" not in params
+
+
+@pytest.mark.usefixtures('cleandir')
+def test_core_tprcopy_alt(spc_water_box):
+ """Test gmx.core.copy_tprfile() for update of end_time.
+
+ Set a new end time that is 5000 steps later than the original. Read dt
+ from file to avoid floating point round-off errors.
+
+ Transitively test gmx.fileio.read_tpr()
+ """
+ tpr_filename = spc_water_box
+ additional_steps = 5000
+ sim_input = read_tpr(tpr_filename)
+ params = sim_input.parameters.extract()
+ dt = params['dt']
+ nsteps = params['nsteps']
+ init_step = params['init-step']
+ initial_endtime = (init_step + nsteps) * dt
+ new_endtime = initial_endtime + additional_steps*dt
+ _, temp_filename = tempfile.mkstemp(suffix='.tpr')
+ gmxapi._gmxapi.rewrite_tprfile(source=tpr_filename, destination=temp_filename, end_time=new_endtime)
+ tprfile = TprFile(temp_filename, 'r')
+ with tprfile as fh:
+ params = read_tpr(fh).parameters.extract()
+ dt = params['dt']
+ nsteps = params['nsteps']
+ init_step = params['init-step']
+ assert (init_step + nsteps) * dt == new_endtime
+
+ os.unlink(temp_filename)
+
+
+@pytest.mark.usefixtures('cleandir')
+def test_write_tpr_file(spc_water_box):
+ """Test gmx.fileio.write_tpr_file() using gmx.core API.
+ """
+ tpr_filename = spc_water_box
+ additional_steps = 5000
+ sim_input = read_tpr(tpr_filename)
+ params = sim_input.parameters.extract()
+ nsteps = params['nsteps']
+ init_step = params['init-step']
+ new_nsteps = init_step + additional_steps
+
+ sim_input.parameters.set('nsteps', new_nsteps)
+
+ _, temp_filename = tempfile.mkstemp(suffix='.tpr')
+ gmxapi.simulation.fileio.write_tpr_file(temp_filename, input=sim_input)
+ tprfile = TprFile(temp_filename, 'r')
+ with tprfile as fh:
+ params = read_tpr(fh).parameters.extract()
+ dt = params['dt']
+ assert params['nsteps'] != nsteps
+ assert params['nsteps'] == new_nsteps
+
+ os.unlink(temp_filename)
add_library(gmxapi SHARED
context.cpp
- exceptions.cpp
+ compat_exceptions.cpp
gmxapi.cpp
md.cpp
mdmodule.cpp