Fix Visual Studio build
[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 <optional>
46
47 #include "gromacs/math/vectypes.h"
48 #include "gromacs/utility/arrayref.h"
49 #include "gromacs/utility/keyvaluetreebuilder.h"
50
51 namespace gmx
52 {
53 class ISerializer;
54
55 /*! \libinternal
56  * \ingroup module_modularsimulator
57  * \brief The operations on CheckpointData
58  *
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
63  * out of sync.
64  */
65 enum class CheckpointDataOperation
66 {
67     Read,
68     Write,
69     Count
70 };
71
72 /*! \internal
73  * \ingroup module_modularsimulator
74  * \brief Get an ArrayRef whose const-ness is defined by the checkpointing operation
75  *
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
80  *
81  * \see ArrayRef
82  */
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)
86 {
87     return container;
88 }
89
90 /*! \internal
91  * \ingroup module_modularsimulator
92  * \brief Struct allowing to check if data is serializable through the KeyValueTree serializer
93  *
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
97  */
98 template<typename T>
99 struct IsSerializableType
100 {
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;
104 };
105
106 /*! \internal
107  * \ingroup module_modularsimulator
108  * \brief Struct allowing to check if enum has a serializable underlying type
109  */
110 //! {
111 template<typename T, bool = std::is_enum<T>::value>
112 struct IsSerializableEnum
113 {
114     static bool const value = IsSerializableType<std::underlying_type_t<T>>::value;
115 };
116 template<typename T>
117 struct IsSerializableEnum<T, false>
118 {
119     static bool const value = false;
120 };
121 //! }
122
123 /*! \libinternal
124  * \ingroup module_modularsimulator
125  * \brief Data type hiding checkpoint implementation details
126  *
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.
131  *
132  * Templating allows checkpoint clients to have symmetric (templated)
133  * implementations for checkpoint reading and writing.
134  *
135  * CheckpointData objects are dispatched via [Write|Read]CheckpointDataHolder
136  * objects, which interact with the checkpoint reading from / writing to
137  * file.
138  */
139
140 template<CheckpointDataOperation operation>
141 class CheckpointData;
142
143 // Shortcuts
144 using ReadCheckpointData  = CheckpointData<CheckpointDataOperation::Read>;
145 using WriteCheckpointData = CheckpointData<CheckpointDataOperation::Write>;
146
147 template<>
148 class CheckpointData<CheckpointDataOperation::Read>
149 {
150 public:
151     /*! \brief Read or write a single value from / to checkpoint
152      *
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
156      * at compile time.
157      *
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]
162      */
163     //! {
164     template<typename T>
165     std::enable_if_t<IsSerializableType<T>::value, void> scalar(const std::string& key, T* value) const;
166     template<typename T>
167     std::enable_if_t<IsSerializableEnum<T>::value, void> enumScalar(const std::string& key, T* value) const;
168     //! }
169
170     /*! \brief Read or write an ArrayRef from / to checkpoint
171      *
172      * Allowed types stored in the ArrayRef include std::string, bool, int,
173      * int64_t, float, double, and gmx::RVec. Type compatibility is checked
174      * at compile time.
175      *
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]
180      */
181     //! {
182     // Read ArrayRef of scalar
183     template<typename T>
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;
188     //! }
189
190     /*! \brief Read or write a tensor from / to checkpoint
191      *
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]
195      */
196     void tensor(const std::string& key, ::tensor values) const;
197
198     /*! \brief Return a subset of the current CheckpointData
199      *
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
203      */
204     //!{
205     CheckpointData subCheckpointData(const std::string& key) const;
206     //!}
207
208 private:
209     //! KV tree read from checkpoint
210     const KeyValueTreeObject* inputTree_ = nullptr;
211
212     //! Construct an input checkpoint data object
213     explicit CheckpointData(const KeyValueTreeObject& inputTree);
214
215     // Only holders should build
216     friend class ReadCheckpointDataHolder;
217 };
218
219 template<>
220 class CheckpointData<CheckpointDataOperation::Write>
221 {
222 public:
223     //! \copydoc CheckpointData<CheckpointDataOperation::Read>::scalar
224     //! {
225     template<typename T>
226     std::enable_if_t<IsSerializableType<T>::value, void> scalar(const std::string& key, const T* value);
227     template<typename T>
228     std::enable_if_t<IsSerializableEnum<T>::value, void> enumScalar(const std::string& key, const T* value);
229     //! }
230
231     //! \copydoc CheckpointData<CheckpointDataOperation::Read>::arrayRef
232     //! {
233     // Write ArrayRef of scalar
234     template<typename T>
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);
239     //! }
240
241     //! \copydoc CheckpointData<CheckpointDataOperation::Read>::tensor
242     void tensor(const std::string& key, const ::tensor values);
243
244     //! \copydoc CheckpointData<CheckpointDataOperation::Read>::subCheckpointData
245     CheckpointData subCheckpointData(const std::string& key);
246
247 private:
248     //! Builder for the tree to be written to checkpoint
249     std::optional<KeyValueTreeObjectBuilder> outputTreeBuilder_ = std::nullopt;
250
251     //! Construct an output checkpoint data object
252     explicit CheckpointData(KeyValueTreeObjectBuilder&& outputTreeBuilder);
253
254     // Only holders should build
255     friend class WriteCheckpointDataHolder;
256 };
257
258
259 /*! \libinternal
260  * \brief Holder for read checkpoint data
261  *
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.
266  */
267 class ReadCheckpointDataHolder
268 {
269 public:
270     //! Check whether a key exists
271     [[nodiscard]] bool keyExists(const std::string& key) const;
272
273     //! Return vector of existing keys
274     [[nodiscard]] std::vector<std::string> keys() const;
275
276     //! Deserialize serializer content into the CheckpointData object
277     void deserialize(ISerializer* serializer);
278
279     /*! \brief Return a subset of the current CheckpointData
280      *
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
283      */
284     [[nodiscard]] ReadCheckpointData checkpointData(const std::string& key) const;
285
286 private:
287     //! KV-tree read from checkpoint
288     KeyValueTreeObject checkpointTree_;
289 };
290
291 /*! \libinternal
292  * \brief Holder for write checkpoint data
293  *
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
297  * functionality.
298  */
299 class WriteCheckpointDataHolder
300 {
301 public:
302     //! Serialize the content of the CheckpointData object
303     void serialize(ISerializer* serializer);
304
305     /*! \brief Return a subset of the current CheckpointData
306      *
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
309      */
310     [[nodiscard]] WriteCheckpointData checkpointData(const std::string& key);
311
312     /*! \brief
313      */
314     [[nodiscard]] bool empty() const;
315
316 private:
317     //! KV-tree builder
318     KeyValueTreeBuilder outputTreeBuilder_;
319     //! Whether any checkpoint data object has been requested
320     bool hasCheckpointDataBeenRequested_ = false;
321 };
322
323 // Function definitions - here to avoid template-related linker problems
324 // doxygen doesn't like these...
325 //! \cond
326 template<typename T>
327 std::enable_if_t<IsSerializableType<T>::value, void> ReadCheckpointData::scalar(const std::string& key,
328                                                                                 T* value) const
329 {
330     GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
331     *value = (*inputTree_)[key].cast<T>();
332 }
333
334 template<typename T>
335 std::enable_if_t<IsSerializableEnum<T>::value, void> ReadCheckpointData::enumScalar(const std::string& key,
336                                                                                     T* value) const
337 {
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);
342 }
343
344 template<typename T>
345 inline std::enable_if_t<IsSerializableType<T>::value, void>
346 WriteCheckpointData::scalar(const std::string& key, const T* value)
347 {
348     GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
349     outputTreeBuilder_->addValue(key, *value);
350 }
351
352 template<typename T>
353 inline std::enable_if_t<IsSerializableEnum<T>::value, void>
354 WriteCheckpointData::enumScalar(const std::string& key, const T* value)
355 {
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);
359 }
360
361 template<typename T>
362 inline std::enable_if_t<IsSerializableType<T>::value, void>
363 ReadCheckpointData::arrayRef(const std::string& key, ArrayRef<T> values) const
364 {
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++)
373     {
374         *outputIt = inputIt->cast<T>();
375     }
376 }
377
378 template<typename T>
379 inline std::enable_if_t<IsSerializableType<T>::value, void>
380 WriteCheckpointData::arrayRef(const std::string& key, ArrayRef<const T> values)
381 {
382     GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
383     auto builder = outputTreeBuilder_->addUniformArray<T>(key);
384     for (const auto& value : values)
385     {
386         builder.addValue(value);
387     }
388 }
389
390 inline void ReadCheckpointData::arrayRef(const std::string& key, ArrayRef<RVec> values) const
391 {
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++)
399     {
400         auto storedRVec = inputIt->asObject()["RVec"].asArray().values();
401         *outputIt       = { storedRVec[XX].cast<real>(), storedRVec[YY].cast<real>(),
402                       storedRVec[ZZ].cast<real>() };
403     }
404 }
405
406 inline void WriteCheckpointData::arrayRef(const std::string& key, ArrayRef<const RVec> values)
407 {
408     auto builder = outputTreeBuilder_->addObjectArray(key);
409     for (const auto& value : values)
410     {
411         auto subbuilder = builder.addObject();
412         subbuilder.addUniformArray("RVec", { value[XX], value[YY], value[ZZ] });
413     }
414 }
415
416 inline void ReadCheckpointData::tensor(const std::string& key, ::tensor values) const
417 {
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>();
428 }
429
430 inline void WriteCheckpointData::tensor(const std::string& key, const ::tensor values)
431 {
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]);
442 }
443
444 inline ReadCheckpointData ReadCheckpointData::subCheckpointData(const std::string& key) const
445 {
446     return CheckpointData((*inputTree_)[key].asObject());
447 }
448
449 inline WriteCheckpointData WriteCheckpointData::subCheckpointData(const std::string& key)
450 {
451     return CheckpointData(outputTreeBuilder_->addObject(key));
452 }
453
454 inline ReadCheckpointData::CheckpointData(const KeyValueTreeObject& inputTree) :
455     inputTree_(&inputTree)
456 {
457 }
458
459 inline WriteCheckpointData::CheckpointData(KeyValueTreeObjectBuilder&& outputTreeBuilder) :
460     outputTreeBuilder_(outputTreeBuilder)
461 {
462 }
463 //! \endcond
464
465 } // namespace gmx
466
467 #endif // GMX_MODULARSIMULATOR_CHECKPOINTDATA_H