Internal parameter storage for IMDModules
authorChristian Blau <cblau@gwdg.de>
Thu, 8 Aug 2019 14:18:35 +0000 (16:18 +0200)
committerErik Lindahl <erik.lindahl@gmail.com>
Wed, 14 Aug 2019 09:36:56 +0000 (11:36 +0200)
It is currently not possible for MdModules to store data in a tpr
file that is non-mdp input, e.g., the result of some computation
during setup level.

Atom indices of index groups are one example: evaluated from strings
during grompp time, they are stored as list of integers in the run
input file. During the mdrun setup the information to evaluate the
index groups is no longer available.

This patch introduces a storage for internal MdModule parameters.

The parameters are stored in a seperate key-value-tree in the input
record that is then serialized into the tpr file. In contrast to the
ir->params, this key-value-tree does not match the options given in
an input file.

Change-Id: I207d8c837c39f1afb196507fe228306700e46a49

docs/doxygen/lib/mdmodules.md
src/gromacs/fileio/tpxio.cpp
src/gromacs/gmxpreprocess/grompp.cpp
src/gromacs/mdlib/broadcaststructs.cpp
src/gromacs/mdrun/mdmodules.h
src/gromacs/mdrun/runner.cpp
src/gromacs/mdtypes/inputrec.h

index 3da16c19b3a8163f622fa18c0430e6943baa7e93..a531b5a1b5957b0c8f3a28a2a3c974aa667dc155 100644 (file)
@@ -191,3 +191,19 @@ To include a notification for your module,
     YourCallbackSignature argument();
     mdModules_.notifier().notify(argument);
   ```
+
+Storing non-mdp option module parameters
+----------------------------------------
+
+Some mdrun modules want to store data that is non-mdp input, e.g., the result of
+computation during setup. Atom indices of index groups are one example:
+they are evaluated from strings during grompp time and stored as list of
+integers in the run input file. During the mdrun setup the information to
+evaluate the index groups is no longer available.
+
+To store parameters, subscribe to the `KeyValueTreeBuilder*` notification that
+provides a handle to a KeyValueTreeBuilder that allows adding own information to
+that tree.
+
+To restore parameters, subscribe to the `const KeyValueTreeObject &`
+notification that returns the tree that is build by the KeyValueTreeBuilder*.
index 0d26379c7532434e021df620beb766676e7d60ab..409ec59fb3b81a0fd92ebb6201d17ab506119bb9 100644 (file)
@@ -101,7 +101,8 @@ static const char *tpx_tag = TPX_TAG_RELEASE;
  * in this enumeration, and write code below that does the right thing
  * according to the value of file_version.
  */
-enum tpxv {
+enum tpxv
+{
     tpxv_ComputationalElectrophysiology = 96,                /**< support for ion/water position swaps (computational electrophysiology) */
     tpxv_Use64BitRandomSeed,                                 /**< change ld_seed from int to int64_t */
     tpxv_RestrictedBendingAndCombinedAngleTorsionPotentials, /**< potentials for supporting coarse-grained force fields */
@@ -121,8 +122,9 @@ enum tpxv {
     tpxv_AcceleratedWeightHistogram,                         /**< sampling with accelerated weight histogram method (AWH) */
     tpxv_RemoveImplicitSolvation,                            /**< removed support for implicit solvation */
     tpxv_PullPrevStepCOMAsReference,                         /**< Enabled using the COM of the pull group of the last frame as reference for PBC */
-    tpxv_MimicQMMM,                                          /**< Inroduced support for MiMiC QM/MM interface */
+    tpxv_MimicQMMM,                                          /**< Introduced support for MiMiC QM/MM interface */
     tpxv_PullAverage,                                        /**< Added possibility to output average pull force and position */
+    tpxv_GenericInternalParameters,                          /**< Added internal parameters for mdrun modules*/
     tpxv_Count                                               /**< the total number of tpxv versions */
 };
 
@@ -1684,6 +1686,20 @@ static void do_inputrec(gmx::ISerializer         *serializer,
     {
         ir->params = new gmx::KeyValueTreeObject(paramsBuilder.build());
     }
+
+    if (file_version >= tpxv_GenericInternalParameters)
+    {
+        if (serializer->reading())
+        {
+            ir->internalParameters = std::make_unique<gmx::KeyValueTreeObject>(gmx::deserializeKeyValueTree(serializer));
+        }
+        else
+        {
+            GMX_RELEASE_ASSERT(ir->internalParameters != nullptr,
+                               "Parameters should be present when writing inputrec");
+            gmx::serializeKeyValueTree(*ir->internalParameters, serializer);
+        }
+    }
 }
 
 
index 43a317cc211793837f3f4531f07e310b3c5bc733..10cb4d9875e5430d2f90a01b292446fead7e3c19 100644 (file)
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/futil.h"
 #include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/keyvaluetreebuilder.h"
 #include "gromacs/utility/smalloc.h"
 #include "gromacs/utility/snprintf.h"
 
@@ -2418,6 +2419,15 @@ int gmx_grompp(int argc, char *argv[])
         }
     }
 
+    // Add the md modules internal parameters that are not mdp options
+    // e.g., atom indices
+
+    {
+        gmx::KeyValueTreeBuilder internalParameterBuilder;
+        mdModules.notifier().notify(&internalParameterBuilder);
+        ir->internalParameters = std::make_unique<gmx::KeyValueTreeObject>(internalParameterBuilder.build());
+    }
+
     if (bVerbose)
     {
         fprintf(stderr, "writing run input file...\n");
index 6eb325684e794c5ad1aaa75e125f169bafc5a5b6..7d67cf00ff21e9e0e5fc66331941fa963edf4fdb 100644 (file)
@@ -677,6 +677,15 @@ static void bc_swapions(const t_commrec *cr, t_swapcoords *swap)
 
 static void bc_inputrec(const t_commrec *cr, t_inputrec *inputrec)
 {
+    // Make sure to destruct all previously set internal parameters properly
+    // before the block_bc on the inputrec overwrites them.
+    // They are expected to be null anyway, but we can't guarantee this here.
+    if (!SIMMASTER(cr))
+    {
+        // will call destructor on previously set parameters upon leaving this block
+        std::unique_ptr<gmx::KeyValueTreeObject> previouslySetInternalParametersOnNonMaster;
+        inputrec->internalParameters.swap(previouslySetInternalParametersOnNonMaster);
+    }
     // Note that this overwrites pointers in inputrec, so all pointer fields
     // Must be initialized separately below.
     block_bc(cr, *inputrec);
@@ -684,6 +693,7 @@ static void bc_inputrec(const t_commrec *cr, t_inputrec *inputrec)
     {
         gmx::InMemorySerializer serializer;
         gmx::serializeKeyValueTree(*inputrec->params, &serializer);
+        gmx::serializeKeyValueTree(*inputrec->internalParameters, &serializer);
         std::vector<char>       buffer = serializer.finishAndGetBuffer();
         size_t                  size   = buffer.size();
         block_bc(cr, size);
@@ -691,7 +701,7 @@ static void bc_inputrec(const t_commrec *cr, t_inputrec *inputrec)
     }
     else
     {
-        // block_bc() above overwrites the old pointer, so set it to a
+        // block_bc() of inputrec above overwrites the old pointer, so set it to a
         // reasonable value in case code below throws.
         inputrec->params = nullptr;
         std::vector<char> buffer;
@@ -701,6 +711,11 @@ static void bc_inputrec(const t_commrec *cr, t_inputrec *inputrec)
         gmx::InMemoryDeserializer serializer(buffer, false);
         inputrec->params = new gmx::KeyValueTreeObject(
                     gmx::deserializeKeyValueTree(&serializer));
+        // release is required because internalParameters' destructor will fail
+        // if block_bc() of inputrec overwrites the internalParameters pointer with garbage
+        auto gmx_unused releasedGarbagePointer = inputrec->internalParameters.release();
+        inputrec->internalParameters = std::make_unique<gmx::KeyValueTreeObject>(
+                    gmx::deserializeKeyValueTree(&serializer));
     }
 
     bc_grpopts(cr, &(inputrec->opts));
index e3c816f8abf9693b95d4b2b2d9039596391d78a3..21ca0ba5a77f53c100c421894a08898dcb75154a 100644 (file)
@@ -59,6 +59,7 @@ class IKeyValueTreeErrorHandler;
 class IKeyValueTreeTransformRules;
 class IMDOutputProvider;
 class KeyValueTreeObject;
+class KeyValueTreeBuilder;
 class IMDModule;
 class LocalAtomSetManager;
 
@@ -110,6 +111,8 @@ class MDModules
         //! Register callback function types for MDModules
         using notifier_type = registerMdModuleNotification<
                     CommunicationIsSetup,
+                    KeyValueTreeBuilder*,
+                    const KeyValueTreeObject &,
                     LocalAtomSetManager *
                     >::type;
 
index 6e01ac3c470eadfe02d5cbeae0b8c9b6fb3bd4f0..bc1a4bbc6ee418f80ba64e57faccf667526ed0f8 100644 (file)
 #include "gromacs/utility/filestream.h"
 #include "gromacs/utility/gmxassert.h"
 #include "gromacs/utility/gmxmpi.h"
+#include "gromacs/utility/keyvaluetree.h"
 #include "gromacs/utility/logger.h"
 #include "gromacs/utility/loggerbuilder.h"
 #include "gromacs/utility/physicalnodecommunicator.h"
@@ -798,6 +799,10 @@ int Mdrunner::mdrunner()
 
     // TODO: Error handling
     mdModules_->assignOptionsToModules(*inputrec->params, nullptr);
+    if (inputrec->internalParameters != nullptr)
+    {
+        mdModules_->notifier().notify(*inputrec->internalParameters);
+    }
 
     if (fplog != nullptr)
     {
index 48b3fda5de83f35bb1b08d5f42e774d974dfe1a2..dc24fc035b194f3894dc1eaacdf52a8498d669b6 100644 (file)
@@ -587,6 +587,9 @@ struct t_inputrec // NOLINT (clang-analyzer-optin.performance.Padding)
 
     //! KVT object that contains input parameters converted to the new style.
     gmx::KeyValueTreeObject *params;
+
+    //! KVT for storing simulation parameters that are not part of the mdp file.
+    std::unique_ptr<gmx::KeyValueTreeObject>  internalParameters;
 };
 
 int ir_optimal_nstcalcenergy(const t_inputrec *ir);