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"
65 class EnergySignaller;
66 class FreeEnergyPerturbationElement;
67 class LoggingSignaller;
68 class StatePropagatorData;
69 class NeighborSearchSignaller;
70 class PmeLoadBalanceHelper;
71 class TrajectoryElementBuilder;
74 * \ingroup module_modularsimulator
75 * \brief The modular simulator
77 * Based on the input given, this simulator builds independent elements and
78 * signallers and stores them in a respective vector. The run function
79 * runs the simulation by, in turn, building a task list from the elements
80 * for a predefined number of steps, then running the task list, and repeating
81 * until the stop criterion is fulfilled.
83 class ModularSimulator final : public ISimulator
89 //! Check for disabled functionality
90 static bool isInputCompatible(bool exitOnFailure,
91 const t_inputrec* inputrec,
93 const gmx_mtop_t& globalTopology,
94 const gmx_multisim_t* ms,
95 const ReplicaExchangeParameters& replExParams,
97 bool doEssentialDynamics,
100 // Only builder can construct
101 friend class SimulatorBuilder;
105 template<typename... Args>
106 explicit ModularSimulator(Args&&... args);
108 /*! \brief The initialisation
110 * This builds all signallers and elements, and is responsible to put
111 * them in the correct order.
113 void constructElementsAndSignallers();
115 /*! \brief A function called once before the simulation run
117 * This function collects calls which need to be made once at the
118 * beginning of the simulation run. These are mainly printing information
119 * to the user and starting the wall time.
121 void simulatorSetup();
123 /*! \brief A function called once after the simulation run
125 * This function collects calls which need to be made once at the
126 * end of the simulation run.
128 void simulatorTeardown();
130 /*! \brief A function called before every step
132 * This function collects calls which need to be made before every
133 * simulation step. The need for this function could likely be
134 * eliminated by rewriting the stop and reset handler to fit the
135 * modular simulator approach.
137 void preStep(Step step, Time time, bool isNeighborSearchingStep);
139 /*! \brief A function called after every step
141 * This function collects calls which need to be made after every
144 void postStep(Step step, Time time);
146 /*! \brief Build the integrator part of the simulator
148 * This includes the force calculation, state propagation, constraints,
149 * global computation, and the points during the process at which valid
150 * micro state / energy states are found. Currently, buildIntegrator
151 * knows about NVE md and md-vv algorithms.
153 std::unique_ptr<ISimulatorElement>
154 buildIntegrator(SignallerBuilder<NeighborSearchSignaller>* neighborSearchSignallerBuilder,
155 SignallerBuilder<EnergySignaller>* energySignallerBuilder,
156 SignallerBuilder<LoggingSignaller>* loggingSignallerBuilder,
157 SignallerBuilder<TrajectorySignaller>* trajectorySignallerBuilder,
158 std::vector<ICheckpointHelperClient*>* checkpointClients,
159 CheckBondedInteractionsCallbackPtr* checkBondedInteractionsCallback,
160 compat::not_null<StatePropagatorData*> statePropagatorDataPtr,
161 compat::not_null<EnergyElement*> energyElementPtr,
162 FreeEnergyPerturbationElement* freeEnergyPerturbationElementPtr,
163 bool hasReadEkinState);
165 //! Build the force element - can be normal forces or shell / flex constraints
166 std::unique_ptr<ISimulatorElement>
167 buildForces(SignallerBuilder<NeighborSearchSignaller>* neighborSearchSignallerBuilder,
168 SignallerBuilder<EnergySignaller>* energySignallerBuilder,
169 StatePropagatorData* statePropagatorDataPtr,
170 EnergyElement* energyElementPtr,
171 FreeEnergyPerturbationElement* freeEnergyPerturbationElement);
173 /*! \brief Add run functions to the task queue
175 * This function calls PME load balancing and domain decomposition first,
176 * and then queries the elements for all steps of the life time of the
177 * current neighbor list for their run functions. When the next step
178 * would be a neighbor-searching step, this function returns, such that
179 * the simulator can run the pre-computed task list before updating the
180 * neighbor list and re-filling the task list.
182 * TODO: In the current approach, unique pointers to tasks are created
183 * repeatedly. While preliminary measures do not seem to indicate
184 * performance problems, a pool allocator would be an obvious
185 * optimization possibility, as would reusing the task queue if
186 * the task queue is periodic around the rebuild point (i.e. the
187 * task queue is identical between rebuilds).
189 void populateTaskQueue();
191 //! Check for disabled functionality (during construction time)
192 void checkInputForDisabledFunctionality();
195 std::queue<SimulatorRunFunctionPtr> taskQueue_;
197 /* Note that the Simulator is owning the signallers and elements.
198 * The ownership list and the call list of the elements are kept
199 * separate, to allow to have elements more than once in the call
200 * lists - for example, using velocity verlet, the compute globals
201 * element needs to be scheduled more than once per step. For the
202 * signallers, no distinction between ownership and call list is
203 * made, all signallers are called exactly once per scheduling step.
205 * Objects being both a signaller and an element are not supported.
207 //! List of signalers (ownership and calling sequence)
208 std::vector<std::unique_ptr<ISignaller>> signallerList_;
209 //! List of schedulerElements (ownership)
210 std::vector<std::unique_ptr<ISimulatorElement>> elementsOwnershipList_;
211 //! List of schedulerElements (calling sequence)
212 std::vector<compat::not_null<ISimulatorElement*>> elementCallList_;
215 //! Helper function to add elements or signallers to the call list via raw pointer
216 template<typename T, typename U>
217 static void addToCallList(U* element, std::vector<compat::not_null<T*>>& callList);
218 //! Helper function to add elements or signallers to the call list via non-null raw pointer
219 template<typename T, typename U>
220 static void addToCallList(compat::not_null<U*> element, std::vector<compat::not_null<T*>>& callList);
221 //! Helper function to add elements or signallers to the call list via smart pointer
222 template<typename T, typename U>
223 static void addToCallList(std::unique_ptr<U>& element, std::vector<compat::not_null<T*>>& callList);
224 /*! \brief Helper function to add elements or signallers to the call list
225 * and move the ownership to the ownership list
227 template<typename T, typename U>
228 static void addToCallListAndMove(std::unique_ptr<U> element,
229 std::vector<compat::not_null<T*>>& callList,
230 std::vector<std::unique_ptr<T>>& elementList);
233 // Infrastructure elements
234 //! The domain decomposition element
235 std::unique_ptr<DomDecHelper> domDecHelper_;
236 //! The PME load balancing element
237 std::unique_ptr<PmeLoadBalanceHelper> pmeLoadBalanceHelper_;
238 //! The checkpoint helper
239 std::unique_ptr<CheckpointHelper> checkpointHelper_;
241 std::unique_ptr<StopHandler> stopHandler_;
242 //! The reset handler
243 std::unique_ptr<ResetHandler> resetHandler_;
244 //! Signal vector (used by stop / reset / checkpointing signaller)
245 SimulationSignals signals_;
246 //! Compute globals communication period
250 std::unique_ptr<TopologyHolder> topologyHolder_;
256 * \brief Signal helper
258 * The simulator needs to know about the last step and the
259 * neighbor searching step, which are determined in signallers.
260 * This object can be registered to the signals and accessed by
261 * the methods of the simulator.
263 class SignalHelper : public ILastStepSignallerClient, public INeighborSearchSignallerClient
267 Step lastStep_ = std::numeric_limits<Step>::max();
269 Step nextNSStep_ = -1;
270 //! ILastStepSignallerClient implementation
271 SignallerCallbackPtr registerLastStepCallback() override;
272 //! INeighborSearchSignallerClient implementation
273 SignallerCallbackPtr registerNSCallback() override;
275 //! The signal helper object
276 std::unique_ptr<SignalHelper> signalHelper_;
278 // TODO: This is a hack for stop handler - needs to go once StopHandler
279 // is adapted to the modular simulator
280 //! Whether this is a neighbor-searching step
281 bool stophandlerIsNSStep_ = false;
283 Step stophandlerCurrentStep_ = -1;
286 //! Constructor implementation (here to avoid template-related linker problems)
287 template<typename... Args>
288 ModularSimulator::ModularSimulator(Args&&... args) : ISimulator(std::forward<Args>(args)...)
290 nstglobalcomm_ = computeGlobalCommunicationPeriod(mdlog, inputrec, cr);
291 signalHelper_ = std::make_unique<SignalHelper>();
292 checkInputForDisabledFunctionality();
296 template<typename T, typename U>
297 void ModularSimulator::addToCallList(U* element, std::vector<compat::not_null<T*>>& callList)
301 callList.emplace_back(element);
305 template<typename T, typename U>
306 void ModularSimulator::addToCallList(gmx::compat::not_null<U*> element,
307 std::vector<compat::not_null<T*>>& callList)
309 callList.emplace_back(element);
312 template<typename T, typename U>
313 void ModularSimulator::addToCallList(std::unique_ptr<U>& element, std::vector<compat::not_null<T*>>& callList)
317 callList.emplace_back(compat::make_not_null(element.get()));
321 template<typename T, typename U>
322 void ModularSimulator::addToCallListAndMove(std::unique_ptr<U> element,
323 std::vector<compat::not_null<T*>>& callList,
324 std::vector<std::unique_ptr<T>>& elementList)
328 callList.emplace_back(compat::make_not_null(element.get()));
329 elementList.emplace_back(std::move(element));
335 * \brief Whether or not to use the ModularSimulator
337 * GMX_DISABLE_MODULAR_SIMULATOR environment variable allows to disable modular simulator for
340 * See ModularSimulator::isInputCompatible() for function signature.
342 * \ingroup module_modularsimulator
344 template<typename... Ts>
345 auto checkUseModularSimulator(Ts&&... args)
346 -> decltype(ModularSimulator::isInputCompatible(std::forward<Ts>(args)...))
348 return ModularSimulator::isInputCompatible(std::forward<Ts>(args)...)
349 && getenv("GMX_DISABLE_MODULAR_SIMULATOR") == nullptr;
354 #endif // GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H