2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2020, 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.
35 /*! \libinternal \file
36 * \brief Provides the checkpoint data structure for the modular simulator
38 * \author Pascal Merz <pascal.merz@me.com>
39 * \ingroup module_mdtypes
42 #ifndef GMX_MODULARSIMULATOR_CHECKPOINTDATA_H
43 #define GMX_MODULARSIMULATOR_CHECKPOINTDATA_H
47 #include "gromacs/math/vectypes.h"
48 #include "gromacs/utility/arrayref.h"
49 #include "gromacs/utility/keyvaluetreebuilder.h"
56 * \ingroup module_modularsimulator
57 * \brief The operations on CheckpointData
59 * This enum defines the two modes of operation on CheckpointData objects,
60 * reading and writing. This allows to template all access functions, which
61 * in turn enables clients to write a single function for read and write
62 * access, eliminating the risk of having read and write functions getting
65 enum class CheckpointDataOperation
73 * \ingroup module_modularsimulator
74 * \brief Get an ArrayRef whose const-ness is defined by the checkpointing operation
76 * \tparam operation Whether we are reading or writing
77 * \tparam T The type of values stored in the ArrayRef
78 * \param container The container the ArrayRef is referencing to
79 * \return The ArrayRef
83 template<CheckpointDataOperation operation, typename T>
84 ArrayRef<std::conditional_t<operation == CheckpointDataOperation::Write || std::is_const<T>::value, const typename T::value_type, typename T::value_type>>
85 makeCheckpointArrayRef(T& container)
91 * \ingroup module_modularsimulator
92 * \brief Struct allowing to check if data is serializable through the KeyValueTree serializer
94 * This list of types is copied from ValueSerializer::initSerializers()
95 * Having this here allows us to catch errors at compile time
96 * instead of having cryptic runtime errors
99 struct IsSerializableType
101 static bool const value = std::is_same<T, std::string>::value || std::is_same<T, bool>::value
102 || std::is_same<T, int>::value || std::is_same<T, int64_t>::value
103 || std::is_same<T, float>::value || std::is_same<T, double>::value;
107 * \ingroup module_modularsimulator
108 * \brief Struct allowing to check if enum has a serializable underlying type
111 template<typename T, bool = std::is_enum<T>::value>
112 struct IsSerializableEnum
114 static bool const value = IsSerializableType<std::underlying_type_t<T>>::value;
117 struct IsSerializableEnum<T, false>
119 static bool const value = false;
124 * \ingroup module_modularsimulator
125 * \brief Data type hiding checkpoint implementation details
127 * This data type allows to separate the implementation details of the
128 * checkpoint writing / reading from the implementation of the checkpoint
129 * clients. Checkpoint clients interface via the methods of the CheckpointData
130 * object, and do not need knowledge of data types used to store the data.
132 * Templating allows checkpoint clients to have symmetric (templated)
133 * implementations for checkpoint reading and writing.
135 * CheckpointData objects are dispatched via [Write|Read]CheckpointDataHolder
136 * objects, which interact with the checkpoint reading from / writing to
140 template<CheckpointDataOperation operation>
141 class CheckpointData;
144 using ReadCheckpointData = CheckpointData<CheckpointDataOperation::Read>;
145 using WriteCheckpointData = CheckpointData<CheckpointDataOperation::Write>;
148 class CheckpointData<CheckpointDataOperation::Read>
151 /*! \brief Read or write a single value from / to checkpoint
153 * Allowed scalar types include std::string, bool, int, int64_t,
154 * float, double, or any enum with one of the previously mentioned
155 * scalar types as underlying type. Type compatibility is checked
158 * \tparam operation Whether we are reading or writing
159 * \tparam T The type of the value
160 * \param key The key to [read|write] the value [from|to]
161 * \param value The value to [read|write]
165 std::enable_if_t<IsSerializableType<T>::value, void> scalar(const std::string& key, T* value) const;
167 std::enable_if_t<IsSerializableEnum<T>::value, void> enumScalar(const std::string& key, T* value) const;
170 /*! \brief Read or write an ArrayRef from / to checkpoint
172 * Allowed types stored in the ArrayRef include std::string, bool, int,
173 * int64_t, float, double, and gmx::RVec. Type compatibility is checked
176 * \tparam operation Whether we are reading or writing
177 * \tparam T The type of values stored in the ArrayRef
178 * \param key The key to [read|write] the ArrayRef [from|to]
179 * \param values The ArrayRef to [read|write]
182 // Read ArrayRef of scalar
184 std::enable_if_t<IsSerializableType<T>::value, void> arrayRef(const std::string& key,
185 ArrayRef<T> values) const;
186 // Read ArrayRef of RVec
187 void arrayRef(const std::string& key, ArrayRef<RVec> values) const;
190 /*! \brief Read or write a tensor from / to checkpoint
192 * \tparam operation Whether we are reading or writing
193 * \param key The key to [read|write] the tensor [from|to]
194 * \param values The tensor to [read|write]
196 void tensor(const std::string& key, ::tensor values) const;
198 /*! \brief Return a subset of the current CheckpointData
200 * \tparam operation Whether we are reading or writing
201 * \param key The key to [read|write] the sub data [from|to]
202 * \return A CheckpointData object representing a subset of the current object
205 CheckpointData subCheckpointData(const std::string& key) const;
209 //! KV tree read from checkpoint
210 const KeyValueTreeObject* inputTree_ = nullptr;
212 //! Construct an input checkpoint data object
213 explicit CheckpointData(const KeyValueTreeObject& inputTree);
215 // Only holders should build
216 friend class ReadCheckpointDataHolder;
220 class CheckpointData<CheckpointDataOperation::Write>
223 //! \copydoc CheckpointData<CheckpointDataOperation::Read>::scalar
226 std::enable_if_t<IsSerializableType<T>::value, void> scalar(const std::string& key, const T* value);
228 std::enable_if_t<IsSerializableEnum<T>::value, void> enumScalar(const std::string& key, const T* value);
231 //! \copydoc CheckpointData<CheckpointDataOperation::Read>::arrayRef
233 // Write ArrayRef of scalar
235 std::enable_if_t<IsSerializableType<T>::value, void> arrayRef(const std::string& key,
236 ArrayRef<const T> values);
237 // Write ArrayRef of RVec
238 void arrayRef(const std::string& key, ArrayRef<const RVec> values);
241 //! \copydoc CheckpointData<CheckpointDataOperation::Read>::tensor
242 void tensor(const std::string& key, const ::tensor values);
244 //! \copydoc CheckpointData<CheckpointDataOperation::Read>::subCheckpointData
245 CheckpointData subCheckpointData(const std::string& key);
248 //! Builder for the tree to be written to checkpoint
249 std::optional<KeyValueTreeObjectBuilder> outputTreeBuilder_ = std::nullopt;
251 //! Construct an output checkpoint data object
252 explicit CheckpointData(KeyValueTreeObjectBuilder&& outputTreeBuilder);
254 // Only holders should build
255 friend class WriteCheckpointDataHolder;
260 * \brief Holder for read checkpoint data
262 * A ReadCheckpointDataHolder object is passed to the checkpoint reading
263 * functionality, and then passed into the SimulatorBuilder object. It
264 * holds the KV-tree read from file and dispatches CheckpointData objects
265 * to the checkpoint clients.
267 class ReadCheckpointDataHolder
270 //! Check whether a key exists
271 [[nodiscard]] bool keyExists(const std::string& key) const;
273 //! Return vector of existing keys
274 [[nodiscard]] std::vector<std::string> keys() const;
276 //! Deserialize serializer content into the CheckpointData object
277 void deserialize(ISerializer* serializer);
279 /*! \brief Return a subset of the current CheckpointData
281 * \param key The key to [read|write] the sub data [from|to]
282 * \return A CheckpointData object representing a subset of the current object
284 [[nodiscard]] ReadCheckpointData checkpointData(const std::string& key) const;
287 //! KV-tree read from checkpoint
288 KeyValueTreeObject checkpointTree_;
292 * \brief Holder for write checkpoint data
294 * The WriteCheckpointDataHolder object holds the KV-tree builder and
295 * dispatches CheckpointData objects to the checkpoint clients to save
296 * their respective data. It is then passed to the checkpoint writing
299 class WriteCheckpointDataHolder
302 //! Serialize the content of the CheckpointData object
303 void serialize(ISerializer* serializer);
305 /*! \brief Return a subset of the current CheckpointData
307 * \param key The key to [read|write] the sub data [from|to]
308 * \return A CheckpointData object representing a subset of the current object
310 [[nodiscard]] WriteCheckpointData checkpointData(const std::string& key);
314 [[nodiscard]] bool empty() const;
318 KeyValueTreeBuilder outputTreeBuilder_;
319 //! Whether any checkpoint data object has been requested
320 bool hasCheckpointDataBeenRequested_ = false;
323 // Function definitions - here to avoid template-related linker problems
324 // doxygen doesn't like these...
327 std::enable_if_t<IsSerializableType<T>::value, void> ReadCheckpointData::scalar(const std::string& key,
330 GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
331 *value = (*inputTree_)[key].cast<T>();
335 std::enable_if_t<IsSerializableEnum<T>::value, void> ReadCheckpointData::enumScalar(const std::string& key,
338 GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
339 std::underlying_type_t<T> castValue;
340 castValue = (*inputTree_)[key].cast<std::underlying_type_t<T>>();
341 *value = static_cast<T>(castValue);
345 inline std::enable_if_t<IsSerializableType<T>::value, void>
346 WriteCheckpointData::scalar(const std::string& key, const T* value)
348 GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
349 outputTreeBuilder_->addValue(key, *value);
353 inline std::enable_if_t<IsSerializableEnum<T>::value, void>
354 WriteCheckpointData::enumScalar(const std::string& key, const T* value)
356 GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
357 auto castValue = static_cast<std::underlying_type_t<T>>(*value);
358 outputTreeBuilder_->addValue(key, castValue);
362 inline std::enable_if_t<IsSerializableType<T>::value, void>
363 ReadCheckpointData::arrayRef(const std::string& key, ArrayRef<T> values) const
365 GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
366 GMX_RELEASE_ASSERT(values.size() >= (*inputTree_)[key].asArray().values().size(),
367 "Read vector does not fit in passed ArrayRef.");
368 auto outputIt = values.begin();
369 auto inputIt = (*inputTree_)[key].asArray().values().begin();
370 auto outputEnd = values.end();
371 auto inputEnd = (*inputTree_)[key].asArray().values().end();
372 for (; outputIt != outputEnd && inputIt != inputEnd; outputIt++, inputIt++)
374 *outputIt = inputIt->cast<T>();
379 inline std::enable_if_t<IsSerializableType<T>::value, void>
380 WriteCheckpointData::arrayRef(const std::string& key, ArrayRef<const T> values)
382 GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
383 auto builder = outputTreeBuilder_->addUniformArray<T>(key);
384 for (const auto& value : values)
386 builder.addValue(value);
390 inline void ReadCheckpointData::arrayRef(const std::string& key, ArrayRef<RVec> values) const
392 GMX_RELEASE_ASSERT(values.size() >= (*inputTree_)[key].asArray().values().size(),
393 "Read vector does not fit in passed ArrayRef.");
394 auto outputIt = values.begin();
395 auto inputIt = (*inputTree_)[key].asArray().values().begin();
396 auto outputEnd = values.end();
397 auto inputEnd = (*inputTree_)[key].asArray().values().end();
398 for (; outputIt != outputEnd && inputIt != inputEnd; outputIt++, inputIt++)
400 auto storedRVec = inputIt->asObject()["RVec"].asArray().values();
401 *outputIt = { storedRVec[XX].cast<real>(), storedRVec[YY].cast<real>(),
402 storedRVec[ZZ].cast<real>() };
406 inline void WriteCheckpointData::arrayRef(const std::string& key, ArrayRef<const RVec> values)
408 auto builder = outputTreeBuilder_->addObjectArray(key);
409 for (const auto& value : values)
411 auto subbuilder = builder.addObject();
412 subbuilder.addUniformArray("RVec", { value[XX], value[YY], value[ZZ] });
416 inline void ReadCheckpointData::tensor(const std::string& key, ::tensor values) const
418 auto array = (*inputTree_)[key].asArray().values();
419 values[XX][XX] = array[0].cast<real>();
420 values[XX][YY] = array[1].cast<real>();
421 values[XX][ZZ] = array[2].cast<real>();
422 values[YY][XX] = array[3].cast<real>();
423 values[YY][YY] = array[4].cast<real>();
424 values[YY][ZZ] = array[5].cast<real>();
425 values[ZZ][XX] = array[6].cast<real>();
426 values[ZZ][YY] = array[7].cast<real>();
427 values[ZZ][ZZ] = array[8].cast<real>();
430 inline void WriteCheckpointData::tensor(const std::string& key, const ::tensor values)
432 auto builder = outputTreeBuilder_->addUniformArray<real>(key);
433 builder.addValue(values[XX][XX]);
434 builder.addValue(values[XX][YY]);
435 builder.addValue(values[XX][ZZ]);
436 builder.addValue(values[YY][XX]);
437 builder.addValue(values[YY][YY]);
438 builder.addValue(values[YY][ZZ]);
439 builder.addValue(values[ZZ][XX]);
440 builder.addValue(values[ZZ][YY]);
441 builder.addValue(values[ZZ][ZZ]);
444 inline ReadCheckpointData ReadCheckpointData::subCheckpointData(const std::string& key) const
446 return CheckpointData((*inputTree_)[key].asObject());
449 inline WriteCheckpointData WriteCheckpointData::subCheckpointData(const std::string& key)
451 return CheckpointData(outputTreeBuilder_->addObject(key));
454 inline ReadCheckpointData::CheckpointData(const KeyValueTreeObject& inputTree) :
455 inputTree_(&inputTree)
459 inline WriteCheckpointData::CheckpointData(KeyValueTreeObjectBuilder&& outputTreeBuilder) :
460 outputTreeBuilder_(outputTreeBuilder)
467 #endif // GMX_MODULARSIMULATOR_CHECKPOINTDATA_H