Merge branch release-2021
[alexxy/gromacs.git] / src / gromacs / modularsimulator / signallers.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2019,2020,2021, 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 /*! \internal \file
36  * \brief Declares the signallers for the modular simulator
37  *
38  * \author Pascal Merz <pascal.merz@me.com>
39  * \ingroup module_modularsimulator
40  *
41  * This header is only used within the modular simulator module
42  */
43
44 #ifndef GMX_MODULARSIMULATOR_SIGNALLERS_H
45 #define GMX_MODULARSIMULATOR_SIGNALLERS_H
46
47 #include <vector>
48
49 #include "gromacs/compat/pointers.h"
50
51 #include "modularsimulatorinterfaces.h"
52
53 namespace gmx
54 {
55 class StopHandler;
56 class TrajectoryElement;
57 enum class StartingBehavior;
58
59 /*! \internal
60  * \ingroup module_modularsimulator
61  * \brief Builder for signallers
62  *
63  * This builder allows clients to register, and then builds the signaller
64  * passing on the list of clients.
65  *
66  * \tparam Signaller  The signaller to be built
67  */
68 template<typename Signaller>
69 class SignallerBuilder final
70 {
71 public:
72     //! Allows clients to register to the signaller
73     void registerSignallerClient(typename Signaller::Client* client);
74
75     //! Build the signaller
76     template<typename... Args>
77     std::unique_ptr<Signaller> build(Args&&... args);
78
79 private:
80     //! List of signaller clients
81     std::vector<typename Signaller::Client*> signallerClients_;
82     //! The state of the builder
83     ModularSimulatorBuilderState state_ = ModularSimulatorBuilderState::AcceptingClientRegistrations;
84
85     //! Helper function to get the callbacks from the clients
86     template<typename... Args>
87     std::vector<SignallerCallback> buildCallbackVector(Args&&... args);
88
89     /*! \brief Get a callback from a single client
90      *
91      * This is in a separate function, as the exact call depends on the
92      * specific signaller / client.
93      */
94     template<typename... Args>
95     std::optional<SignallerCallback> getSignallerCallback(typename Signaller::Client* client,
96                                                           Args&&... args);
97 };
98
99 /*! \internal
100  * \ingroup module_modularsimulator
101  * \brief Element signalling a neighbor search step
102  *
103  * This element informs its clients via callbacks
104  * when a neighbor-searching step is happening.
105  */
106 class NeighborSearchSignaller final : public ISignaller
107 {
108 public:
109     /*! \brief Run the signaller at a specific step / time
110      *
111      * Informs callbacks if step % nstlist_ == 0
112      *
113      * \param step  The current time step
114      * \param time  The current time
115      */
116     void signal(Step step, Time time) override;
117
118     //! Do nothing at setup time
119     void setup() override{};
120
121     //! Allow builder to construct
122     friend class SignallerBuilder<NeighborSearchSignaller>;
123     //! Define client type
124     typedef INeighborSearchSignallerClient Client;
125
126 private:
127     /*! \brief Constructor
128      *
129      * \param callbacks  A vector of pointers to callbacks
130      * \param nstlist    The frequency at which neighbor search is performed
131      * \param initStep   The first step of the simulation
132      * \param initTime   The start time of the simulation
133      */
134     NeighborSearchSignaller(std::vector<SignallerCallback> callbacks, Step nstlist, Step initStep, Time initTime);
135
136     //! Client callbacks
137     std::vector<SignallerCallback> callbacks_;
138
139     //! The NS frequency
140     const Step nstlist_;
141     //! The initial step of the simulation
142     const Step initStep_;
143     //! The initial time of the simulation
144     const Time initTime_;
145 };
146
147 /*! \internal
148  * \ingroup module_modularsimulator
149  * \brief Element signalling the last step
150  *
151  * This element informs its clients via callbacks
152  * when the last step is happening.
153  */
154 class LastStepSignaller final : public ISignaller, public INeighborSearchSignallerClient
155 {
156 public:
157     /*! \brief Run the signaller at a specific step / time
158      *
159      * Informs callbacks if this is the last step
160      *
161      * \param step  The current time step
162      * \param time  The current time
163      */
164     void signal(Step step, Time time) override;
165
166     //! Check that necessary registration was done
167     void setup() override;
168
169     //! Allow builder to construct
170     friend class SignallerBuilder<LastStepSignaller>;
171     //! Define client type
172     typedef ILastStepSignallerClient Client;
173
174 private:
175     /*! \brief Constructor
176      *
177      * \param callbacks    A vector of pointers to callbacks
178      * \param nsteps       The total number of steps for the simulation
179      * \param initStep     The first step of the simulation
180      * \param stopHandler  A pointer to the stop handler (LastStepSignaller takes ownership)
181      */
182     LastStepSignaller(std::vector<SignallerCallback> callbacks, Step nsteps, Step initStep, StopHandler* stopHandler);
183
184     //! Client callbacks
185     std::vector<SignallerCallback> callbacks_;
186
187     //! The last step of the simulation
188     const Step stopStep_;
189     //! Whether we signalled last step due to stop condition
190     bool signalledStopCondition_;
191     //! A pointer to the stop handler communicating signal and time-related stops
192     StopHandler* stopHandler_;
193
194     //! INeighborSearchSignallerClient implementation
195     std::optional<SignallerCallback> registerNSCallback() override;
196     //! The next NS step (notified by NS signaller)
197     Step nextNSStep_;
198     //! Whether we registered to the NS signaller
199     bool nsStepRegistrationDone_;
200 };
201
202 /*! \internal
203  * \ingroup module_modularsimulator
204  * \brief Element signalling a logging step
205  *
206  * This element informs its clients via callbacks
207  * when a logging step is happening.
208  */
209 class LoggingSignaller final : public ISignaller, public ILastStepSignallerClient
210 {
211 public:
212     /*! \brief Run the signaller at a specific step / time
213      *
214      * Informs callbacks if step % nstlog_ == 0
215      *
216      * \param step  The current time step
217      * \param time  The current time
218      */
219     void signal(Step step, Time time) override;
220
221     //! Check that necessary registration was done
222     void setup() override;
223
224     //! Allow builder to construct
225     friend class SignallerBuilder<LoggingSignaller>;
226     //! Define client type
227     typedef ILoggingSignallerClient Client;
228
229 private:
230     /*! \brief Constructor
231      *
232      * \param callbacks         A vector of pointers to callbacks
233      * \param nstlog            The logging frequency
234      * \param initStep          The first step of the simulation
235      * \param startingBehavior  Whether this is a new simulation or restarting from checkpoint
236      */
237     LoggingSignaller(std::vector<SignallerCallback> callbacks,
238                      Step                           nstlog,
239                      Step                           initStep,
240                      StartingBehavior               startingBehavior);
241
242     //! Client callbacks
243     std::vector<SignallerCallback> callbacks_;
244
245     //! The logging frequency
246     const Step nstlog_;
247     //! The initial step of the simulation
248     const Step initStep_;
249     //! How we are starting the simulation
250     const StartingBehavior startingBehavior_;
251
252     //! ILastStepSignallerClient implementation
253     std::optional<SignallerCallback> registerLastStepCallback() override;
254     //! The last step (notified by signaller)
255     Step lastStep_;
256     //! Whether we registered to the last step signaller
257     bool lastStepRegistrationDone_;
258 };
259
260 /*! \internal
261  * \ingroup module_modularsimulator
262  * \brief Element signalling trajectory writing
263  *
264  * During signalling phase, it checks whether the current step is a writing
265  * step for either the energy or the state (position, velocity, forces)
266  * trajectory. It then notifies the signaller clients of the upcoming step.
267  *
268  * The TrajectorySignaller works in close collaboration with the TrajectoryElement
269  * which does the actual trajectory writing during the simulation step.
270  */
271 class TrajectorySignaller final : public ISignaller, public ILastStepSignallerClient
272 {
273 public:
274     /*! \brief Prepare signaller
275      *
276      * Check that necessary registration was done
277      */
278     void setup() override;
279
280     /*! \brief Run the signaller at a specific step / time
281      *
282      * Informs clients when energy or state will be written.
283      *
284      * \param step           The current time step
285      * \param time           The current time
286      */
287     void signal(Step step, Time time) override;
288
289     //! Allow builder to construct
290     friend class SignallerBuilder<TrajectorySignaller>;
291     //! Define client type
292     typedef ITrajectorySignallerClient Client;
293
294 private:
295     //! Constructor
296     TrajectorySignaller(std::vector<SignallerCallback> signalEnergyCallbacks,
297                         std::vector<SignallerCallback> signalStateCallbacks,
298                         int                            nstxout,
299                         int                            nstvout,
300                         int                            nstfout,
301                         int                            nstxoutCompressed,
302                         int                            tngBoxOut,
303                         int                            tngLambdaOut,
304                         int                            tngBoxOutCompressed,
305                         int                            tngLambdaOutCompressed,
306                         int                            nstenergy);
307
308     //! Output frequencies
309     //! {
310     const int nstxout_;
311     const int nstvout_;
312     const int nstfout_;
313     const int nstxoutCompressed_;
314     const int tngBoxOut_;
315     const int tngLambdaOut_;
316     const int tngBoxOutCompressed_;
317     const int tngLambdaOutCompressed_;
318     const int nstenergy_;
319     //! }
320
321     //! Callbacks to signal events
322     //! {
323     std::vector<SignallerCallback> signalEnergyCallbacks_;
324     std::vector<SignallerCallback> signalStateCallbacks_;
325     //! }
326
327     /*
328      * Last step client
329      */
330     Step lastStep_;
331     bool lastStepRegistrationDone_;
332     //! ILastStepSignallerClient implementation
333     std::optional<SignallerCallback> registerLastStepCallback() override;
334 };
335
336 /*! \internal
337  * \ingroup module_modularsimulator
338  * \brief Element signalling energy related special steps
339  *
340  * This element informs its clients via callbacks
341  * of the following events:
342  *   - energy calculation step
343  *   - virial calculation step
344  *   - free energy calculation step
345  */
346 class EnergySignaller final : public ISignaller, public ITrajectorySignallerClient, public ILoggingSignallerClient
347 {
348 public:
349     /*! \brief Run the signaller at a specific step / time
350      *
351      * Informs callbacks of energy / virial / free energy special steps
352      *
353      * \param step  The current time step
354      * \param time  The current time
355      */
356     void signal(Step step, Time time) override;
357
358     //! Check that necessary registration was done
359     void setup() override;
360
361     //! Allow builder to construct
362     friend class SignallerBuilder<EnergySignaller>;
363     //! Define client type
364     typedef IEnergySignallerClient Client;
365
366 private:
367     /*! \brief Constructor
368      *
369      * \param calculateEnergyCallbacks      A vector of pointers to callbacks (energy steps)
370      * \param calculateVirialCallbacks      A vector of pointers to callbacks (virial steps)
371      * \param calculateFreeEnergyCallbacks  A vector of pointers to callbacks (free energy steps)
372      * \param nstcalcenergy                 The energy calculation frequency
373      * \param nstcalcfreeenergy             The free energy calculation frequency
374      * \param nstcalcvirial                 The free energy calculation frequency
375      */
376     EnergySignaller(std::vector<SignallerCallback> calculateEnergyCallbacks,
377                     std::vector<SignallerCallback> calculateVirialCallbacks,
378                     std::vector<SignallerCallback> calculateFreeEnergyCallbacks,
379                     int                            nstcalcenergy,
380                     int                            nstcalcfreeenergy,
381                     int                            nstcalcvirial);
382
383     //! Client callbacks
384     //! {
385     std::vector<SignallerCallback> calculateEnergyCallbacks_;
386     std::vector<SignallerCallback> calculateVirialCallbacks_;
387     std::vector<SignallerCallback> calculateFreeEnergyCallbacks_;
388     //! }
389
390     //! The energy calculation frequency
391     const int nstcalcenergy_;
392     //! The free energy calculation frequency
393     const int nstcalcfreeenergy_;
394     //! The virial calculation frequency
395     const int nstcalcvirial_;
396
397     //! ITrajectorySignallerClient implementation
398     std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
399     //! The energy writing step (notified by signaller)
400     Step energyWritingStep_;
401     //! Whether we registered to the trajectory signaller
402     bool trajectoryRegistrationDone_;
403
404     //! ILoggingSignallerClient implementation
405     std::optional<SignallerCallback> registerLoggingCallback() override;
406     //! The next logging step (notified by signaller)
407     Step loggingStep_;
408     //! Whether we registered to the logging signaller
409     bool loggingRegistrationDone_;
410 };
411
412 //! Allows clients to register to the signaller
413 template<class Signaller>
414 void SignallerBuilder<Signaller>::registerSignallerClient(typename Signaller::Client* client)
415 {
416     if (client)
417     {
418         if (state_ == ModularSimulatorBuilderState::NotAcceptingClientRegistrations)
419         {
420             throw SimulationAlgorithmSetupError(
421                     "Tried to register to signaller after it was built.");
422         }
423         signallerClients_.emplace_back(client);
424     }
425 }
426
427 /*! \brief Build the signaller
428  *
429  * General version - for NeighborSearchSignaller, LastStepSignaller, LoggingSignaller
430  */
431 template<class Signaller>
432 template<typename... Args>
433 std::unique_ptr<Signaller> SignallerBuilder<Signaller>::build(Args&&... args)
434 {
435     state_         = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
436     auto callbacks = buildCallbackVector();
437     // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
438     return std::unique_ptr<Signaller>(new Signaller(std::move(callbacks), std::forward<Args>(args)...));
439 }
440
441 /*! \brief Build the signaller
442  *
443  * Specialized version - TrajectorySignaller has a different build process
444  */
445 template<>
446 template<typename... Args>
447 std::unique_ptr<TrajectorySignaller> SignallerBuilder<TrajectorySignaller>::build(Args&&... args)
448 {
449     state_                     = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
450     auto signalEnergyCallbacks = buildCallbackVector(TrajectoryEvent::EnergyWritingStep);
451     auto signalStateCallbacks  = buildCallbackVector(TrajectoryEvent::StateWritingStep);
452     // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
453     return std::unique_ptr<TrajectorySignaller>(new TrajectorySignaller(
454             std::move(signalEnergyCallbacks), std::move(signalStateCallbacks), std::forward<Args>(args)...));
455 }
456
457 /*! \brief Build the signaller
458  *
459  * Specialized version - EnergySignaller has a significantly different build process
460  */
461 template<>
462 template<typename... Args>
463 std::unique_ptr<EnergySignaller> SignallerBuilder<EnergySignaller>::build(Args&&... args)
464 {
465     state_                        = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
466     auto calculateEnergyCallbacks = buildCallbackVector(EnergySignallerEvent::EnergyCalculationStep);
467     auto calculateVirialCallbacks = buildCallbackVector(EnergySignallerEvent::VirialCalculationStep);
468     auto calculateFreeEnergyCallbacks =
469             buildCallbackVector(EnergySignallerEvent::FreeEnergyCalculationStep);
470     // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
471     return std::unique_ptr<EnergySignaller>(new EnergySignaller(std::move(calculateEnergyCallbacks),
472                                                                 std::move(calculateVirialCallbacks),
473                                                                 std::move(calculateFreeEnergyCallbacks),
474                                                                 std::forward<Args>(args)...));
475 }
476
477 //! Helper function to get the callbacks from the clients
478 template<typename Signaller>
479 template<typename... Args>
480 std::vector<SignallerCallback> SignallerBuilder<Signaller>::buildCallbackVector(Args&&... args)
481 {
482     std::vector<SignallerCallback> callbacks;
483     // Allow clients to register their callbacks
484     for (auto& client : signallerClients_)
485     {
486         if (auto callback = getSignallerCallback(client, std::forward<Args>(args)...)) // don't register nullptr
487         {
488             callbacks.emplace_back(std::move(*callback));
489         }
490     }
491     return callbacks;
492 }
493
494 //! Get a callback from a single client - NeighborSearchSignaller
495 template<>
496 template<typename... Args>
497 std::optional<SignallerCallback> SignallerBuilder<NeighborSearchSignaller>::getSignallerCallback(
498         typename NeighborSearchSignaller::Client* client,
499         Args&&... args)
500 {
501     return client->registerNSCallback(std::forward<Args>(args)...);
502 }
503
504 //! Get a callback from a single client - LastStepSignaller
505 template<>
506 template<typename... Args>
507 std::optional<SignallerCallback>
508 SignallerBuilder<LastStepSignaller>::getSignallerCallback(typename LastStepSignaller::Client* client,
509                                                           Args&&... args)
510 {
511     return client->registerLastStepCallback(std::forward<Args>(args)...);
512 }
513
514 //! Get a callback from a single client - LoggingSignaller
515 template<>
516 template<typename... Args>
517 std::optional<SignallerCallback>
518 SignallerBuilder<LoggingSignaller>::getSignallerCallback(typename LoggingSignaller::Client* client,
519                                                          Args&&... args)
520 {
521     return client->registerLoggingCallback(std::forward<Args>(args)...);
522 }
523
524 //! Get a callback from a single client - TrajectorySignaller
525 template<>
526 template<typename... Args>
527 std::optional<SignallerCallback>
528 SignallerBuilder<TrajectorySignaller>::getSignallerCallback(typename TrajectorySignaller::Client* client,
529                                                             Args&&... args)
530 {
531     return client->registerTrajectorySignallerCallback(std::forward<Args>(args)...);
532 }
533
534 //! Get a callback from a single client - EnergySignaller
535 template<>
536 template<typename... Args>
537 std::optional<SignallerCallback>
538 SignallerBuilder<EnergySignaller>::getSignallerCallback(typename EnergySignaller::Client* client,
539                                                         Args&&... args)
540 {
541     return client->registerEnergyCallback(std::forward<Args>(args)...);
542 }
543
544 } // namespace gmx
545
546 #endif // GMX_MODULARSIMULATOR_SIGNALLERS_H