Restore gmxapi._gmxapi.add_mdmodule() Python functionality.
[alexxy/gromacs.git] / python_packaging / sample_restraint / src / cpp / sessionresources.h
1 /*! \file
2  * \brief Provide some useful types and templates for GROMACS restraints.
3  *
4  * \todo This should be part of a template library installed with GROMACS.
5  *
6  * \author M. Eric Irrgang <ericirrgang@gmail.com>
7  */
8
9 #ifndef RESTRAINT_SESSIONRESOURCES_H
10 #define RESTRAINT_SESSIONRESOURCES_H
11
12 #include <functional>
13 #include <memory>
14 #include <mutex>
15 #include <vector>
16
17 #include "gmxapi/gromacsfwd.h"
18 #include "gmxapi/session.h"
19 #include "gmxapi/session/resources.h"
20 #include "gmxapi/md/mdmodule.h"
21
22 #include "gromacs/restraint/restraintpotential.h"
23 #include "gromacs/utility/real.h"
24
25 namespace plugin
26 {
27
28 // Stop-gap for cross-language data exchange pending SharedData implementation and inclusion of
29 // Eigen. Adapted from pybind docs.
30 template<class T>
31 class Matrix
32 {
33 public:
34     Matrix(size_t rows, size_t cols) : rows_(rows), cols_(cols), data_(rows_ * cols_, 0) {}
35
36     explicit Matrix(std::vector<T>&& captured_data) :
37         rows_{ 1 }, cols_{ captured_data.size() }, data_{ std::move(captured_data) }
38     {
39     }
40
41     std::vector<T>* vector() { return &data_; }
42
43     T* data() { return data_.data(); };
44
45     [[nodiscard]] size_t rows() const { return rows_; }
46
47     [[nodiscard]] size_t cols() const { return cols_; }
48
49 private:
50     size_t         rows_;
51     size_t         cols_;
52     std::vector<T> data_;
53 };
54
55 // Defer implicit instantiation to ensemblepotential.cpp
56 extern template class Matrix<double>;
57
58 /*!
59  * \brief An active handle to ensemble resources provided by the Context.
60  *
61  * The semantics of holding this handle aren't determined yet, but it should be held as briefly as possible since it
62  * may involve locking global resources or preventing the simulation from advancing. Basically, though, it allows the
63  * Context implementation flexibility in how or where it provides services.
64  *
65  * Resources may be incoming input data or functors to trigger output data events.
66  *
67  * \internal
68  * It is not yet clear whether we want to assume that default behavior is for an operation to be called for each edge
69  * on every iterative graph execution, leaving less frequent calls as an optimization, or to fully support occasional
70  * data events issued by nodes during their execution.
71  *
72  * In this example, assume the plugin has specified that it provides a `.ostream.stop` port that provides asynchronous
73  * boolean events. We can provide a template member function that will handle either execution mode.
74  *
75  * ResourceHandle::ostream() will return access to a gmxapi::session::OutputStream object, which will provide
76  * set("stop", true), to give access to a function pointer from a member vector of function pointers.
77  *
78  * In the case that we are triggering asynchronous data events, the function will make the appropriate call. In the case
79  * that we have output at regular intervals, the function will update internal state for the next time the edge is
80  * evaluated.
81  *
82  * In an alternative implementation, we could maintain a data object that could be queried by subscribers, but a publish
83  * and subscribe model seems much more useful, optimizeable, and robust. We can issue the calls to the subscribers and
84  * then be done with it.
85  *
86  * If we need to optimize for reuse of memory locations, we can do one of two things: require that
87  * the subscribing object not return until it has done what it needed with the data (including deep copy) or use
88  * managed handles for the data, possibly with a custom allocator, that prevents rewriting while there are read handles
89  * still open. One way that came up in conversation with Mark to allow some optimization is to allow the recipient of
90  * the handle to make either an `open` that gets a potentially blocking read-lock or an `open` that requests ownership.
91  * If no other consumers of the data request ownership, the ownership can be transferred without a copy. Otherwise, a
92  * copy is made.
93  */
94 class ResourcesHandle
95 {
96 public:
97     /*!
98      * \brief Ensemble reduce.
99      *
100      * \param send Matrices to be summed across the ensemble using Context resources.
101      * \param receive destination of reduced data instead of updating internal Matrix.
102      */
103     void reduce(const Matrix<double>& send, Matrix<double>* receive) const;
104
105     /*!
106      * \brief Issue a stop condition event.
107      *
108      * Can be called on any or all ranks. Sets a condition that will cause the current simulation to
109      * shut down after the current step.
110      */
111     void stop();
112
113     // to be abstracted and hidden...
114     const std::function<void(const Matrix<double>&, Matrix<double>*)>* reduce_;
115
116     gmxapi::SessionResources* session_;
117 };
118
119 /*!
120  * \brief Reference to workflow-level resources managed by the Context.
121  *
122  * Provides a connection to the higher-level workflow management with which to access resources and operations. The
123  * reference provides no resources directly and we may find that it should not extend the life of a Session or Context.
124  * Resources are accessed through Handle objects returned by member functions.
125  *
126  * gmxapi version 0.1.0 will provide this functionality through SessionResources.
127  */
128 class Resources
129 {
130 public:
131     /*!
132      * \brief Create a new resources object.
133      *
134      * This constructor is called by the framework during Session launch to provide the plugin
135      * potential with external resources.
136      *
137      * \note If getHandle() is going to be used, setSession() must be called first.
138      *
139      * \param reduce ownership of a function object providing ensemble averaging of a 2D matrix.
140      */
141     explicit Resources(std::function<void(const Matrix<double>&, Matrix<double>*)>&& reduce) :
142         reduce_(reduce), session_(nullptr){};
143
144     /*!
145      * \brief Grant the caller an active handle for the currently executing block of code.
146      *
147      * Objects should not keep resource handles open for longer than a single block of code.
148      * calculate() and callback() functions get a handle to the resources for the current time step
149      * by calling getHandle().
150      *
151      * \note setSession() must be called before this function can be used.
152      * This clumsy protocol requires other infrastructure before it can be
153      * cleaned up for gmxapi 0.1
154      *
155      * \return resource handle
156      *
157      * In this release, the only facility provided by the resources is a function object for
158      * the ensemble averaging function provided by the Context.
159      */
160     [[nodiscard]] ResourcesHandle getHandle() const;
161
162     /*!
163      * \brief Acquires a pointer to a Session managing these resources.
164      *
165      * \param session non-owning pointer to Session resources.
166      */
167     void setSession(gmxapi::SessionResources* session);
168
169 private:
170     //! bound function object to provide ensemble reduce facility.
171     std::function<void(const Matrix<double>&, Matrix<double>*)> reduce_;
172
173     // Raw pointer to the session in which these resources live.
174     gmxapi::SessionResources* session_;
175 };
176
177 /*!
178  * \brief Template for MDModules from restraints.
179  *
180  * Allows a GROMACS module to be produced easily from the provided class. Refer to
181  * src/pythonmodule/export_plugin.cpp for how this template is used.
182  *
183  * \tparam R a class implementing the gmx::IRestraintPotential interface.
184  *
185  * The template type parameter is a class that defines a ``input_param_type`` member type
186  * and which implements a constructor with the signature
187  * ``R(std::vector<int>, const R::input_param_type&, std::shared_ptr<Resources>)``.
188  *
189  * \todo move this to a template header in gmxapi */
190 template<class R>
191 class RestraintModule : public gmxapi::MDModule // consider names
192 {
193 public:
194     using param_t = typename R::input_param_type;
195
196     /*!
197      * \brief Construct a named restraint module.
198      *
199      * Objects of this type are created during Session launch, so this code really doesn't belong
200      * here. The Director / Builder for the restraint uses a generic interface to pass standard
201      * parameters for pair restraints: a list of sites, a (custom) parameters structure, and
202      * resources provided by the Session.
203      *
204      * \param name
205      * \param sites
206      * \param params
207      * \param resources
208      */
209     RestraintModule(std::string                         name,
210                     std::vector<int>                    sites,
211                     const typename R::input_param_type& params,
212                     std::shared_ptr<Resources>          resources) :
213         sites_{ std::move(sites) },
214         params_{ params },
215         resources_{ std::move(resources) },
216         name_{ std::move(name) } {
217
218         };
219
220     ~RestraintModule() override = default;
221
222     /*!
223      * \brief Implement gmxapi::MDModule interface to get module name.
224      *
225      * name is provided during the building stage.
226      * \return
227      */
228     // \todo make member function const
229     [[nodiscard]] const char* name() const override { return name_.c_str(); }
230
231     /*!
232      * \brief Implement gmxapi::MDModule interface to create a restraint for libgromacs.
233      *
234      * \return (Possibly shared) Ownership of a restraint instance
235      *
236      * Creates the restraint instance if it does not already exist. Only creates one restraint
237      * instance in the lifetime of the RestraintModule.
238      *
239      * Note this interface is not stable but requires other GROMACS and gmxapi infrastructure
240      * to mature before it is clear whether we will be creating a new instance or sharing ownership
241      * of the object. A future version may use a std::unique_ptr.
242      */
243     std::shared_ptr<gmx::IRestraintPotential> getRestraint() override
244     {
245         std::lock_guard<std::mutex> lock(restraintInstantiation_);
246         if (!restraint_)
247         {
248             restraint_ = std::make_shared<R>(sites_, params_, resources_);
249         }
250         return restraint_;
251     }
252
253 private:
254     std::vector<int> sites_;
255     param_t          params_;
256
257     // Need to figure out if this is copyable or who owns it.
258     std::shared_ptr<Resources> resources_;
259
260     const std::string  name_;
261     std::shared_ptr<R> restraint_{ nullptr };
262     std::mutex         restraintInstantiation_;
263 };
264
265 /*!
266  * \brief Filehandle management helper class.
267  *
268  * Use the RAII pattern to make sure that a (newly) constructed object has an open filehandle and
269  * that a the filehandle for a destructed object is closed. Closing a file is not guaranteed to be
270  * error-free, so the programmer should explicitly call close() and check for errors (see the C
271  * library docs for fclose()).
272  *
273  * RAIIFile makes sure that fclose() is called exactly once, whether client code issues close()
274  * or not.
275  */
276 class RAIIFile
277 {
278 public:
279     /*!
280      * \brief Open a file in the chosen access mode.
281      *
282      * \param filename Name of file to be opened.
283      * \param mode access mode as described for the fopen C library call.
284      */
285     RAIIFile(const char* filename, const char* mode) : fh_{ fopen(filename, mode) } {}
286
287     /*!
288      * \brief Open a file for writing.
289      *
290      * \param filename Name of file to be opened.
291      *
292      * File is opened in mode "w", which truncates data if the file already exists.
293      * For other file access modes, use RAIIFile(const char* filename, const char* mode)
294      */
295     explicit RAIIFile(const char* filename) : RAIIFile(filename, "w") {}
296
297     /*!
298      * \brief Explicitly close the associated filehandle.
299      *
300      * It is good practice to explicitly close the file at a known point in the client code, though
301      * it is not strictly necessary. If the filehandle is still open when the RAIIFile object is
302      * destroyed, the fclose will be called then.
303      *
304      * Calling close() additional times on the same RAIIFile object is fine and has no effect in
305      * single-threaded code. However, the destructor and close() routines are not thread-safe, so
306      * the client code should make sure that close() is not called at the same time by multiple
307      * threads. Standard reference-counting constructs, like std::shared_ptr, can be used to make
308      * sure the object destructor is called exactly once if it needs to be shared.
309      *
310      * Refer to documentation on fclose() on checking for and interpreting `errno`.
311      */
312     void close()
313     {
314         if (fh_ != nullptr)
315         {
316             fclose(fh_);
317         }
318         fh_ = nullptr;
319     }
320
321     /*!
322      * \brief RAII destructor.
323      *
324      * Make sure the filehandle gets closed exactly once.
325      */
326     ~RAIIFile()
327     {
328         if (fh_ != nullptr)
329         {
330             fclose(fh_);
331         }
332     }
333
334     RAIIFile(const RAIIFile&) = delete;
335
336     RAIIFile& operator=(const RAIIFile&) = delete;
337
338     RAIIFile(RAIIFile&&) = default;
339
340     RAIIFile& operator=(RAIIFile&&) = default;
341
342     /*!
343      * \brief Get the managed filehandle.
344      *
345      * \return raw pointer to the underlying filehandle.
346      */
347     [[nodiscard]] FILE* fh() const noexcept { return fh_; }
348
349 private:
350     /// file handle
351     FILE* fh_{ nullptr };
352 };
353
354 } // end namespace plugin
355
356 #endif // RESTRAINT_SESSIONRESOURCES_H