2 * This file is part of the GROMACS molecular simulation package.
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.
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.
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.
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.
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.
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.
35 /*! \libinternal \file
36 * \brief Declares the modular simulator
38 * \author Pascal Merz <pascal.merz@me.com>
39 * \ingroup module_modularsimulator
41 #ifndef GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H
42 #define GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H
46 #include "gromacs/mdlib/md_support.h"
47 #include "gromacs/mdlib/resethandler.h"
48 #include "gromacs/mdrun/isimulator.h"
50 #include "checkpointhelper.h"
51 #include "domdechelper.h"
52 #include "modularsimulatorinterfaces.h"
53 #include "pmeloadbalancehelper.h"
54 #include "topologyholder.h"
60 class EnergySignaller;
61 class FreeEnergyPerturbationElement;
62 class LoggingSignaller;
63 class StatePropagatorData;
64 class NeighborSearchSignaller;
65 class PmeLoadBalanceHelper;
66 class TrajectoryElementBuilder;
69 * \ingroup module_modularsimulator
70 * \brief The modular simulator
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.
78 class ModularSimulator final : public ISimulator
84 //! Check for disabled functionality
85 static bool isInputCompatible(bool exitOnFailure,
86 const t_inputrec* inputrec,
88 const gmx_mtop_t& globalTopology,
89 const gmx_multisim_t* ms,
90 const ReplicaExchangeParameters& replExParams,
92 bool doEssentialDynamics,
95 // Only builder can construct
96 friend class SimulatorBuilder;
100 template<typename... Args>
101 explicit ModularSimulator(Args&&... args);
103 /*! \brief The initialisation
105 * This builds all signallers and elements, and is responsible to put
106 * them in the correct order.
108 void constructElementsAndSignallers();
110 /*! \brief A function called once before the simulation run
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.
116 void simulatorSetup();
118 /*! \brief A function called once after the simulation run
120 * This function collects calls which need to be made once at the
121 * end of the simulation run.
123 void simulatorTeardown();
125 /*! \brief A function called before every step
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.
132 void preStep(Step step, Time time, bool isNeighborSearchingStep);
134 /*! \brief A function called after every step
136 * This function collects calls which need to be made after every
139 void postStep(Step step, Time time);
141 /*! \brief Build the integrator part of the simulator
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.
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);
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);
168 /*! \brief Add run functions to the task queue
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.
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).
184 void populateTaskQueue();
186 //! Check for disabled functionality (during construction time)
187 void checkInputForDisabledFunctionality();
190 std::queue<SimulatorRunFunctionPtr> taskQueue_;
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
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_.
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_;
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
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);
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_;
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
255 std::unique_ptr<TopologyHolder> topologyHolder_;
261 * \brief Signal helper
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.
268 class SignalHelper : public ILastStepSignallerClient, public INeighborSearchSignallerClient
272 Step lastStep_ = std::numeric_limits<Step>::max();
274 Step nextNSStep_ = -1;
275 //! ILastStepSignallerClient implementation
276 SignallerCallbackPtr registerLastStepCallback() override;
277 //! INeighborSearchSignallerClient implementation
278 SignallerCallbackPtr registerNSCallback() override;
280 //! The signal helper object
281 std::unique_ptr<SignalHelper> signalHelper_;
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;
288 Step stophandlerCurrentStep_ = -1;
291 //! Constructor implementation (here to avoid template-related linker problems)
292 template<typename... Args>
293 ModularSimulator::ModularSimulator(Args&&... args) : ISimulator(std::forward<Args>(args)...)
295 nstglobalcomm_ = computeGlobalCommunicationPeriod(mdlog, inputrec, cr);
296 signalHelper_ = std::make_unique<SignalHelper>();
297 checkInputForDisabledFunctionality();
301 template<typename T, typename U>
302 void ModularSimulator::addToCallList(U* element, std::vector<compat::not_null<T*>>& callList)
306 callList.emplace_back(element);
310 template<typename T, typename U>
311 void ModularSimulator::addToCallList(gmx::compat::not_null<U*> element,
312 std::vector<compat::not_null<T*>>& callList)
314 callList.emplace_back(element);
317 template<typename T, typename U>
318 void ModularSimulator::addToCallList(std::unique_ptr<U>& element, std::vector<compat::not_null<T*>>& callList)
322 callList.emplace_back(compat::make_not_null(element.get()));
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)
333 callList.emplace_back(compat::make_not_null(element.get()));
334 elementList.emplace_back(std::move(element));
341 #endif // GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H