Clean up simulator algorithm
authorPascal Merz <pascal.merz@me.com>
Thu, 27 May 2021 08:55:14 +0000 (08:55 +0000)
committerMark Abraham <mark.j.abraham@gmail.com>
Thu, 27 May 2021 08:55:14 +0000 (08:55 +0000)
src/gromacs/modularsimulator/simulatoralgorithm.cpp
src/gromacs/modularsimulator/simulatoralgorithm.h

index aa354dd7b352ce4e27c1b4a6cad28d593bc65884..eac4ea170f16deca4ba96c97c3630ce5ad0a0fd6 100644 (file)
@@ -128,7 +128,7 @@ void ModularSimulatorAlgorithm::setup()
         domDecHelper_->setup();
     }
 
-    for (auto& element : elementsOwnershipList_)
+    for (auto& element : elementSetupTeardownList_)
     {
         element->elementSetup();
     }
@@ -169,7 +169,7 @@ void ModularSimulatorAlgorithm::updateTaskQueue()
 
 void ModularSimulatorAlgorithm::teardown()
 {
-    for (auto& element : elementsOwnershipList_)
+    for (auto& element : elementSetupTeardownList_)
     {
         element->elementTeardown();
     }
@@ -407,6 +407,7 @@ ModularSimulatorAlgorithmBuilder::ModularSimulatorAlgorithmBuilder(
     {
         freeEnergyPerturbationData_ = std::make_unique<FreeEnergyPerturbationData>(
                 legacySimulatorData->fplog, *legacySimulatorData->inputrec, legacySimulatorData->mdAtoms);
+        registerExistingElement(freeEnergyPerturbationData_->element());
     }
 
     statePropagatorData_ = std::make_unique<StatePropagatorData>(
@@ -421,6 +422,7 @@ ModularSimulatorAlgorithmBuilder::ModularSimulatorAlgorithmBuilder(
             legacySimulatorData->inputrec,
             legacySimulatorData->mdAtoms->mdatoms(),
             legacySimulatorData->top_global);
+    registerExistingElement(statePropagatorData_->element());
 
     // Multi sim is turned off
     const bool simulationsShareState = false;
@@ -440,14 +442,15 @@ ModularSimulatorAlgorithmBuilder::ModularSimulatorAlgorithmBuilder(
                                                legacySimulatorData->observablesHistory,
                                                legacySimulatorData->startingBehavior,
                                                simulationsShareState);
+    registerExistingElement(energyData_->element());
 }
 
 ModularSimulatorAlgorithm ModularSimulatorAlgorithmBuilder::build()
 {
     if (algorithmHasBeenBuilt_)
     {
-        throw SimulationAlgorithmSetupError(
-                "Tried to build ModularSimulationAlgorithm more than once.");
+        GMX_THROW(SimulationAlgorithmSetupError(
+                "Tried to build ModularSimulationAlgorithm more than once."));
     }
     algorithmHasBeenBuilt_ = true;
 
@@ -553,17 +556,7 @@ ModularSimulatorAlgorithm ModularSimulatorAlgorithmBuilder::build()
                                                              simulationsShareState);
     registerWithInfrastructureAndSignallers(trajectoryElement.get());
 
-    // Build free energy element
-    std::unique_ptr<FreeEnergyPerturbationData::Element> freeEnergyPerturbationElement = nullptr;
-    if (algorithm.freeEnergyPerturbationData_)
-    {
-        freeEnergyPerturbationElement = std::make_unique<FreeEnergyPerturbationData::Element>(
-                algorithm.freeEnergyPerturbationData_.get(),
-                legacySimulatorData_->inputrec->fepvals->delta_lambda);
-        registerWithInfrastructureAndSignallers(freeEnergyPerturbationElement.get());
-    }
-
-    // Build domdec helper (free energy element is a client, so keep this after it is built)
+    // Build domdec helper
     if (DOMAINDECOMP(legacySimulatorData_->cr))
     {
         algorithm.domDecHelper_ =
@@ -660,14 +653,16 @@ ModularSimulatorAlgorithm ModularSimulatorAlgorithmBuilder::build()
                 inputrec->nstlist, inputrec->init_step, inputrec->init_t));
     }
 
+    // Move setup / teardown list
+    algorithm.elementSetupTeardownList_ = std::move(setupAndTeardownList_);
+
     // Create element list
     // Checkpoint helper needs to be in the call list (as first element!) to react to last step
     algorithm.elementCallList_.emplace_back(algorithm.checkpointHelper_.get());
     // Next, update the free energy lambda vector if needed
-    if (freeEnergyPerturbationElement)
+    if (algorithm.freeEnergyPerturbationData_)
     {
-        algorithm.elementsOwnershipList_.emplace_back(std::move(freeEnergyPerturbationElement));
-        algorithm.elementCallList_.emplace_back(algorithm.elementsOwnershipList_.back().get());
+        algorithm.elementCallList_.emplace_back(algorithm.freeEnergyPerturbationData_->element());
     }
     // Then, move the built algorithm
     algorithm.elementsOwnershipList_.insert(algorithm.elementsOwnershipList_.end(),
@@ -680,18 +675,12 @@ ModularSimulatorAlgorithm ModularSimulatorAlgorithmBuilder::build()
     // (relevant data was stored by elements through energy signaller)
     algorithm.elementsOwnershipList_.emplace_back(std::move(trajectoryElement));
     algorithm.elementCallList_.emplace_back(algorithm.elementsOwnershipList_.back().get());
+    algorithm.elementSetupTeardownList_.emplace_back(algorithm.elementsOwnershipList_.back().get());
 
     algorithm.setup();
     return algorithm;
 }
 
-ISimulatorElement* ModularSimulatorAlgorithmBuilder::addElementToSimulatorAlgorithm(
-        std::unique_ptr<ISimulatorElement> element)
-{
-    elements_.emplace_back(std::move(element));
-    return elements_.back().get();
-}
-
 bool ModularSimulatorAlgorithmBuilder::elementExists(const ISimulatorElement* element) const
 {
     // Check whether element exists in element list
@@ -702,20 +691,11 @@ bool ModularSimulatorAlgorithmBuilder::elementExists(const ISimulatorElement* el
         return true;
     }
     // Check whether element exists in other places controlled by *this
-    return (statePropagatorData_->element() == element || energyData_->element() == element
+    return ((statePropagatorData_ && statePropagatorData_->element() == element)
+            || (energyData_ && energyData_->element() == element)
             || (freeEnergyPerturbationData_ && freeEnergyPerturbationData_->element() == element));
 }
 
-void ModularSimulatorAlgorithmBuilder::addElementToSetupTeardownList(ISimulatorElement* element)
-{
-    // Add element if it's not already in the list
-    if (std::find(setupAndTeardownList_.begin(), setupAndTeardownList_.end(), element)
-        == setupAndTeardownList_.end())
-    {
-        setupAndTeardownList_.emplace_back(element);
-    }
-}
-
 std::optional<SignallerCallback> ModularSimulatorAlgorithm::SignalHelper::registerLastStepCallback()
 {
     return [this](Step step, Time gmx_unused time) { this->lastStep_ = step; };
@@ -747,11 +727,6 @@ ModularSimulatorAlgorithmBuilderHelper::ModularSimulatorAlgorithmBuilderHelper(
 {
 }
 
-ISimulatorElement* ModularSimulatorAlgorithmBuilderHelper::storeElement(std::unique_ptr<ISimulatorElement> element)
-{
-    return builder_->addElementToSimulatorAlgorithm(std::move(element));
-}
-
 bool ModularSimulatorAlgorithmBuilderHelper::elementIsStored(const ISimulatorElement* element) const
 {
     return builder_->elementExists(element);
index 8edb2e2d77ff492f7a754225f3d1f00d23e70b21..2333effb1f9eb5749f5149a067e71da566925ba0 100644 (file)
@@ -189,8 +189,10 @@ private:
     std::vector<std::unique_ptr<ISignaller>> signallerList_;
     //! List of schedulerElements (ownership)
     std::vector<std::unique_ptr<ISimulatorElement>> elementsOwnershipList_;
-    //! List of schedulerElements (calling sequence)
+    //! List of schedulerElements (run calling sequence)
     std::vector<ISimulatorElement*> elementCallList_;
+    //! List of schedulerElements (setup / teardown calling sequence)
+    std::vector<ISimulatorElement*> elementSetupTeardownList_;
 
     // Infrastructure elements
     //! The domain decomposition element
@@ -318,7 +320,8 @@ public:
     //! Constructor
     ModularSimulatorAlgorithmBuilderHelper(ModularSimulatorAlgorithmBuilder* builder);
     //! Store an element to the ModularSimulatorAlgorithmBuilder
-    ISimulatorElement* storeElement(std::unique_ptr<ISimulatorElement> element);
+    template<typename Element>
+    Element* storeElement(std::unique_ptr<Element> element);
     //! Check if an element is stored in the ModularSimulatorAlgorithmBuilder
     bool elementIsStored(const ISimulatorElement* element) const;
     /*! \brief Set arbitrary data in the ModularSimulatorAlgorithmBuilder
@@ -441,15 +444,33 @@ private:
      *
      * This function returns a non-owning pointer to the new location of that
      * element, allowing further usage (e.g. adding the element to the call list).
-     * Note that simply addin an element using this function will not call it
+     * This will also add the element to the setup / teardown list, and register
+     * it with all applicable signallers and infrastructure objects.
+     * Note that simply adding an element using this function will not call it
      * during the simulation - it needs to be added to the call list separately.
-     * Note that generally, users will want to add elements to the call list, but
-     * it might not be practical to do this in the same order.
+     * Also note that generally, users will want to add elements to the call list,
+     * but it might not be practical to do this in the same order.
      *
+     * \tparam Element  Type of the Element
      * \param element  A unique pointer to the element
      * \return  A non-owning (raw) pointer to the element for further usage
      */
-    ISimulatorElement* addElementToSimulatorAlgorithm(std::unique_ptr<ISimulatorElement> element);
+    template<typename Element>
+    Element* addElementToSimulatorAlgorithm(std::unique_ptr<Element> element);
+
+    /*! \brief Register existing element to infrastructure
+     *
+     * This function adds existing elements to the setup / teardown list, and
+     * registers them with all applicable signallers and infrastructure objects.
+     * This is only permissible for elements owned directly by the builder or
+     * indirectly through data objects. Before registering the element, the function
+     * checks that the element is owned by the builder or a known object.
+     *
+     * \tparam Element  Type of the Element
+     * \param element   A non-owning (raw) pointer to the element
+     */
+    template<typename Element>
+    void registerExistingElement(Element* element);
 
     /*! \brief Check if element is owned by *this
      *
@@ -458,12 +479,6 @@ private:
      */
     [[nodiscard]] bool elementExists(const ISimulatorElement* element) const;
 
-    /*! \brief Add element to setupAndTeardownList_ if it's not already there
-     *
-     * \param element  Element pointer to be added
-     */
-    void addElementToSetupTeardownList(ISimulatorElement* element);
-
     //! Vector to store elements, allowing the SimulatorAlgorithm to control their lifetime
     std::vector<std::unique_ptr<ISimulatorElement>> elements_;
     /*! \brief List defining in which order elements are called every step
@@ -580,8 +595,8 @@ void ModularSimulatorAlgorithmBuilder::add(Args&&... args)
 {
     if (algorithmHasBeenBuilt_)
     {
-        throw SimulationAlgorithmSetupError(
-                "Tried to add an element after ModularSimulationAlgorithm was built.");
+        GMX_THROW(SimulationAlgorithmSetupError(
+                "Tried to add an element after ModularSimulationAlgorithm was built."));
     }
 
     // Get element from factory method
@@ -597,14 +612,10 @@ void ModularSimulatorAlgorithmBuilder::add(Args&&... args)
     // Ensuring this makes sure we can control the life time
     if (!elementExists(element))
     {
-        throw ElementNotFoundError("Tried to append non-existing element to call list.");
+        GMX_THROW(ElementNotFoundError("Tried to append non-existing element to call list."));
     }
     // Add to call list
     callList_.emplace_back(element);
-    // Add to setup / teardown list if element hasn't been added yet
-    addElementToSetupTeardownList(element);
-    // Register element to all applicable signallers
-    registerWithInfrastructureAndSignallers(element);
 }
 
 //! Returns a pointer casted to type Base if the Element is derived from Base
@@ -642,6 +653,42 @@ void ModularSimulatorAlgorithmBuilder::registerWithInfrastructureAndSignallers(E
     domDecHelperBuilder_.registerClient(castOrNull<IDomDecHelperClient, Element>(element));
 }
 
+template<typename Element>
+Element* ModularSimulatorAlgorithmBuilder::addElementToSimulatorAlgorithm(std::unique_ptr<Element> element)
+{
+    // Store element
+    elements_.emplace_back(std::move(element));
+    // Get non-owning pointer for further use
+    Element* elementPtr = static_cast<Element*>(elements_.back().get());
+    // Register element to infrastructure
+    registerExistingElement(elementPtr);
+
+    return elementPtr;
+}
+
+template<typename Element>
+void ModularSimulatorAlgorithmBuilder::registerExistingElement(Element* element)
+{
+    // Make sure the element pointer is owned by *this
+    // Ensuring this makes sure we can control the life time
+    if (!elementExists(element))
+    {
+        GMX_THROW(
+                ElementNotFoundError("Tried to register non-existing element to infrastructure."));
+    }
+
+    // Add to setup / teardown list
+    setupAndTeardownList_.emplace_back(element);
+    // Register element to all applicable signallers
+    registerWithInfrastructureAndSignallers(element);
+}
+
+template<typename Element>
+Element* ModularSimulatorAlgorithmBuilderHelper::storeElement(std::unique_ptr<Element> element)
+{
+    return builder_->addElementToSimulatorAlgorithm(std::move(element));
+}
+
 template<typename ValueType>
 void ModularSimulatorAlgorithmBuilderHelper::storeBuilderData(const std::string& key, const ValueType& value)
 {