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 Provides the modular simulator.
38 * Defines the ModularSimulator class. Provides checkUseModularSimulator() utility function
39 * to determine whether the ModularSimulator should be used.
41 * \author Pascal Merz <pascal.merz@me.com>
42 * \ingroup module_modularsimulator
44 #ifndef GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H
45 #define GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H
49 #include "gromacs/mdlib/md_support.h"
50 #include "gromacs/mdlib/resethandler.h"
51 #include "gromacs/mdrun/isimulator.h"
53 #include "checkpointhelper.h"
54 #include "domdechelper.h"
55 #include "modularsimulatorinterfaces.h"
56 #include "pmeloadbalancehelper.h"
57 #include "topologyholder.h"
63 class EnergySignaller;
64 class FreeEnergyPerturbationElement;
65 class LoggingSignaller;
66 class StatePropagatorData;
67 class NeighborSearchSignaller;
68 class PmeLoadBalanceHelper;
69 class TrajectoryElementBuilder;
72 * \ingroup module_modularsimulator
73 * \brief The modular simulator
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.
81 class ModularSimulator final : public ISimulator
87 //! Check for disabled functionality
88 static bool isInputCompatible(bool exitOnFailure,
89 const t_inputrec* inputrec,
91 const gmx_mtop_t& globalTopology,
92 const gmx_multisim_t* ms,
93 const ReplicaExchangeParameters& replExParams,
95 bool doEssentialDynamics,
98 // Only builder can construct
99 friend class SimulatorBuilder;
103 template<typename... Args>
104 explicit ModularSimulator(Args&&... args);
106 /*! \brief The initialisation
108 * This builds all signallers and elements, and is responsible to put
109 * them in the correct order.
111 void constructElementsAndSignallers();
113 /*! \brief A function called once before the simulation run
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.
119 void simulatorSetup();
121 /*! \brief A function called once after the simulation run
123 * This function collects calls which need to be made once at the
124 * end of the simulation run.
126 void simulatorTeardown();
128 /*! \brief A function called before every step
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.
135 void preStep(Step step, Time time, bool isNeighborSearchingStep);
137 /*! \brief A function called after every step
139 * This function collects calls which need to be made after every
142 void postStep(Step step, Time time);
144 /*! \brief Build the integrator part of the simulator
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.
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);
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);
171 /*! \brief Add run functions to the task queue
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.
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).
187 void populateTaskQueue();
189 //! Check for disabled functionality (during construction time)
190 void checkInputForDisabledFunctionality();
193 std::queue<SimulatorRunFunctionPtr> taskQueue_;
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.
203 * Objects being both a signaller and an element are not supported.
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_;
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
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);
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_;
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
248 std::unique_ptr<TopologyHolder> topologyHolder_;
254 * \brief Signal helper
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.
261 class SignalHelper : public ILastStepSignallerClient, public INeighborSearchSignallerClient
265 Step lastStep_ = std::numeric_limits<Step>::max();
267 Step nextNSStep_ = -1;
268 //! ILastStepSignallerClient implementation
269 SignallerCallbackPtr registerLastStepCallback() override;
270 //! INeighborSearchSignallerClient implementation
271 SignallerCallbackPtr registerNSCallback() override;
273 //! The signal helper object
274 std::unique_ptr<SignalHelper> signalHelper_;
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;
281 Step stophandlerCurrentStep_ = -1;
284 //! Constructor implementation (here to avoid template-related linker problems)
285 template<typename... Args>
286 ModularSimulator::ModularSimulator(Args&&... args) : ISimulator(std::forward<Args>(args)...)
288 nstglobalcomm_ = computeGlobalCommunicationPeriod(mdlog, inputrec, cr);
289 signalHelper_ = std::make_unique<SignalHelper>();
290 checkInputForDisabledFunctionality();
294 template<typename T, typename U>
295 void ModularSimulator::addToCallList(U* element, std::vector<compat::not_null<T*>>& callList)
299 callList.emplace_back(element);
303 template<typename T, typename U>
304 void ModularSimulator::addToCallList(gmx::compat::not_null<U*> element,
305 std::vector<compat::not_null<T*>>& callList)
307 callList.emplace_back(element);
310 template<typename T, typename U>
311 void ModularSimulator::addToCallList(std::unique_ptr<U>& element, std::vector<compat::not_null<T*>>& callList)
315 callList.emplace_back(compat::make_not_null(element.get()));
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)
326 callList.emplace_back(compat::make_not_null(element.get()));
327 elementList.emplace_back(std::move(element));
333 * \brief Whether or not to use the ModularSimulator
335 * GMX_DISABLE_MODULAR_SIMULATOR environment variable allows to disable modular simulator for
338 * See ModularSimulator::isInputCompatible() for function signature.
340 * \ingroup module_modularsimulator
342 template<typename... Ts>
343 auto checkUseModularSimulator(Ts&&... args)
344 -> decltype(ModularSimulator::isInputCompatible(std::forward<Ts>(args)...))
346 return ModularSimulator::isInputCompatible(std::forward<Ts>(args)...)
347 && getenv("GMX_DISABLE_MODULAR_SIMULATOR") == nullptr;
352 #endif // GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H