2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2019, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * \brief Helper code for TPR file access.
39 * \author M. Eric Irrgang <ericirrgang@gmail.com>
40 * \ingroup gmxapi_compat
43 #include "gmxapi/compat/tpr.h"
52 #include "gromacs/mdtypes/inputrec.h"
53 #include "gromacs/topology/topology.h"
54 #include "gromacs/mdtypes/state.h"
55 #include "gromacs/fileio/oenv.h"
56 #include "gromacs/fileio/tpxio.h"
57 #include "gromacs/options/timeunitmanager.h"
58 #include "gromacs/utility/cstringutil.h"
59 #include "gromacs/utility/programcontext.h"
61 #include "gmxapi/compat/exceptions.h"
62 #include "gmxapi/compat/mdparams.h"
64 namespace gmxapicompat
70 explicit TprContents(const std::string
&infile
) :
71 irInstance_
{std::make_unique
<t_inputrec
>()},
72 mtop_
{std::make_unique
<gmx_mtop_t
>()},
73 state_
{std::make_unique
<t_state
>()}
75 read_tpx_state(infile
.c_str(), irInstance_
.get(), state_
.get(), mtop_
.get());
77 ~TprContents() = default;
78 TprContents(TprContents
&&source
) noexcept
= default;
79 TprContents
&operator=(TprContents
&&) noexcept
= default;
82 * \brief Get a reference to the input record in the TPR file.
84 * Note that this implementation allows different objects to share ownership
85 * of the TprFile and does not provide access restrictions to prevent multiple
86 * code blocks writing to the input record. This should be resolved with a
87 * combination of managed access controlled handles and through better
88 * management of the data structures in the TPR file. I.e. the t_inputrec is
89 * not copyable, moveable, nor default constructable (at least, to produce a
90 * valid record), and it does not necessarily make sense to map the library
91 * data structure to the file data structure (except that we don't have another
92 * way of constructing a complete and valid input record).
94 * \todo We can't play fast and loose with the irInstance for long...
98 t_inputrec
&inputRecord() const
104 gmx_mtop_t
&molecularTopology() const
110 t_state
&state() const
116 // These types are not moveable in GROMACS 2019, so we use unique_ptr as a
117 // moveable wrapper to let TprContents be moveable.
118 std::unique_ptr
<t_inputrec
> irInstance_
;
119 std::unique_ptr
<gmx_mtop_t
> mtop_
;
120 std::unique_ptr
<t_state
> state_
;
124 // Note: This mapping is incomplete. Hopefully we can replace it before more mapping is necessary.
125 // TODO: (#2993) Replace with GROMACS library header resources when available.
126 std::map
<std::string
, GmxapiType
> simulationParameterTypeMap()
130 "integrator", GmxapiType::STRING
133 "tinit", GmxapiType::FLOAT64
136 "dt", GmxapiType::FLOAT64
139 "nsteps", GmxapiType::INT64
142 "init-step", GmxapiType::INT64
145 "simulation-part", GmxapiType::INT64
148 "comm-mode", GmxapiType::STRING
151 "nstcomm", GmxapiType::INT64
154 "comm-grps", GmxapiType::NDARRAY
155 }, // Note: we do not have processing for this yet.
157 "bd-fric", GmxapiType::FLOAT64
160 "ld-seed", GmxapiType::INT64
163 "emtol", GmxapiType::FLOAT64
166 "emstep", GmxapiType::FLOAT64
169 "niter", GmxapiType::INT64
172 "fcstep", GmxapiType::FLOAT64
175 "nstcgsteep", GmxapiType::INT64
178 "nbfgscorr", GmxapiType::INT64
181 "rtpi", GmxapiType::FLOAT64
184 "nstxout", GmxapiType::INT64
187 "nstvout", GmxapiType::INT64
190 "nstfout", GmxapiType::INT64
193 "nstlog", GmxapiType::INT64
196 "nstcalcenergy", GmxapiType::INT64
199 "nstenergy", GmxapiType::INT64
202 "nstxout-compressed", GmxapiType::INT64
205 "compressed-x-precision", GmxapiType::FLOAT64
208 "cutoff-scheme", GmxapiType::STRING
211 "nstlist", GmxapiType::INT64
214 "ns-type", GmxapiType::STRING
217 "pbc", GmxapiType::STRING
220 "periodic-molecules", GmxapiType::BOOL
228 * TODO: Visitor for predetermined known types.
230 * Development sequence:
233 * 3. template the Visitor setter for compile-time extensibility of type and to prune incompatible types.
234 * 4. switch to Variant type for handling (setter templated on caller input)
235 * 5. switch to Variant type for input as well? (Variant in public API?)
238 std::map
<std::string
, bool t_inputrec::*> boolParams()
242 "periodic-molecules", &t_inputrec::bPeriodicMols
248 std::map
<std::string
, int t_inputrec::*> int32Params()
252 "simulation-part", &t_inputrec::simulation_part
255 "nstcomm", &t_inputrec::nstcomm
258 "niter", &t_inputrec::niter
261 "nstcgsteep", &t_inputrec::nstcgsteep
264 "nbfgscorr", &t_inputrec::nbfgscorr
267 "nstxout", &t_inputrec::nstxout
270 "nstvout", &t_inputrec::nstvout
273 "nstfout", &t_inputrec::nstfout
276 "nstlog", &t_inputrec::nstlog
279 "nstcalcenergy", &t_inputrec::nstcalcenergy
282 "nstenergy", &t_inputrec::nstenergy
285 "nstxout-compressed", &t_inputrec::nstxout_compressed
288 "nstlist", &t_inputrec::nstlist
297 // Provide a helper to disambiguate `real` typed inputrec values.
299 // Primary template returns empty map.
300 template<typename RealT
>
301 std::map
<std::string
, RealT
t_inputrec::*> compatibleRealParams()
306 // Specialize for the compile-time typedef of `real` to get the inputrec parameters
307 // compatible with the template parameter whose type is not known until compile time.
309 std::map
<std::string
, real
t_inputrec::*> compatibleRealParams()
313 "bd-fric", &t_inputrec::bd_fric
316 "emtol", &t_inputrec::em_tol
319 "emstep", &t_inputrec::em_stepsize
322 "fcstep", &t_inputrec::fc_stepsize
325 "rtpi", &t_inputrec::rtpi
328 "compressed-x-precision", &t_inputrec::x_compression_precision
337 std::map
<std::string
, float t_inputrec::*> float32Params()
339 return compatibleRealParams
<float>();
342 std::map
<std::string
, double t_inputrec::*> float64Params()
344 static const std::map
<std::string
, double t_inputrec::*> explicitDoubles
= {
346 "dt", &t_inputrec::delta_t
349 "tinit", &t_inputrec::init_t
355 // Initialize map to be returned with any compile-time-only doubles.
356 auto fullMap
= compatibleRealParams
<double>();
358 // Get the explicitly `double` parameters.
359 for (const auto &item
: explicitDoubles
)
361 fullMap
.emplace(item
);
367 std::map
<std::string
, int64_t t_inputrec::*> int64Params()
371 "nsteps", &t_inputrec::nsteps
374 "init-step", &t_inputrec::init_step
377 "ld-seed", &t_inputrec::ld_seed
385 * \brief Static mapping of parameter names to gmxapi types for GROMACS 2019.
387 * \param name MDP entry name.
388 * \return enumeration value for known parameters.
390 * \throws gmxapi_compat::ValueError for parameters with no mapping.
392 GmxapiType
mdParamToType(const std::string
&name
)
394 const auto staticMap
= simulationParameterTypeMap();
395 auto entry
= staticMap
.find(name
);
396 if (entry
== staticMap
.end())
398 throw ValueError("Named parameter has unknown type mapping.");
400 return entry
->second
;
405 * \brief Handle / manager for GROMACS molecular computation input parameters.
407 * Interface should be consistent with MDP file entries, but data maps to TPR
408 * file interface. For type safety and simplicity, we don't have generic operator
409 * accessors. Instead, we have templated accessors that throw exceptions when
412 * When MDP input is entirely stored in a key-value tree, this class can be a
413 * simple adapter or wrapper. Until then, we need a manually maintained mapping
414 * of MDP entries to TPR data.
416 * Alternatively, we could update the infrastructure used by list_tpx to provide
417 * more generic output, but our efforts may be better spent in updating the
418 * infrastructure for the key-value tree input system.
420 class GmxMdParamsImpl final
424 * \brief Create an initialized but empty parameters structure.
426 * Parameter keys are set at construction, but all values are empty. This
427 * allows the caller to check for valid parameter names or their types,
428 * while allowing the consuming code to know which parameters were explicitly
431 * To load values from a TPR file, see getMdParams().
435 explicit GmxMdParamsImpl(std::shared_ptr
<TprContents
> tprContents
);
438 * \brief Get the current list of keys.
442 std::vector
<std::string
> keys() const
444 std::vector
<std::string
> keyList
;
445 for (auto && entry
: int64Params_
)
447 keyList
.emplace_back(entry
.first
);
449 for (auto && entry
: intParams_
)
451 keyList
.emplace_back(entry
.first
);
453 for (auto && entry
: floatParams_
)
455 keyList
.emplace_back(entry
.first
);
457 for (auto && entry
: float64Params_
)
459 keyList
.emplace_back(entry
.first
);
464 template<typename T
> T
extract(const std::string
& /* key */) const
467 // should be an APIError
468 throw TypeError("unhandled type");
471 void set(const std::string
&key
, const int64_t &value
)
473 if (int64Params_
.find(key
) != int64Params_
.end())
475 int64Params_
[key
] = std::make_pair(value
, true);
479 auto memberPointer
= int64Params().at(key
);
480 source_
->inputRecord().*memberPointer
= value
;
483 else if (intParams_
.find(key
) != intParams_
.end())
485 // TODO: check whether value is too large?
486 intParams_
[key
] = std::make_pair(static_cast<int>(value
), true);
490 auto memberPointer
= int32Params().at(key
);
491 source_
->inputRecord().*memberPointer
= value
;
497 throw KeyError("Named parameter is incompatible with integer type value.");
501 void set(const std::string
&key
, const double &value
)
503 if (float64Params_
.find(key
) != float64Params_
.end())
505 float64Params_
[key
] = std::make_pair(value
, true);
509 auto memberPointer
= float64Params().at(key
);
510 source_
->inputRecord().*memberPointer
= value
;
514 else if (floatParams_
.find(key
) != floatParams_
.end())
516 // TODO: check whether value is too large?
517 floatParams_
[key
] = std::make_pair(static_cast<float>(value
), true);
521 auto memberPointer
= float32Params().at(key
);
522 source_
->inputRecord().*memberPointer
= value
;
528 throw KeyError("Named parameter is incompatible with floating point type value.");
532 TprReadHandle
getSource() const
534 // Note: might return a null handle. Need to decide what that means and how to address it.
535 return TprReadHandle(source_
);
540 // Hold the settable parameters and whether or not they have been set.
541 // TODO: update to gmxapi named types?
542 std::map
< std::string
, std::pair
< int64_t, bool>> int64Params_
;
543 std::map
< std::string
, std::pair
< int, bool>> intParams_
;
544 std::map
< std::string
, std::pair
< float, bool>> floatParams_
;
545 std::map
< std::string
, std::pair
< double, bool>> float64Params_
;
547 /*! \brief Shared ownership of a pack of TPR data.
549 * This is a non-normative way to retain access to gmxapi resources.
550 * \todo Subscribe to a Context-managed resource.
552 std::shared_ptr
<TprContents
> source_
;
555 void setParam(gmxapicompat::GmxMdParams
*params
, const std::string
&name
, double value
)
557 assert(params
!= nullptr);
558 assert(params
->params_
!= nullptr);
559 params
->params_
->set(name
, value
);
562 void setParam(gmxapicompat::GmxMdParams
*params
, const std::string
&name
, int64_t value
)
564 assert(params
!= nullptr);
565 assert(params
->params_
!= nullptr);
566 params
->params_
->set(name
, value
);
569 template<typename ParamsContainerT
, typename Mapping
>
570 static void setParam(ParamsContainerT
* params
, const TprContents
&source
, const Mapping
&map
)
572 for (const auto &definition
: map
)
574 const auto &key
= definition
.first
;
575 auto memberPointer
= definition
.second
;
576 auto &irInstance
= source
.inputRecord();
577 auto fileValue
= irInstance
.*memberPointer
;
578 (*params
)[key
] = std::make_pair(fileValue
, true);
583 * \brief A GmxMdParams implementation that depends on TPR files.
587 GmxMdParamsImpl::GmxMdParamsImpl(std::shared_ptr
<gmxapicompat::TprContents
> tprContents
) :
588 source_
{std::move(tprContents
)}
592 setParam(&int64Params_
, *source_
, int64Params());
593 setParam(&intParams_
, *source_
, int32Params());
594 setParam(&float64Params_
, *source_
, float32Params());
595 setParam(&float64Params_
, *source_
, float64Params());
599 GmxMdParamsImpl::GmxMdParamsImpl() :
600 GmxMdParamsImpl(nullptr)
604 int GmxMdParamsImpl::extract
<int>(const std::string
&key
) const
606 const auto ¶ms
= intParams_
;
607 const auto &entry
= params
.find(key
);
608 if (entry
== params
.cend())
610 throw KeyError("Parameter of the requested name and type not defined.");
612 else if (!entry
->second
.second
)
614 // TODO: handle invalid and unset parameters differently.
615 throw KeyError("Parameter of the requested name not set.");
619 return entry
->second
.first
;
624 int64_t GmxMdParamsImpl::extract
<int64_t>(const std::string
&key
) const
626 const auto ¶ms
= int64Params_
;
627 const auto &entry
= params
.find(key
);
628 if (entry
== params
.cend())
630 throw KeyError("Parameter of the requested name and type not defined.");
632 else if (!entry
->second
.second
)
634 // TODO: handle invalid and unset parameters differently.
635 throw KeyError("Parameter of the requested name not set.");
639 return entry
->second
.first
;
643 float GmxMdParamsImpl::extract
<float>(const std::string
&key
) const
645 const auto ¶ms
= floatParams_
;
646 const auto &entry
= params
.find(key
);
647 if (entry
== params
.cend())
649 throw KeyError("Parameter of the requested name and type not defined.");
651 else if (!entry
->second
.second
)
653 // TODO: handle invalid and unset parameters differently.
654 throw KeyError("Parameter of the requested name not set.");
658 return entry
->second
.first
;
662 double GmxMdParamsImpl::extract
<double>(const std::string
&key
) const
664 const auto ¶ms
= float64Params_
;
665 const auto &entry
= params
.find(key
);
666 if (entry
== params
.cend())
668 throw KeyError("Parameter of the requested name and type not defined.");
670 else if (!entry
->second
.second
)
672 // TODO: handle invalid and unset parameters differently.
673 throw KeyError("Parameter of the requested name not set.");
677 return entry
->second
.first
;
682 int extractParam(const GmxMdParams
¶ms
, const std::string
&name
, int)
684 assert(params
.params_
);
685 return params
.params_
->extract
<int>(name
);
688 int64_t extractParam(const GmxMdParams
¶ms
, const std::string
&name
, int64_t)
690 assert(params
.params_
);
692 // Allow fetching both known integer types.
695 value
= params
.params_
->extract
<int>(name
);
697 catch (const KeyError
&error
)
699 // If not found as a regular int, check for int64.
702 value
= params
.params_
->extract
<int64_t >(name
);
704 catch (const KeyError
&error64
)
706 throw KeyError("Parameter of the requested name not set.");
709 // Any other exceptions propagate out.
713 float extractParam(const GmxMdParams
¶ms
, const std::string
&name
, float)
715 assert(params
.params_
);
716 return params
.params_
->extract
<float>(name
);
719 double extractParam(const GmxMdParams
¶ms
, const std::string
&name
, double)
721 assert(params
.params_
);
723 // Allow fetching both single and double precision.
726 value
= params
.params_
->extract
<double>(name
);
728 catch (const KeyError
&errorDouble
)
730 // If not found as a double precision value, check for single-precision.
733 value
= params
.params_
->extract
<float>(name
);
735 catch (const KeyError
&errorFloat
)
737 throw KeyError("Parameter of the requested name not set.");
740 // Any other exceptions propagate out.
744 std::vector
<std::string
> keys(const GmxMdParams
¶ms
)
746 return params
.params_
->keys();
750 TprReadHandle
readTprFile(const std::string
&filename
)
752 auto tprfile
= gmxapicompat::TprContents(filename
);
753 auto handle
= gmxapicompat::TprReadHandle(std::move(tprfile
));
757 GmxMdParams
getMdParams(const TprReadHandle
&handle
)
759 auto tprfile
= handle
.get();
760 // TODO: convert to exception / decide whether null handles are allowed.
763 params
.params_
= std::make_unique
<GmxMdParamsImpl
>(tprfile
);
767 TopologySource
getTopologySource(const TprReadHandle
&handle
)
769 TopologySource source
;
770 source
.tprFile_
= handle
.get();
774 SimulationState
getSimulationState(const TprReadHandle
&handle
)
776 SimulationState source
;
777 source
.tprFile_
= handle
.get();
781 StructureSource
getStructureSource(const TprReadHandle
&handle
)
783 StructureSource source
;
784 source
.tprFile_
= handle
.get();
788 TprReadHandle::TprReadHandle(std::shared_ptr
<TprContents
> tprFile
) :
789 tprContents_
{std::move(tprFile
)}
793 TprReadHandle
getSourceFileHandle(const GmxMdParams
¶ms
)
795 return params
.params_
->getSource();
798 void writeTprFile(const std::string
&filename
,
799 const GmxMdParams
¶ms
,
800 const StructureSource
&structure
,
801 const SimulationState
&state
,
802 const TopologySource
&topology
)
804 assert(params
.params_
);
805 // The only way we can check for consistent input right now is to make sure
806 // it all comes from the same file.
807 if (structure
.tprFile_
.get() != state
.tprFile_
.get() ||
808 state
.tprFile_
.get() != topology
.tprFile_
.get() ||
809 topology
.tprFile_
.get() != params
.params_
->getSource().get().get() ||
810 params
.params_
->getSource().get().get() != structure
.tprFile_
.get()
813 throw ValueError("writeTprFile does not yet know how to reconcile data from different TPR file sources.");
816 const auto tprFileHandle
= params
.params_
->getSource();
817 const auto tprFile
= tprFileHandle
.get();
819 const auto &inputRecord
= tprFile
->inputRecord();
820 const auto &writeState
= tprFile
->state();
821 const auto &writeTopology
= tprFile
->molecularTopology();
822 write_tpx_state(filename
.c_str(), &inputRecord
, &writeState
, &writeTopology
);
826 TprReadHandle::TprReadHandle(TprContents
&&tprFile
) :
827 TprReadHandle
{std::make_shared
<TprContents
>(std::move(tprFile
))}
831 std::shared_ptr
<TprContents
> TprReadHandle::get() const
836 // defaulted here to delay definition until after member types are defined.
837 TprReadHandle::~TprReadHandle() = default;
839 GmxMdParams::~GmxMdParams() = default;
841 GmxMdParams::GmxMdParams() :
842 params_
{std::make_unique
<GmxMdParamsImpl
>()}
845 GmxMdParams::GmxMdParams(GmxMdParams
&&) noexcept
= default;
847 GmxMdParams
&GmxMdParams::operator=(GmxMdParams
&&) noexcept
= default;
849 // maybe this should return a handle to the new file?
850 bool copy_tprfile(const gmxapicompat::TprReadHandle
&input
, std::string outFile
)
856 gmxapicompat::writeTprFile(outFile
,
857 gmxapicompat::getMdParams(input
),
858 gmxapicompat::getStructureSource(input
),
859 gmxapicompat::getSimulationState(input
),
860 gmxapicompat::getTopologySource(input
));
864 bool rewrite_tprfile(std::string inFile
, std::string outFile
, double endTime
)
866 bool success
= false;
868 const char * top_fn
= inFile
.c_str();
870 t_inputrec irInstance
;
873 read_tpx_state(top_fn
, &irInstance
, &state
, &mtop
);
875 /* set program name, command line, and default values for output options */
876 gmx_output_env_t
*oenv
;
877 gmx::TimeUnit timeUnit
= gmx::TimeUnit_Default
;
880 }; // argument that says we don't want to view graphs.
884 output_env_init(&oenv
, gmx::getProgramContext(),
885 static_cast<time_unit_t
>(timeUnit
+ 1), bView
, // NOLINT(misc-misplaced-widening-cast)
886 static_cast<xvg_format_t
>(xvgFormat
+ 1), 0);
888 double run_t
= irInstance
.init_step
*irInstance
.delta_t
+ irInstance
.init_t
;
890 irInstance
.nsteps
= static_cast<int64_t>((endTime
- run_t
) / irInstance
.delta_t
+ 0.5);
892 write_tpx_state(outFile
.c_str(), &irInstance
, &state
, &mtop
);
898 } // end namespace gmxapicompat