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