b82da7cf1e72cdceebac689d78c7848ec316594d
[alexxy/gromacs.git] / src / gromacs / mdtypes / checkpointdata.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \libinternal \file
36  * \brief Provides the checkpoint data structure for the modular simulator
37  *
38  * \author Pascal Merz <pascal.merz@me.com>
39  * \ingroup module_mdtypes
40  */
41
42 #ifndef GMX_MODULARSIMULATOR_CHECKPOINTDATA_H
43 #define GMX_MODULARSIMULATOR_CHECKPOINTDATA_H
44
45 #include "gromacs/math/vectypes.h"
46 #include "gromacs/utility/arrayref.h"
47 #include "gromacs/utility/keyvaluetreebuilder.h"
48
49 namespace gmx
50 {
51 class ISerializer;
52
53 /*! \libinternal
54  * \ingroup module_modularsimulator
55  * \brief The operations on CheckpointData
56  *
57  * This enum defines the two modes of operation on CheckpointData objects,
58  * reading and writing. This allows to template all access functions, which
59  * in turn enables clients to write a single function for read and write
60  * access, eliminating the risk of having read and write functions getting
61  * out of sync.
62  */
63 enum class CheckpointDataOperation
64 {
65     Read,
66     Write,
67     Count
68 };
69
70 /*! \internal
71  * \ingroup module_modularsimulator
72  * \brief Get an ArrayRef whose const-ness is defined by the checkpointing operation
73  *
74  * \tparam operation  Whether we are reading or writing
75  * \tparam T          The type of values stored in the ArrayRef
76  * \param container   The container the ArrayRef is referencing to
77  * \return            The ArrayRef
78  *
79  * \see ArrayRef
80  */
81 template<CheckpointDataOperation operation, typename T>
82 ArrayRef<std::conditional_t<operation == CheckpointDataOperation::Write || std::is_const<T>::value, const typename T::value_type, typename T::value_type>>
83 makeCheckpointArrayRef(T& container)
84 {
85     return container;
86 }
87
88 /*! \internal
89  * \ingroup module_modularsimulator
90  * \brief Struct allowing to check if data is serializable through the KeyValueTree serializer
91  *
92  * This list of types is copied from ValueSerializer::initSerializers()
93  * Having this here allows us to catch errors at compile time
94  * instead of having cryptic runtime errors
95  */
96 template<typename T>
97 struct IsSerializableType
98 {
99     static bool const value = std::is_same<T, std::string>::value || std::is_same<T, bool>::value
100                               || std::is_same<T, int>::value || std::is_same<T, int64_t>::value
101                               || std::is_same<T, float>::value || std::is_same<T, double>::value;
102 };
103
104 /*! \internal
105  * \ingroup module_modularsimulator
106  * \brief Struct allowing to check if enum has a serializable underlying type
107  */
108 //! {
109 template<typename T, bool = std::is_enum<T>::value>
110 struct IsSerializableEnum
111 {
112     static bool const value = IsSerializableType<std::underlying_type_t<T>>::value;
113 };
114 template<typename T>
115 struct IsSerializableEnum<T, false>
116 {
117     static bool const value = false;
118 };
119 //! }
120
121 /*! \libinternal
122  * \ingroup module_modularsimulator
123  * \brief Data type hiding checkpoint implementation details
124  *
125  * This data type allows to separate the implementation details of the
126  * checkpoint writing / reading from the implementation of the checkpoint
127  * clients. Checkpoint clients interface via the methods of the CheckpointData
128  * object, and do not need knowledge of data types used to store the data.
129  *
130  * Templating allows checkpoint clients to have symmetric (templated)
131  * implementations for checkpoint reading and writing.
132  *
133  * CheckpointData objects are dispatched via [Write|Read]CheckpointDataHolder
134  * objects, which interact with the checkpoint reading from / writing to
135  * file.
136  */
137
138 template<CheckpointDataOperation operation>
139 class CheckpointData;
140
141 // Shortcuts
142 using ReadCheckpointData  = CheckpointData<CheckpointDataOperation::Read>;
143 using WriteCheckpointData = CheckpointData<CheckpointDataOperation::Write>;
144
145 template<>
146 class CheckpointData<CheckpointDataOperation::Read>
147 {
148 public:
149     /*! \brief Read or write a single value from / to checkpoint
150      *
151      * Allowed scalar types include std::string, bool, int, int64_t,
152      * float, double, or any enum with one of the previously mentioned
153      * scalar types as underlying type. Type compatibility is checked
154      * at compile time.
155      *
156      * \tparam operation  Whether we are reading or writing
157      * \tparam T          The type of the value
158      * \param key         The key to [read|write] the value [from|to]
159      * \param value       The value to [read|write]
160      */
161     //! {
162     template<typename T>
163     std::enable_if_t<IsSerializableType<T>::value, void> scalar(const std::string& key, T* value) const;
164     template<typename T>
165     std::enable_if_t<IsSerializableEnum<T>::value, void> enumScalar(const std::string& key, T* value) const;
166     //! }
167
168     /*! \brief Read or write an ArrayRef from / to checkpoint
169      *
170      * Allowed types stored in the ArrayRef include std::string, bool, int,
171      * int64_t, float, double, and gmx::RVec. Type compatibility is checked
172      * at compile time.
173      *
174      * \tparam operation  Whether we are reading or writing
175      * \tparam T          The type of values stored in the ArrayRef
176      * \param key         The key to [read|write] the ArrayRef [from|to]
177      * \param values      The ArrayRef to [read|write]
178      */
179     //! {
180     // Read ArrayRef of scalar
181     template<typename T>
182     std::enable_if_t<IsSerializableType<T>::value, void> arrayRef(const std::string& key,
183                                                                   ArrayRef<T>        values) const;
184     // Read ArrayRef of RVec
185     void arrayRef(const std::string& key, ArrayRef<RVec> values) const;
186     //! }
187
188     /*! \brief Read or write a tensor from / to checkpoint
189      *
190      * \tparam operation  Whether we are reading or writing
191      * \param key         The key to [read|write] the tensor [from|to]
192      * \param values      The tensor to [read|write]
193      */
194     void tensor(const std::string& key, ::tensor values) const;
195
196     /*! \brief Return a subset of the current CheckpointData
197      *
198      * \tparam operation  Whether we are reading or writing
199      * \param key         The key to [read|write] the sub data [from|to]
200      * \return            A CheckpointData object representing a subset of the current object
201      */
202     //!{
203     CheckpointData subCheckpointData(const std::string& key) const;
204     //!}
205
206 private:
207     //! KV tree read from checkpoint
208     const KeyValueTreeObject* inputTree_ = nullptr;
209
210     //! Construct an input checkpoint data object
211     explicit CheckpointData(const KeyValueTreeObject& inputTree);
212
213     // Only holders should build
214     friend class ReadCheckpointDataHolder;
215 };
216
217 template<>
218 class CheckpointData<CheckpointDataOperation::Write>
219 {
220 public:
221     //! \copydoc CheckpointData<CheckpointDataOperation::Read>::scalar
222     //! {
223     template<typename T>
224     std::enable_if_t<IsSerializableType<T>::value, void> scalar(const std::string& key, const T* value);
225     template<typename T>
226     std::enable_if_t<IsSerializableEnum<T>::value, void> enumScalar(const std::string& key, const T* value);
227     //! }
228
229     //! \copydoc CheckpointData<CheckpointDataOperation::Read>::arrayRef
230     //! {
231     // Write ArrayRef of scalar
232     template<typename T>
233     std::enable_if_t<IsSerializableType<T>::value, void> arrayRef(const std::string& key,
234                                                                   ArrayRef<const T>  values);
235     // Write ArrayRef of RVec
236     void arrayRef(const std::string& key, ArrayRef<const RVec> values);
237     //! }
238
239     //! \copydoc CheckpointData<CheckpointDataOperation::Read>::tensor
240     void tensor(const std::string& key, const ::tensor values);
241
242     //! \copydoc CheckpointData<CheckpointDataOperation::Read>::subCheckpointData
243     CheckpointData subCheckpointData(const std::string& key);
244
245 private:
246     //! Builder for the tree to be written to checkpoint
247     std::optional<KeyValueTreeObjectBuilder> outputTreeBuilder_ = std::nullopt;
248
249     //! Construct an output checkpoint data object
250     explicit CheckpointData(KeyValueTreeObjectBuilder&& outputTreeBuilder);
251
252     // Only holders should build
253     friend class WriteCheckpointDataHolder;
254 };
255
256
257 /*! \libinternal
258  * \brief Holder for read checkpoint data
259  *
260  * A ReadCheckpointDataHolder object is passed to the checkpoint reading
261  * functionality, and then passed into the SimulatorBuilder object. It
262  * holds the KV-tree read from file and dispatches CheckpointData objects
263  * to the checkpoint clients.
264  */
265 class ReadCheckpointDataHolder
266 {
267 public:
268     //! Check whether a key exists
269     [[nodiscard]] bool keyExists(const std::string& key) const;
270
271     //! Return vector of existing keys
272     [[nodiscard]] std::vector<std::string> keys() const;
273
274     //! Deserialize serializer content into the CheckpointData object
275     void deserialize(ISerializer* serializer);
276
277     /*! \brief Return a subset of the current CheckpointData
278      *
279      * \param key         The key to [read|write] the sub data [from|to]
280      * \return            A CheckpointData object representing a subset of the current object
281      */
282     [[nodiscard]] ReadCheckpointData checkpointData(const std::string& key) const;
283
284 private:
285     //! KV-tree read from checkpoint
286     KeyValueTreeObject checkpointTree_;
287 };
288
289 /*! \libinternal
290  * \brief Holder for write checkpoint data
291  *
292  * The WriteCheckpointDataHolder object holds the KV-tree builder and
293  * dispatches CheckpointData objects to the checkpoint clients to save
294  * their respective data. It is then passed to the checkpoint writing
295  * functionality.
296  */
297 class WriteCheckpointDataHolder
298 {
299 public:
300     //! Serialize the content of the CheckpointData object
301     void serialize(ISerializer* serializer);
302
303     /*! \brief Return a subset of the current CheckpointData
304      *
305      * \param key         The key to [read|write] the sub data [from|to]
306      * \return            A CheckpointData object representing a subset of the current object
307      */
308     [[nodiscard]] WriteCheckpointData checkpointData(const std::string& key);
309
310     /*! \brief
311      */
312     [[nodiscard]] bool empty() const;
313
314 private:
315     //! KV-tree builder
316     KeyValueTreeBuilder outputTreeBuilder_;
317     //! Whether any checkpoint data object has been requested
318     bool hasCheckpointDataBeenRequested_ = false;
319 };
320
321 // Function definitions - here to avoid template-related linker problems
322 // doxygen doesn't like these...
323 //! \cond
324 template<typename T>
325 std::enable_if_t<IsSerializableType<T>::value, void> ReadCheckpointData::scalar(const std::string& key,
326                                                                                 T* value) const
327 {
328     GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
329     *value = (*inputTree_)[key].cast<T>();
330 }
331
332 template<typename T>
333 std::enable_if_t<IsSerializableEnum<T>::value, void> ReadCheckpointData::enumScalar(const std::string& key,
334                                                                                     T* value) const
335 {
336     GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
337     std::underlying_type_t<T> castValue;
338     castValue = (*inputTree_)[key].cast<std::underlying_type_t<T>>();
339     *value    = static_cast<T>(castValue);
340 }
341
342 template<typename T>
343 inline std::enable_if_t<IsSerializableType<T>::value, void>
344 WriteCheckpointData::scalar(const std::string& key, const T* value)
345 {
346     GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
347     outputTreeBuilder_->addValue(key, *value);
348 }
349
350 template<typename T>
351 inline std::enable_if_t<IsSerializableEnum<T>::value, void>
352 WriteCheckpointData::enumScalar(const std::string& key, const T* value)
353 {
354     GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
355     auto castValue = static_cast<std::underlying_type_t<T>>(*value);
356     outputTreeBuilder_->addValue(key, castValue);
357 }
358
359 template<typename T>
360 inline std::enable_if_t<IsSerializableType<T>::value, void>
361 ReadCheckpointData::arrayRef(const std::string& key, ArrayRef<T> values) const
362 {
363     GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
364     GMX_RELEASE_ASSERT(values.size() >= (*inputTree_)[key].asArray().values().size(),
365                        "Read vector does not fit in passed ArrayRef.");
366     auto outputIt  = values.begin();
367     auto inputIt   = (*inputTree_)[key].asArray().values().begin();
368     auto outputEnd = values.end();
369     auto inputEnd  = (*inputTree_)[key].asArray().values().end();
370     for (; outputIt != outputEnd && inputIt != inputEnd; outputIt++, inputIt++)
371     {
372         *outputIt = inputIt->cast<T>();
373     }
374 }
375
376 template<typename T>
377 inline std::enable_if_t<IsSerializableType<T>::value, void>
378 WriteCheckpointData::arrayRef(const std::string& key, ArrayRef<const T> values)
379 {
380     GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
381     auto builder = outputTreeBuilder_->addUniformArray<T>(key);
382     for (const auto& value : values)
383     {
384         builder.addValue(value);
385     }
386 }
387
388 inline void ReadCheckpointData::arrayRef(const std::string& key, ArrayRef<RVec> values) const
389 {
390     GMX_RELEASE_ASSERT(values.size() >= (*inputTree_)[key].asArray().values().size(),
391                        "Read vector does not fit in passed ArrayRef.");
392     auto outputIt  = values.begin();
393     auto inputIt   = (*inputTree_)[key].asArray().values().begin();
394     auto outputEnd = values.end();
395     auto inputEnd  = (*inputTree_)[key].asArray().values().end();
396     for (; outputIt != outputEnd && inputIt != inputEnd; outputIt++, inputIt++)
397     {
398         auto storedRVec = inputIt->asObject()["RVec"].asArray().values();
399         *outputIt       = { storedRVec[XX].cast<real>(), storedRVec[YY].cast<real>(),
400                       storedRVec[ZZ].cast<real>() };
401     }
402 }
403
404 inline void WriteCheckpointData::arrayRef(const std::string& key, ArrayRef<const RVec> values)
405 {
406     auto builder = outputTreeBuilder_->addObjectArray(key);
407     for (const auto& value : values)
408     {
409         auto subbuilder = builder.addObject();
410         subbuilder.addUniformArray("RVec", { value[XX], value[YY], value[ZZ] });
411     }
412 }
413
414 inline void ReadCheckpointData::tensor(const std::string& key, ::tensor values) const
415 {
416     auto array     = (*inputTree_)[key].asArray().values();
417     values[XX][XX] = array[0].cast<real>();
418     values[XX][YY] = array[1].cast<real>();
419     values[XX][ZZ] = array[2].cast<real>();
420     values[YY][XX] = array[3].cast<real>();
421     values[YY][YY] = array[4].cast<real>();
422     values[YY][ZZ] = array[5].cast<real>();
423     values[ZZ][XX] = array[6].cast<real>();
424     values[ZZ][YY] = array[7].cast<real>();
425     values[ZZ][ZZ] = array[8].cast<real>();
426 }
427
428 inline void WriteCheckpointData::tensor(const std::string& key, const ::tensor values)
429 {
430     auto builder = outputTreeBuilder_->addUniformArray<real>(key);
431     builder.addValue(values[XX][XX]);
432     builder.addValue(values[XX][YY]);
433     builder.addValue(values[XX][ZZ]);
434     builder.addValue(values[YY][XX]);
435     builder.addValue(values[YY][YY]);
436     builder.addValue(values[YY][ZZ]);
437     builder.addValue(values[ZZ][XX]);
438     builder.addValue(values[ZZ][YY]);
439     builder.addValue(values[ZZ][ZZ]);
440 }
441
442 inline ReadCheckpointData ReadCheckpointData::subCheckpointData(const std::string& key) const
443 {
444     return CheckpointData((*inputTree_)[key].asObject());
445 }
446
447 inline WriteCheckpointData WriteCheckpointData::subCheckpointData(const std::string& key)
448 {
449     return CheckpointData(outputTreeBuilder_->addObject(key));
450 }
451
452 inline ReadCheckpointData::CheckpointData(const KeyValueTreeObject& inputTree) :
453     inputTree_(&inputTree)
454 {
455 }
456
457 inline WriteCheckpointData::CheckpointData(KeyValueTreeObjectBuilder&& outputTreeBuilder) :
458     outputTreeBuilder_(outputTreeBuilder)
459 {
460 }
461 //! \endcond
462
463 } // namespace gmx
464
465 #endif // GMX_MODULARSIMULATOR_CHECKPOINTDATA_H