f5f6bb4e99a3769c13238f766992adb6d4b07c03
[alexxy/gromacs.git] / src / gromacs / modularsimulator / modularsimulator.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2019,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 modular simulator.
37  *
38  * Defines the ModularSimulator class. Provides checkUseModularSimulator() utility function
39  * to determine whether the ModularSimulator should be used.
40  *
41  * \author Pascal Merz <pascal.merz@me.com>
42  * \ingroup module_modularsimulator
43  */
44 #ifndef GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H
45 #define GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H
46
47 #include <queue>
48
49 #include "gromacs/mdlib/md_support.h"
50 #include "gromacs/mdlib/resethandler.h"
51 #include "gromacs/mdrun/isimulator.h"
52
53 #include "checkpointhelper.h"
54 #include "domdechelper.h"
55 #include "modularsimulatorinterfaces.h"
56 #include "pmeloadbalancehelper.h"
57 #include "topologyholder.h"
58
59 namespace gmx
60 {
61 class DomDecHelper;
62 class EnergyElement;
63 class EnergySignaller;
64 class FreeEnergyPerturbationElement;
65 class LoggingSignaller;
66 class StatePropagatorData;
67 class NeighborSearchSignaller;
68 class PmeLoadBalanceHelper;
69 class TrajectoryElementBuilder;
70
71 /*! \libinternal
72  * \ingroup module_modularsimulator
73  * \brief The modular simulator
74  *
75  * Based on the input given, this simulator builds independent elements and
76  * signallers and stores them in a respective vector. The run function
77  * runs the simulation by, in turn, building a task list from the elements
78  * for a predefined number of steps, then running the task list, and repeating
79  * until the stop criterion is fulfilled.
80  */
81 class ModularSimulator final : public ISimulator
82 {
83 public:
84     //! Run the simulator
85     void run() override;
86
87     //! Check for disabled functionality
88     static bool isInputCompatible(bool                             exitOnFailure,
89                                   const t_inputrec*                inputrec,
90                                   bool                             doRerun,
91                                   const gmx_mtop_t&                globalTopology,
92                                   const gmx_multisim_t*            ms,
93                                   const ReplicaExchangeParameters& replExParams,
94                                   const t_fcdata*                  fcd,
95                                   bool                             doEssentialDynamics,
96                                   bool                             doMembed);
97
98     // Only builder can construct
99     friend class SimulatorBuilder;
100
101 private:
102     //! Constructor
103     template<typename... Args>
104     explicit ModularSimulator(Args&&... args);
105
106     /*! \brief The initialisation
107      *
108      * This builds all signallers and elements, and is responsible to put
109      * them in the correct order.
110      */
111     void constructElementsAndSignallers();
112
113     /*! \brief A function called once before the simulation run
114      *
115      * This function collects calls which need to be made once at the
116      * beginning of the simulation run. These are mainly printing information
117      * to the user and starting the wall time.
118      */
119     void simulatorSetup();
120
121     /*! \brief A function called once after the simulation run
122      *
123      * This function collects calls which need to be made once at the
124      * end of the simulation run.
125      */
126     void simulatorTeardown();
127
128     /*! \brief A function called before every step
129      *
130      * This function collects calls which need to be made before every
131      * simulation step. The need for this function could likely be
132      * eliminated by rewriting the stop and reset handler to fit the
133      * modular simulator approach.
134      */
135     void preStep(Step step, Time time, bool isNeighborSearchingStep);
136
137     /*! \brief A function called after every step
138      *
139      * This function collects calls which need to be made after every
140      * simulation step.
141      */
142     void postStep(Step step, Time time);
143
144     /*! \brief Build the integrator part of the simulator
145      *
146      * This includes the force calculation, state propagation, constraints,
147      * global computation, and the points during the process at which valid
148      * micro state / energy states are found. Currently, buildIntegrator
149      * knows about NVE md and md-vv algorithms.
150      */
151     std::unique_ptr<ISimulatorElement>
152     buildIntegrator(SignallerBuilder<NeighborSearchSignaller>* neighborSearchSignallerBuilder,
153                     SignallerBuilder<EnergySignaller>*         energySignallerBuilder,
154                     SignallerBuilder<LoggingSignaller>*        loggingSignallerBuilder,
155                     SignallerBuilder<TrajectorySignaller>*     trajectorySignallerBuilder,
156                     std::vector<ICheckpointHelperClient*>*     checkpointClients,
157                     CheckBondedInteractionsCallbackPtr*        checkBondedInteractionsCallback,
158                     compat::not_null<StatePropagatorData*>     statePropagatorDataPtr,
159                     compat::not_null<EnergyElement*>           energyElementPtr,
160                     FreeEnergyPerturbationElement*             freeEnergyPerturbationElementPtr,
161                     bool                                       hasReadEkinState);
162
163     //! Build the force element - can be normal forces or shell / flex constraints
164     std::unique_ptr<ISimulatorElement>
165     buildForces(SignallerBuilder<NeighborSearchSignaller>* neighborSearchSignallerBuilder,
166                 SignallerBuilder<EnergySignaller>*         energySignallerBuilder,
167                 StatePropagatorData*                       statePropagatorDataPtr,
168                 EnergyElement*                             energyElementPtr,
169                 FreeEnergyPerturbationElement*             freeEnergyPerturbationElement);
170
171     /*! \brief Add run functions to the task queue
172      *
173      * This function calls PME load balancing and domain decomposition first,
174      * and then queries the elements for all steps of the life time of the
175      * current neighbor list for their run functions. When the next step
176      * would be a neighbor-searching step, this function returns, such that
177      * the simulator can run the pre-computed task list before updating the
178      * neighbor list and re-filling the task list.
179      *
180      * TODO: In the current approach, unique pointers to tasks are created
181      *       repeatedly. While preliminary measures do not seem to indicate
182      *       performance problems, a pool allocator would be an obvious
183      *       optimization possibility, as would reusing the task queue if
184      *       the task queue is periodic around the rebuild point (i.e. the
185      *       task queue is identical between rebuilds).
186      */
187     void populateTaskQueue();
188
189     //! Check for disabled functionality (during construction time)
190     void checkInputForDisabledFunctionality();
191
192     //! The run queue
193     std::queue<SimulatorRunFunctionPtr> taskQueue_;
194
195     /* Note that the Simulator is owning the signallers and elements.
196      * The ownership list and the call list of the elements are kept
197      * separate, to allow to have elements more than once in the call
198      * lists - for example, using velocity verlet, the compute globals
199      * element needs to be scheduled more than once per step. For the
200      * signallers, no distinction between ownership and call list is
201      * made, all signallers are called exactly once per scheduling step.
202      *
203      * Objects being both a signaller and an element are not supported.
204      */
205     //! List of signalers (ownership and calling sequence)
206     std::vector<std::unique_ptr<ISignaller>> signallerList_;
207     //! List of schedulerElements (ownership)
208     std::vector<std::unique_ptr<ISimulatorElement>> elementsOwnershipList_;
209     //! List of schedulerElements (calling sequence)
210     std::vector<compat::not_null<ISimulatorElement*>> elementCallList_;
211
212     //! \cond
213     //! Helper function to add elements or signallers to the call list via raw pointer
214     template<typename T, typename U>
215     static void addToCallList(U* element, std::vector<compat::not_null<T*>>& callList);
216     //! Helper function to add elements or signallers to the call list via non-null raw pointer
217     template<typename T, typename U>
218     static void addToCallList(compat::not_null<U*> element, std::vector<compat::not_null<T*>>& callList);
219     //! Helper function to add elements or signallers to the call list via smart pointer
220     template<typename T, typename U>
221     static void addToCallList(std::unique_ptr<U>& element, std::vector<compat::not_null<T*>>& callList);
222     /*! \brief Helper function to add elements or signallers to the call list
223      *         and move the ownership to the ownership list
224      */
225     template<typename T, typename U>
226     static void addToCallListAndMove(std::unique_ptr<U>                 element,
227                                      std::vector<compat::not_null<T*>>& callList,
228                                      std::vector<std::unique_ptr<T>>&   elementList);
229     //! \endcond
230
231     // Infrastructure elements
232     //! The domain decomposition element
233     std::unique_ptr<DomDecHelper> domDecHelper_;
234     //! The PME load balancing element
235     std::unique_ptr<PmeLoadBalanceHelper> pmeLoadBalanceHelper_;
236     //! The checkpoint helper
237     std::unique_ptr<CheckpointHelper> checkpointHelper_;
238     //! The stop handler
239     std::unique_ptr<StopHandler> stopHandler_;
240     //! The reset handler
241     std::unique_ptr<ResetHandler> resetHandler_;
242     //! Signal vector (used by stop / reset / checkpointing signaller)
243     SimulationSignals signals_;
244     //! Compute globals communication period
245     int nstglobalcomm_;
246
247     //! The topology
248     std::unique_ptr<TopologyHolder> topologyHolder_;
249
250     //! The current step
251     Step step_ = -1;
252
253     /*! \internal
254      * \brief Signal helper
255      *
256      * The simulator needs to know about the last step and the
257      * neighbor searching step, which are determined in signallers.
258      * This object can be registered to the signals and accessed by
259      * the methods of the simulator.
260      */
261     class SignalHelper : public ILastStepSignallerClient, public INeighborSearchSignallerClient
262     {
263     public:
264         //! The last step
265         Step lastStep_ = std::numeric_limits<Step>::max();
266         //! The next NS step
267         Step nextNSStep_ = -1;
268         //! ILastStepSignallerClient implementation
269         SignallerCallbackPtr registerLastStepCallback() override;
270         //! INeighborSearchSignallerClient implementation
271         SignallerCallbackPtr registerNSCallback() override;
272     };
273     //! The signal helper object
274     std::unique_ptr<SignalHelper> signalHelper_;
275
276     // TODO: This is a hack for stop handler - needs to go once StopHandler
277     //       is adapted to the modular simulator
278     //! Whether this is a neighbor-searching step
279     bool stophandlerIsNSStep_ = false;
280     //! The current step
281     Step stophandlerCurrentStep_ = -1;
282 };
283
284 //! Constructor implementation (here to avoid template-related linker problems)
285 template<typename... Args>
286 ModularSimulator::ModularSimulator(Args&&... args) : ISimulator(std::forward<Args>(args)...)
287 {
288     nstglobalcomm_ = computeGlobalCommunicationPeriod(mdlog, inputrec, cr);
289     signalHelper_  = std::make_unique<SignalHelper>();
290     checkInputForDisabledFunctionality();
291 }
292
293 //! \cond
294 template<typename T, typename U>
295 void ModularSimulator::addToCallList(U* element, std::vector<compat::not_null<T*>>& callList)
296 {
297     if (element)
298     {
299         callList.emplace_back(element);
300     }
301 }
302
303 template<typename T, typename U>
304 void ModularSimulator::addToCallList(gmx::compat::not_null<U*>          element,
305                                      std::vector<compat::not_null<T*>>& callList)
306 {
307     callList.emplace_back(element);
308 }
309
310 template<typename T, typename U>
311 void ModularSimulator::addToCallList(std::unique_ptr<U>& element, std::vector<compat::not_null<T*>>& callList)
312 {
313     if (element)
314     {
315         callList.emplace_back(compat::make_not_null(element.get()));
316     }
317 }
318
319 template<typename T, typename U>
320 void ModularSimulator::addToCallListAndMove(std::unique_ptr<U>                 element,
321                                             std::vector<compat::not_null<T*>>& callList,
322                                             std::vector<std::unique_ptr<T>>&   elementList)
323 {
324     if (element)
325     {
326         callList.emplace_back(compat::make_not_null(element.get()));
327         elementList.emplace_back(std::move(element));
328     }
329 }
330 //! \endcond
331
332 /*!
333  * \brief Whether or not to use the ModularSimulator
334  *
335  * GMX_DISABLE_MODULAR_SIMULATOR environment variable allows to disable modular simulator for
336  * all uses.
337  *
338  * See ModularSimulator::isInputCompatible() for function signature.
339  *
340  * \ingroup module_modularsimulator
341  */
342 template<typename... Ts>
343 auto checkUseModularSimulator(Ts&&... args)
344         -> decltype(ModularSimulator::isInputCompatible(std::forward<Ts>(args)...))
345 {
346     return ModularSimulator::isInputCompatible(std::forward<Ts>(args)...)
347            && getenv("GMX_DISABLE_MODULAR_SIMULATOR") == nullptr;
348 }
349
350 } // namespace gmx
351
352 #endif // GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H