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