2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2020,2021, 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.
36 * \brief Defines the modular simulator algorithm
38 * \author Pascal Merz <pascal.merz@me.com>
39 * \ingroup module_modularsimulator
44 #include "simulatoralgorithm.h"
46 #include "gromacs/commandline/filenm.h"
47 #include "gromacs/domdec/domdec.h"
48 #include "gromacs/ewald/pme.h"
49 #include "gromacs/ewald/pme_load_balancing.h"
50 #include "gromacs/ewald/pme_pp.h"
51 #include "gromacs/gmxlib/nrnb.h"
52 #include "gromacs/listed_forces/listed_forces.h"
53 #include "gromacs/mdlib/checkpointhandler.h"
54 #include "gromacs/mdlib/constr.h"
55 #include "gromacs/mdlib/energyoutput.h"
56 #include "gromacs/mdlib/md_support.h"
57 #include "gromacs/mdlib/mdatoms.h"
58 #include "gromacs/mdlib/resethandler.h"
59 #include "gromacs/mdlib/stat.h"
60 #include "gromacs/mdrun/replicaexchange.h"
61 #include "gromacs/mdrun/shellfc.h"
62 #include "gromacs/mdrunutility/handlerestart.h"
63 #include "gromacs/mdrunutility/printtime.h"
64 #include "gromacs/mdtypes/commrec.h"
65 #include "gromacs/mdtypes/fcdata.h"
66 #include "gromacs/mdtypes/forcerec.h"
67 #include "gromacs/mdtypes/inputrec.h"
68 #include "gromacs/mdtypes/mdatom.h"
69 #include "gromacs/mdtypes/mdrunoptions.h"
70 #include "gromacs/mdtypes/observableshistory.h"
71 #include "gromacs/nbnxm/nbnxm.h"
72 #include "gromacs/timing/walltime_accounting.h"
73 #include "gromacs/topology/topology.h"
74 #include "gromacs/utility/cstringutil.h"
75 #include "gromacs/utility/fatalerror.h"
77 #include "checkpointhelper.h"
78 #include "domdechelper.h"
79 #include "energydata.h"
80 #include "freeenergyperturbationdata.h"
81 #include "modularsimulator.h"
82 #include "parrinellorahmanbarostat.h"
83 #include "pmeloadbalancehelper.h"
84 #include "propagator.h"
85 #include "statepropagatordata.h"
86 #include "velocityscalingtemperaturecoupling.h"
90 ModularSimulatorAlgorithm::ModularSimulatorAlgorithm(std::string topologyName,
93 const MDLogger& mdlog,
94 const MdrunOptions& mdrunOptions,
95 const t_inputrec* inputrec,
97 gmx_wallcycle* wcycle,
99 gmx_walltime_accounting* walltime_accounting) :
100 taskIterator_(taskQueue_.end()),
101 statePropagatorData_(nullptr),
102 energyData_(nullptr),
103 freeEnergyPerturbationData_(nullptr),
106 topologyName_(std::move(topologyName)),
110 mdrunOptions(mdrunOptions),
115 walltime_accounting(walltime_accounting)
117 signalHelper_ = std::make_unique<SignalHelper>();
120 void ModularSimulatorAlgorithm::setup()
123 for (auto& signaller : signallerList_)
129 domDecHelper_->setup();
132 for (auto& element : elementsOwnershipList_)
134 element->elementSetup();
136 statePropagatorData_->setup();
137 if (pmeLoadBalanceHelper_)
139 // State must have been initialized so pmeLoadBalanceHelper_ gets a valid box
140 pmeLoadBalanceHelper_->setup();
144 const SimulatorRunFunction* ModularSimulatorAlgorithm::getNextTask()
146 if (!taskQueue_.empty())
150 if (taskIterator_ == taskQueue_.end())
157 taskIterator_ = taskQueue_.begin();
159 return &*taskIterator_;
162 void ModularSimulatorAlgorithm::updateTaskQueue()
164 // For now, we'll just clean the task queue and then re-populate
165 // TODO: If tasks are periodic around updates of the task queue,
166 // we should reuse it instead
171 void ModularSimulatorAlgorithm::teardown()
173 for (auto& element : elementsOwnershipList_)
175 element->elementTeardown();
177 energyData_->teardown();
178 if (pmeLoadBalanceHelper_)
180 pmeLoadBalanceHelper_->teardown();
185 void ModularSimulatorAlgorithm::simulatorSetup()
187 if (!mdrunOptions.writeConfout)
189 // This is on by default, and the main known use case for
190 // turning it off is for convenience in benchmarking, which is
191 // something that should not show up in the general user
196 "The -noconfout functionality is deprecated, and "
197 "may be removed in a future version.");
202 char sbuf[STEPSTRSIZE], sbuf2[STEPSTRSIZE];
203 std::string timeString;
204 fprintf(stderr, "starting mdrun '%s'\n", topologyName_.c_str());
205 if (inputrec->nsteps >= 0)
207 timeString = formatString(
208 "%8.1f", static_cast<double>(inputrec->init_step + inputrec->nsteps) * inputrec->delta_t);
212 timeString = "infinite";
214 if (inputrec->init_step > 0)
217 "%s steps, %s ps (continuing from step %s, %8.1f ps).\n",
218 gmx_step_str(inputrec->init_step + inputrec->nsteps, sbuf),
220 gmx_step_str(inputrec->init_step, sbuf2),
221 inputrec->init_step * inputrec->delta_t);
225 fprintf(stderr, "%s steps, %s ps.\n", gmx_step_str(inputrec->nsteps, sbuf), timeString.c_str());
227 fprintf(fplog, "\n");
230 walltime_accounting_start_time(walltime_accounting);
231 wallcycle_start(wcycle, ewcRUN);
232 print_start(fplog, cr, walltime_accounting, "mdrun");
234 step_ = inputrec->init_step;
237 void ModularSimulatorAlgorithm::simulatorTeardown()
240 // Stop measuring walltime
241 walltime_accounting_end_time(walltime_accounting);
243 if (!thisRankHasDuty(cr, DUTY_PME))
245 /* Tell the PME only node to finish */
246 gmx_pme_send_finish(cr);
249 walltime_accounting_set_nsteps_done(walltime_accounting, step_ - inputrec->init_step);
252 void ModularSimulatorAlgorithm::preStep(Step step, Time gmx_unused time, bool isNeighborSearchingStep)
254 if (stopHandler_->stoppingAfterCurrentStep(isNeighborSearchingStep) && step != signalHelper_->lastStep_)
257 * Stop handler wants to stop after the current step, which was
258 * not known when building the current task queue. This happens
259 * e.g. when a stop is signalled by OS. We therefore want to purge
260 * the task queue now, and re-schedule this step as last step.
269 resetHandler_->setSignal(walltime_accounting);
270 // This is a hack to avoid having to rewrite StopHandler to be a NeighborSearchSignaller
271 // and accept the step as input. Eventually, we want to do that, but currently this would
272 // require introducing NeighborSearchSignaller in the legacy do_md or a lot of code
274 stophandlerIsNSStep_ = isNeighborSearchingStep;
275 stophandlerCurrentStep_ = step;
276 stopHandler_->setSignal();
278 wallcycle_start(wcycle, ewcSTEP);
281 void ModularSimulatorAlgorithm::postStep(Step step, Time gmx_unused time)
286 if (do_per_step(step, inputrec->nstlog))
288 if (fflush(fplog) != 0)
290 gmx_fatal(FARGS, "Cannot flush logfile - maybe you are out of disk space?");
294 const bool do_verbose = mdrunOptions.verbose
295 && (step % mdrunOptions.verboseStepPrintInterval == 0
296 || step == inputrec->init_step || step == signalHelper_->lastStep_);
297 // Print the remaining wall clock time for the run
298 if (MASTER(cr) && (do_verbose || gmx_got_usr_signal())
299 && !(pmeLoadBalanceHelper_ && pmeLoadBalanceHelper_->pmePrinting()))
301 print_time(stderr, walltime_accounting, step, inputrec, cr);
304 double cycles = wallcycle_stop(wcycle, ewcSTEP);
305 if (DOMAINDECOMP(cr) && wcycle)
307 dd_cycles_add(cr->dd, static_cast<float>(cycles), ddCyclStep);
310 resetHandler_->resetCounters(step,
311 step - inputrec->init_step,
318 pmeLoadBalanceHelper_ ? pmeLoadBalanceHelper_->loadBalancingObject() : nullptr,
320 walltime_accounting);
323 void ModularSimulatorAlgorithm::populateTaskQueue()
326 * The registerRunFunction emplaces functions to the task queue.
327 * All elements are owned by the ModularSimulatorAlgorithm, as is the task queue.
328 * Elements can hence register lambdas capturing their `this` pointers without expecting
329 * life time issues, as the task queue and the elements are in the same scope.
331 auto registerRunFunction = [this](SimulatorRunFunction function) {
332 taskQueue_.emplace_back(std::move(function));
335 Time startTime = inputrec->init_t;
336 Time timeStep = inputrec->delta_t;
337 Time time = startTime + step_ * timeStep;
339 // Run an initial call to the signallers
340 for (auto& signaller : signallerList_)
342 signaller->signal(step_, time);
345 if (checkpointHelper_)
347 checkpointHelper_->run(step_, time);
350 if (pmeLoadBalanceHelper_)
352 pmeLoadBalanceHelper_->run(step_, time);
356 domDecHelper_->run(step_, time);
361 // local variables for lambda capturing
362 const int step = step_;
363 const bool isNSStep = step == signalHelper_->nextNSStep_;
365 // register pre-step (task queue is local, so no problem with `this`)
366 registerRunFunction([this, step, time, isNSStep]() { preStep(step, time, isNSStep); });
367 // register elements for step
368 for (auto& element : elementCallList_)
370 element->scheduleTask(step_, time, registerRunFunction);
372 // register post-step (task queue is local, so no problem with `this`)
373 registerRunFunction([this, step, time]() { postStep(step, time); });
377 time = startTime + step_ * timeStep;
378 for (auto& signaller : signallerList_)
380 signaller->signal(step_, time);
382 } while (step_ != signalHelper_->nextNSStep_ && step_ <= signalHelper_->lastStep_);
384 runFinished_ = (step_ > signalHelper_->lastStep_);
388 // task queue is local, so no problem with `this`
389 registerRunFunction([this]() { teardown(); });
393 ModularSimulatorAlgorithmBuilder::ModularSimulatorAlgorithmBuilder(
394 compat::not_null<LegacySimulatorData*> legacySimulatorData,
395 std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder) :
396 legacySimulatorData_(legacySimulatorData),
397 signals_(std::make_unique<SimulationSignals>()),
398 elementAdditionHelper_(this),
399 globalCommunicationHelper_(computeGlobalCommunicationPeriod(legacySimulatorData->mdlog,
400 legacySimulatorData->inputrec,
401 legacySimulatorData->cr),
403 checkpointHelperBuilder_(std::move(checkpointDataHolder),
404 legacySimulatorData->startingBehavior,
405 legacySimulatorData->cr)
407 if (legacySimulatorData->inputrec->efep != FreeEnergyPerturbationType::No)
409 freeEnergyPerturbationData_ = std::make_unique<FreeEnergyPerturbationData>(
410 legacySimulatorData->fplog, *legacySimulatorData->inputrec, legacySimulatorData->mdAtoms);
413 statePropagatorData_ = std::make_unique<StatePropagatorData>(
414 legacySimulatorData->top_global.natoms,
415 legacySimulatorData->fplog,
416 legacySimulatorData->cr,
417 legacySimulatorData->state_global,
418 legacySimulatorData->fr->nbv->useGpu(),
419 legacySimulatorData->fr->bMolPBC,
420 legacySimulatorData->mdrunOptions.writeConfout,
421 opt2fn("-c", legacySimulatorData->nfile, legacySimulatorData->fnm),
422 legacySimulatorData->inputrec,
423 legacySimulatorData->mdAtoms->mdatoms(),
424 legacySimulatorData->top_global);
426 // Multi sim is turned off
427 const bool simulationsShareState = false;
429 energyData_ = std::make_unique<EnergyData>(statePropagatorData_.get(),
430 freeEnergyPerturbationData_.get(),
431 legacySimulatorData->top_global,
432 legacySimulatorData->inputrec,
433 legacySimulatorData->mdAtoms,
434 legacySimulatorData->enerd,
435 legacySimulatorData->ekind,
436 legacySimulatorData->constr,
437 legacySimulatorData->fplog,
438 legacySimulatorData->fr->fcdata.get(),
439 legacySimulatorData->mdModulesNotifier,
440 MASTER(legacySimulatorData->cr),
441 legacySimulatorData->observablesHistory,
442 legacySimulatorData->startingBehavior,
443 simulationsShareState);
446 ModularSimulatorAlgorithm ModularSimulatorAlgorithmBuilder::build()
448 if (algorithmHasBeenBuilt_)
450 throw SimulationAlgorithmSetupError(
451 "Tried to build ModularSimulationAlgorithm more than once.");
453 algorithmHasBeenBuilt_ = true;
455 // Connect propagators with thermostat / barostat
456 for (const auto& thermostatRegistration : thermostatRegistrationFunctions_)
458 for (const auto& connection : propagatorThermostatConnections_)
460 thermostatRegistration(connection);
463 for (const auto& barostatRegistration : barostatRegistrationFunctions_)
465 for (const auto& connection : propagatorBarostatConnections_)
467 barostatRegistration(connection);
471 ModularSimulatorAlgorithm algorithm(*(legacySimulatorData_->top_global.name),
472 legacySimulatorData_->fplog,
473 legacySimulatorData_->cr,
474 legacySimulatorData_->mdlog,
475 legacySimulatorData_->mdrunOptions,
476 legacySimulatorData_->inputrec,
477 legacySimulatorData_->nrnb,
478 legacySimulatorData_->wcycle,
479 legacySimulatorData_->fr,
480 legacySimulatorData_->walltime_accounting);
481 registerWithInfrastructureAndSignallers(algorithm.signalHelper_.get());
482 algorithm.statePropagatorData_ = std::move(statePropagatorData_);
483 algorithm.energyData_ = std::move(energyData_);
484 algorithm.freeEnergyPerturbationData_ = std::move(freeEnergyPerturbationData_);
485 algorithm.signals_ = std::move(signals_);
487 // Multi sim is turned off
488 const bool simulationsShareState = false;
490 // Build stop handler
491 algorithm.stopHandler_ = legacySimulatorData_->stopHandlerBuilder->getStopHandlerMD(
492 compat::not_null<SimulationSignal*>(
493 &(*globalCommunicationHelper_.simulationSignals())[eglsSTOPCOND]),
494 simulationsShareState,
495 MASTER(legacySimulatorData_->cr),
496 legacySimulatorData_->inputrec->nstlist,
497 legacySimulatorData_->mdrunOptions.reproducible,
498 globalCommunicationHelper_.nstglobalcomm(),
499 legacySimulatorData_->mdrunOptions.maximumHoursToRun,
500 legacySimulatorData_->inputrec->nstlist == 0,
501 legacySimulatorData_->fplog,
502 algorithm.stophandlerCurrentStep_,
503 algorithm.stophandlerIsNSStep_,
504 legacySimulatorData_->walltime_accounting);
506 // Build reset handler
507 const bool simulationsShareResetCounters = false;
508 algorithm.resetHandler_ = std::make_unique<ResetHandler>(
509 compat::make_not_null<SimulationSignal*>(
510 &(*globalCommunicationHelper_.simulationSignals())[eglsRESETCOUNTERS]),
511 simulationsShareResetCounters,
512 legacySimulatorData_->inputrec->nsteps,
513 MASTER(legacySimulatorData_->cr),
514 legacySimulatorData_->mdrunOptions.timingOptions.resetHalfway,
515 legacySimulatorData_->mdrunOptions.maximumHoursToRun,
516 legacySimulatorData_->mdlog,
517 legacySimulatorData_->wcycle,
518 legacySimulatorData_->walltime_accounting);
520 // Build topology holder
521 algorithm.topologyHolder_ = topologyHolderBuilder_.build(legacySimulatorData_->top_global,
522 legacySimulatorData_->cr,
523 legacySimulatorData_->inputrec,
524 legacySimulatorData_->fr,
525 legacySimulatorData_->mdAtoms,
526 legacySimulatorData_->constr,
527 legacySimulatorData_->vsite);
529 // Build PME load balance helper
530 if (PmeLoadBalanceHelper::doPmeLoadBalancing(legacySimulatorData_->mdrunOptions,
531 legacySimulatorData_->inputrec,
532 legacySimulatorData_->fr))
534 algorithm.pmeLoadBalanceHelper_ =
535 std::make_unique<PmeLoadBalanceHelper>(legacySimulatorData_->mdrunOptions.verbose,
536 algorithm.statePropagatorData_.get(),
537 legacySimulatorData_->fplog,
538 legacySimulatorData_->cr,
539 legacySimulatorData_->mdlog,
540 legacySimulatorData_->inputrec,
541 legacySimulatorData_->wcycle,
542 legacySimulatorData_->fr);
543 registerWithInfrastructureAndSignallers(algorithm.pmeLoadBalanceHelper_.get());
545 // Build domdec helper
546 if (DOMAINDECOMP(legacySimulatorData_->cr))
548 algorithm.domDecHelper_ = std::make_unique<DomDecHelper>(
549 legacySimulatorData_->mdrunOptions.verbose,
550 legacySimulatorData_->mdrunOptions.verboseStepPrintInterval,
551 algorithm.statePropagatorData_.get(),
552 algorithm.freeEnergyPerturbationData_.get(),
553 algorithm.topologyHolder_.get(),
554 globalCommunicationHelper_.nstglobalcomm(),
555 legacySimulatorData_->fplog,
556 legacySimulatorData_->cr,
557 legacySimulatorData_->mdlog,
558 legacySimulatorData_->constr,
559 legacySimulatorData_->inputrec,
560 legacySimulatorData_->mdAtoms,
561 legacySimulatorData_->nrnb,
562 legacySimulatorData_->wcycle,
563 legacySimulatorData_->fr,
564 legacySimulatorData_->vsite,
565 legacySimulatorData_->imdSession,
566 legacySimulatorData_->pull_work);
567 registerWithInfrastructureAndSignallers(algorithm.domDecHelper_.get());
570 // Build trajectory element
571 auto trajectoryElement = trajectoryElementBuilder_.build(legacySimulatorData_->fplog,
572 legacySimulatorData_->nfile,
573 legacySimulatorData_->fnm,
574 legacySimulatorData_->mdrunOptions,
575 legacySimulatorData_->cr,
576 legacySimulatorData_->outputProvider,
577 legacySimulatorData_->mdModulesNotifier,
578 legacySimulatorData_->inputrec,
579 legacySimulatorData_->top_global,
580 legacySimulatorData_->oenv,
581 legacySimulatorData_->wcycle,
582 legacySimulatorData_->startingBehavior,
583 simulationsShareState);
584 registerWithInfrastructureAndSignallers(trajectoryElement.get());
586 // Build free energy element
587 std::unique_ptr<FreeEnergyPerturbationData::Element> freeEnergyPerturbationElement = nullptr;
588 if (algorithm.freeEnergyPerturbationData_)
590 freeEnergyPerturbationElement = std::make_unique<FreeEnergyPerturbationData::Element>(
591 algorithm.freeEnergyPerturbationData_.get(),
592 legacySimulatorData_->inputrec->fepvals->delta_lambda);
593 registerWithInfrastructureAndSignallers(freeEnergyPerturbationElement.get());
596 // Build checkpoint helper (do this last so everyone else can be a checkpoint client!)
598 checkpointHelperBuilder_.setCheckpointHandler(std::make_unique<CheckpointHandler>(
599 compat::make_not_null<SimulationSignal*>(&(*algorithm.signals_)[eglsCHKPT]),
600 simulationsShareState,
601 legacySimulatorData_->inputrec->nstlist == 0,
602 MASTER(legacySimulatorData_->cr),
603 legacySimulatorData_->mdrunOptions.writeConfout,
604 legacySimulatorData_->mdrunOptions.checkpointOptions.period));
605 algorithm.checkpointHelper_ =
606 checkpointHelperBuilder_.build(legacySimulatorData_->inputrec->init_step,
607 trajectoryElement.get(),
608 legacySimulatorData_->fplog,
609 legacySimulatorData_->cr,
610 legacySimulatorData_->observablesHistory,
611 legacySimulatorData_->walltime_accounting,
612 legacySimulatorData_->state_global,
613 legacySimulatorData_->mdrunOptions.writeConfout);
614 registerWithInfrastructureAndSignallers(algorithm.checkpointHelper_.get());
619 /* Signallers need to be called in an exact order. Some signallers are clients
620 * of other signallers, which requires the clients signallers to be called
621 * _after_ any signaller they are registered to - otherwise, they couldn't
622 * adapt their behavior to the information they got signalled.
624 * Signallers being clients of other signallers require registration.
625 * That registration happens during construction, which in turn means that
626 * we want to construct the signallers in the reverse order of their later
629 * For the above reasons, the `addSignaller` lambda defined below emplaces
630 * added signallers at the beginning of the signaller list, which will yield
631 * a signaller list which is inverse to the build order (and hence equal to
632 * the intended call order).
634 auto addSignaller = [this, &algorithm](auto signaller) {
635 registerWithInfrastructureAndSignallers(signaller.get());
636 algorithm.signallerList_.emplace(algorithm.signallerList_.begin(), std::move(signaller));
638 const auto* inputrec = legacySimulatorData_->inputrec;
639 addSignaller(energySignallerBuilder_.build(
640 inputrec->nstcalcenergy, inputrec->fepvals->nstdhdl, inputrec->nstpcouple));
641 addSignaller(trajectorySignallerBuilder_.build(inputrec->nstxout,
644 inputrec->nstxout_compressed,
645 trajectoryElement->tngBoxOut(),
646 trajectoryElement->tngLambdaOut(),
647 trajectoryElement->tngBoxOutCompressed(),
648 trajectoryElement->tngLambdaOutCompressed(),
649 inputrec->nstenergy));
650 addSignaller(loggingSignallerBuilder_.build(
651 inputrec->nstlog, inputrec->init_step, legacySimulatorData_->startingBehavior));
652 addSignaller(lastStepSignallerBuilder_.build(
653 inputrec->nsteps, inputrec->init_step, algorithm.stopHandler_.get()));
654 addSignaller(neighborSearchSignallerBuilder_.build(
655 inputrec->nstlist, inputrec->init_step, inputrec->init_t));
658 // Create element list
659 // Checkpoint helper needs to be in the call list (as first element!) to react to last step
660 algorithm.elementCallList_.emplace_back(algorithm.checkpointHelper_.get());
661 // Next, update the free energy lambda vector if needed
662 if (freeEnergyPerturbationElement)
664 algorithm.elementsOwnershipList_.emplace_back(std::move(freeEnergyPerturbationElement));
665 algorithm.elementCallList_.emplace_back(algorithm.elementsOwnershipList_.back().get());
667 // Then, move the built algorithm
668 algorithm.elementsOwnershipList_.insert(algorithm.elementsOwnershipList_.end(),
669 std::make_move_iterator(elements_.begin()),
670 std::make_move_iterator(elements_.end()));
671 algorithm.elementCallList_.insert(algorithm.elementCallList_.end(),
672 std::make_move_iterator(callList_.begin()),
673 std::make_move_iterator(callList_.end()));
674 // Finally, all trajectory writing is happening after the step
675 // (relevant data was stored by elements through energy signaller)
676 algorithm.elementsOwnershipList_.emplace_back(std::move(trajectoryElement));
677 algorithm.elementCallList_.emplace_back(algorithm.elementsOwnershipList_.back().get());
683 ISimulatorElement* ModularSimulatorAlgorithmBuilder::addElementToSimulatorAlgorithm(
684 std::unique_ptr<ISimulatorElement> element)
686 elements_.emplace_back(std::move(element));
687 return elements_.back().get();
690 bool ModularSimulatorAlgorithmBuilder::elementExists(const ISimulatorElement* element) const
692 // Check whether element exists in element list
693 if (std::any_of(elements_.begin(), elements_.end(), [element](auto& existingElement) {
694 return element == existingElement.get();
699 // Check whether element exists in other places controlled by *this
700 return (statePropagatorData_->element() == element || energyData_->element() == element
701 || (freeEnergyPerturbationData_ && freeEnergyPerturbationData_->element() == element));
704 void ModularSimulatorAlgorithmBuilder::addElementToSetupTeardownList(ISimulatorElement* element)
706 // Add element if it's not already in the list
707 if (std::find(setupAndTeardownList_.begin(), setupAndTeardownList_.end(), element)
708 == setupAndTeardownList_.end())
710 setupAndTeardownList_.emplace_back(element);
714 std::optional<SignallerCallback> ModularSimulatorAlgorithm::SignalHelper::registerLastStepCallback()
716 return [this](Step step, Time gmx_unused time) { this->lastStep_ = step; };
719 std::optional<SignallerCallback> ModularSimulatorAlgorithm::SignalHelper::registerNSCallback()
721 return [this](Step step, Time gmx_unused time) { this->nextNSStep_ = step; };
724 GlobalCommunicationHelper::GlobalCommunicationHelper(int nstglobalcomm, SimulationSignals* simulationSignals) :
725 nstglobalcomm_(nstglobalcomm),
726 simulationSignals_(simulationSignals)
730 int GlobalCommunicationHelper::nstglobalcomm() const
732 return nstglobalcomm_;
735 SimulationSignals* GlobalCommunicationHelper::simulationSignals()
737 return simulationSignals_;
740 ModularSimulatorAlgorithmBuilderHelper::ModularSimulatorAlgorithmBuilderHelper(
741 ModularSimulatorAlgorithmBuilder* builder) :
746 ISimulatorElement* ModularSimulatorAlgorithmBuilderHelper::storeElement(std::unique_ptr<ISimulatorElement> element)
748 return builder_->addElementToSimulatorAlgorithm(std::move(element));
751 bool ModularSimulatorAlgorithmBuilderHelper::elementIsStored(const ISimulatorElement* element) const
753 return builder_->elementExists(element);
756 std::optional<std::any> ModularSimulatorAlgorithmBuilderHelper::getStoredValue(const std::string& key) const
758 const auto iter = values_.find(key);
759 if (iter == values_.end())
769 void ModularSimulatorAlgorithmBuilderHelper::registerThermostat(
770 std::function<void(const PropagatorThermostatConnection&)> registrationFunction)
772 builder_->thermostatRegistrationFunctions_.emplace_back(std::move(registrationFunction));
775 void ModularSimulatorAlgorithmBuilderHelper::registerBarostat(
776 std::function<void(const PropagatorBarostatConnection&)> registrationFunction)
778 builder_->barostatRegistrationFunctions_.emplace_back(std::move(registrationFunction));
781 void ModularSimulatorAlgorithmBuilderHelper::registerWithThermostat(PropagatorThermostatConnection connectionData)
783 builder_->propagatorThermostatConnections_.emplace_back(std::move(connectionData));
786 void ModularSimulatorAlgorithmBuilderHelper::registerWithBarostat(PropagatorBarostatConnection connectionData)
788 builder_->propagatorBarostatConnections_.emplace_back(std::move(connectionData));