Change MPI setup to communicate TPR as buffer
authorPaul Bauer <paul.bauer.q@gmail.com>
Fri, 14 Jun 2019 09:42:28 +0000 (11:42 +0200)
committerMark Abraham <mark.j.abraham@gmail.com>
Mon, 23 Sep 2019 10:17:02 +0000 (12:17 +0200)
Changed the initial setup of nodes to communicate the full tpr file
buffer instead of using the individual calls for the fields.

Now non-master nodes receive the inputrec and mtop
and populate them themselves.

Refs #2971

Change-Id: Id4f3739a978ca507dacc45c78a8a75368cfe86fd

src/gromacs/fileio/tpxio.cpp
src/gromacs/fileio/tpxio.h
src/gromacs/mdlib/broadcaststructs.cpp
src/gromacs/mdlib/broadcaststructs.h
src/gromacs/mdrun/runner.cpp
src/gromacs/utility/inmemoryserializer.cpp
src/gromacs/utility/inmemoryserializer.h

index 302af1dc5b7f2214e6d020eb6b425b5fcc0de248..33279bd528215a7daaa3f90bb5521523afa40c47 100644 (file)
@@ -2651,7 +2651,6 @@ static void do_tpxheader(gmx::FileIOXdrSerializer *serializer,
                          t_fileio                 *fio,
                          bool                      TopOnlyOK)
 {
-    gmx_bool  bDouble;
     int       precision;
     int       idum = 0;
     real      rdum = 0;
@@ -2670,24 +2669,28 @@ static void do_tpxheader(gmx::FileIOXdrSerializer *serializer,
                       "             Make a new one with grompp or use a gro or pdb file, if possible",
                       filename);
         }
+        // We need to know the precision used to write the TPR file, to match it
+        // to the precision of the currently running binary. If the precisions match
+        // there is no problem, but mismatching precision needs to be accounted for
+        // by reading into temporary variables of the correct precision instead
+        // of the desired target datastructures.
         serializer->doInt(&precision);
-        bDouble = (precision == sizeof(double));
-        if ((precision != sizeof(float)) && !bDouble)
+        tpx->isDouble = (precision == sizeof(double));
+        if ((precision != sizeof(float)) && !tpx->isDouble)
         {
             gmx_fatal(FARGS, "Unknown precision in file %s: real is %d bytes "
                       "instead of %zu or %zu",
                       filename, precision, sizeof(float), sizeof(double));
         }
-        gmx_fio_setprecision(fio, bDouble);
+        gmx_fio_setprecision(fio, tpx->isDouble);
         fprintf(stderr, "Reading file %s, %s (%s precision)\n",
-                filename, buf.c_str(), bDouble ? "double" : "single");
+                filename, buf.c_str(), tpx->isDouble ? "double" : "single");
     }
     else
     {
         buf = gmx::formatString("VERSION %s", gmx_version());
         serializer->doString(&buf);
-        bDouble = (precision == sizeof(double));
-        gmx_fio_setprecision(fio, bDouble);
+        gmx_fio_setprecision(fio, tpx->isDouble);
         serializer->doInt(&precision);
         fileTag        = gmx::formatString("%s", tpx_tag);
     }
@@ -2783,6 +2786,21 @@ static void do_tpxheader(gmx::FileIOXdrSerializer *serializer,
 
 #define do_test(serializer, b, p) if ((serializer)->reading() && ((p) != nullptr) && !(b)) gmx_fatal(FARGS, "No %s in input file",#p)
 
+/*! \brief
+ * Process the first part of the TPR into the state datastructure.
+ *
+ * Due to the structure of the legacy code, it is necessary
+ * to split up the state reading into two parts, with the
+ * box and legacy temperature coupling processed before the
+ * topology datastructures.
+ *
+ * See the documentation for do_tpx_body for the correct order of
+ * the operations for reading a tpr file.
+ *
+ * \param[in] serializer Abstract serializer used to read/write data.
+ * \param[in] tpx The file header data.
+ * \param[in, out] state Global state data.
+ */
 static void do_tpx_state_first(gmx::ISerializer *serializer,
                                TpxFileHeader    *tpx,
                                t_state          *state)
@@ -2827,6 +2845,16 @@ static void do_tpx_state_first(gmx::ISerializer *serializer,
     }
 }
 
+/*! \brief
+ * Process global topology data.
+ *
+ * See the documentation for do_tpx_body for the correct order of
+ * the operations for reading a tpr file.
+ *
+ * \param[in] serializer Abstract serializer  used to read/write data.
+ * \param[in] tpx The file header data.
+ * \param[in,out] mtop Global topology.
+ */
 static void do_tpx_mtop(gmx::ISerializer *serializer,
                         TpxFileHeader    *tpx,
                         gmx_mtop_t       *mtop)
@@ -2847,7 +2875,20 @@ static void do_tpx_mtop(gmx::ISerializer *serializer,
         }
     }
 }
-
+/*! \brief
+ * Process coordinate vectors for state data.
+ *
+ * Main part of state gets processed here.
+ *
+ * See the documentation for do_tpx_body for the correct order of
+ * the operations for reading a tpr file.
+ *
+ * \param[in] serializer Abstract serializer used to read/write data.
+ * \param[in] tpx The file header data.
+ * \param[in,out] state Global state data.
+ * \param[in,out] x Individual coordinates for processing, deprecated.
+ * \param[in,out] v Individual velocities for processing, deprecated.
+ */
 static void do_tpx_state_second(gmx::ISerializer *serializer,
                                 TpxFileHeader    *tpx,
                                 t_state          *state,
@@ -2922,7 +2963,16 @@ static void do_tpx_state_second(gmx::ISerializer *serializer,
         serializer->doRvecArray(as_rvec_array(dummyForces.data()), tpx->natoms);
     }
 }
-
+/*! \brief
+ * Process simulation parameters.
+ *
+ * See the documentation for do_tpx_body for the correct order of
+ * the operations for reading a tpr file.
+ *
+ * \param[in] serializer Abstract serializer used to read/write data.
+ * \param[in] tpx The file header data.
+ * \param[in,out] ir Datastructure with simulation parameters.
+ */
 static int do_tpx_ir(gmx::ISerializer *serializer,
                      TpxFileHeader    *tpx,
                      t_inputrec       *ir)
@@ -2976,7 +3026,10 @@ static int do_tpx_ir(gmx::ISerializer *serializer,
 /*! \brief
  * Correct and finalize read information.
  *
- * Moved here from previous code because this is done after reading files.
+ * If \p state is nullptr, skip the parts dependent on it.
+ *
+ * See the documentation for do_tpx_body for the correct order of
+ * the operations for reading a tpr file.
  *
  * \param[in] tpx The file header used to check version numbers.
  * \param[out] ir Input rec that needs correction.
@@ -2988,13 +3041,13 @@ static void do_tpx_finalize(TpxFileHeader *tpx,
                             t_state       *state,
                             gmx_mtop_t    *mtop)
 {
-    if (tpx->fileVersion < 51)
+    if (tpx->fileVersion < 51 && state)
     {
         set_box_rel(ir, state);
     }
     if (tpx->bIr && ir)
     {
-        if (state->ngtc == 0)
+        if (state && state->ngtc == 0)
         {
             /* Reading old version without tcoupl state data: set it */
             init_gtc_state(state, ir->opts.ngtc, 0, ir->opts.nhchainlength);
@@ -3016,6 +3069,26 @@ static void do_tpx_finalize(TpxFileHeader *tpx,
     }
 }
 
+/*! \brief
+ * Process TPR data for file reading/writing.
+ *
+ * The TPR file gets processed in in four stages due to the organization
+ * of the data within it.
+ *
+ * First, state data for the box is processed in do_tpx_state_first.
+ * This is followed by processing the topology in do_tpx_mtop.
+ * Coordinate and velocity vectors are handled next in do_tpx_state_second.
+ * The last file information processed is the collection of simulation parameters in do_tpx_ir.
+ * When reading, a final processing step is undertaken at the end.
+ *
+ * \param[in] serializer Abstract serializer used to read/write data.
+ * \param[in] tpx The file header data.
+ * \param[in,out] ir Datastructures with simulation parameters.
+ * \param[in,out] state Global state data.
+ * \param[in,out] x Individual coordinates for processing, deprecated.
+ * \param[in,out] v Individual velocities for processing, deprecated.
+ * \param[in,out] mtop Global topology.
+ */
 static int do_tpx_body(gmx::ISerializer *serializer,
                        TpxFileHeader    *tpx,
                        t_inputrec       *ir,
@@ -3024,9 +3097,15 @@ static int do_tpx_body(gmx::ISerializer *serializer,
                        rvec             *v,
                        gmx_mtop_t       *mtop)
 {
-    do_tpx_state_first(serializer, tpx, state);
+    if (state)
+    {
+        do_tpx_state_first(serializer, tpx, state);
+    }
     do_tpx_mtop(serializer, tpx, mtop);
-    do_tpx_state_second(serializer, tpx, state, x, v);
+    if (state)
+    {
+        do_tpx_state_second(serializer, tpx, state, x, v);
+    }
     int ePBC = do_tpx_ir(serializer, tpx, ir);
     if (serializer->reading())
     {
@@ -3035,6 +3114,22 @@ static int do_tpx_body(gmx::ISerializer *serializer,
     return ePBC;
 }
 
+/*! \brief
+ * Overload for do_tpx_body that defaults to state vectors being nullptr.
+ *
+ * \param[in] serializer Abstract serializer used to read/write data.
+ * \param[in] tpx The file header data.
+ * \param[in,out] ir Datastructures with simulation parameters.
+ * \param[in,out] mtop Global topology.
+ */
+static int do_tpx_body(gmx::ISerializer *serializer,
+                       TpxFileHeader    *tpx,
+                       t_inputrec       *ir,
+                       gmx_mtop_t       *mtop)
+{
+    return do_tpx_body(serializer, tpx, ir, nullptr, nullptr, nullptr, mtop);
+}
+
 static t_fileio *open_tpx(const char *fn, const char *mode)
 {
     return gmx_fio_open(fn, mode);
@@ -3073,13 +3168,100 @@ static TpxFileHeader populateTpxHeader(const t_state    &state,
     header.bBox           = true;
     header.fileVersion    = tpx_version;
     header.fileGeneration = tpx_generation;
+    header.isDouble       = (sizeof(real) == sizeof(double));
 
     return header;
 }
 
-static void doTpxBodyBuffer(gmx::ISerializer *topologySerializer, gmx::ArrayRef<char> buffer)
+/*! \brief
+ * Process the body of a TPR file as char buffer.
+ *
+ * Reads/writes the information in \p buffer from/to the \p serializer
+ * provided to the function. Does not interact with the actual
+ * TPR datastructures but with an in memory representation of the
+ * data, so that this data can be efficiently read or written from/to
+ * an original source.
+ *
+ * \param[in] serializer The abstract serializer used for reading or writing
+ *                       the information in \p buffer.
+ * \param[in,out] buffer Information from TPR file as char buffer.
+ */
+static void doTpxBodyBuffer(gmx::ISerializer *serializer, gmx::ArrayRef<char> buffer)
+{
+    serializer->doCharArray(buffer.data(), buffer.size());
+}
+
+/*! \brief
+ * Populates simulation datastructures.
+ *
+ * Here the information from the serialization interface \p serializer
+ * is used to first populate the datastructures containing the simulation
+ * information. Depending on the version found in the header \p tpx,
+ * this is done using the new reading of the data as one block from disk,
+ * followed by complete deserialization of the information read from there.
+ * Otherwise, the datastructures are populated as before one by one from disk.
+ * The second version is the default for the legacy tools that read the
+ * coordinates and velocities separate from the state.
+ *
+ * After reading in the data, a separate buffer is populated from them
+ * containing only \p ir and \p mtop that can be communicated directly
+ * to nodes needing the information to set up a simulation.
+ *
+ * \param[in] tpx The file header.
+ * \param[in] serializer The Serialization interface used to read the TPR.
+ * \param[out] ir Input rec to populate.
+ * \param[out] state State vectors to populate.
+ * \param[out] x Coordinates to populate if needed.
+ * \param[out] v Velocities to populate if needed.
+ * \param[out] mtop Global topology to populate.
+ *
+ * \returns Partial de-serialized TPR used for communication to nodes.
+ */
+static PartialDeserializedTprFile readTpxBody(TpxFileHeader *tpx,
+                                              gmx::ISerializer *serializer,
+                                              t_inputrec *ir,
+                                              t_state *state,
+                                              rvec *x, rvec *v,
+                                              gmx_mtop_t *mtop)
 {
-    topologySerializer->doCharArray(buffer.data(), buffer.size());
+    PartialDeserializedTprFile partialDeserializedTpr;
+    if (tpx->fileVersion >= tpxv_AddSizeField && tpx->fileGeneration >= 27)
+    {
+        partialDeserializedTpr.body.resize(tpx->sizeOfTprBody);
+        partialDeserializedTpr.header = *tpx;
+        doTpxBodyBuffer(serializer, partialDeserializedTpr.body);
+
+        partialDeserializedTpr.ePBC =
+            completeTprDeserialization(&partialDeserializedTpr,
+                                       ir,
+                                       state,
+                                       x, v,
+                                       mtop);
+    }
+    else
+    {
+        partialDeserializedTpr.ePBC =
+            do_tpx_body(serializer,
+                        tpx,
+                        ir,
+                        state,
+                        x,
+                        v,
+                        mtop);
+    }
+    // Update header to system info for communication to nodes.
+    // As we only need to communicate the inputrec and mtop to other nodes,
+    // we prepare a new char buffer with the information we have already read
+    // in on master.
+    partialDeserializedTpr.header = populateTpxHeader(*state, ir, mtop);
+    gmx::InMemorySerializer tprBodySerializer;
+    do_tpx_body(&tprBodySerializer,
+                &partialDeserializedTpr.header,
+                ir,
+                mtop);
+    partialDeserializedTpr.body = tprBodySerializer.finishAndGetBuffer();
+
+    return partialDeserializedTpr;
 }
 
 /************************************************************
@@ -3139,73 +3321,54 @@ void write_tpx_state(const char *fn,
     close_tpx(fio);
 }
 
-/*! \brief
- * Wraps reading of header before and after introduction of size field.
- *
- * \param[in] tpx The file header.
- * \param[in] serializer The Serialization interface used to read the TPR.
- * \param[in] isDouble Whether the file is double or single precision.
- * \param[out] ir Input rec to populate.
- * \param[out] state State vectors to populate.
- * \param[out] x Coordinates to populate if needed.
- * \param[out] v Velocities to populate if needed.
- * \param[out] mtop Global topology to populate.
- *
- * \returns Flag for pbc.
- */
-static int do_tpx_body_dispatcher(TpxFileHeader *tpx,
-                                  gmx::ISerializer *serializer,
-                                  bool isDouble,
-                                  t_inputrec *ir,
-                                  t_state *state,
-                                  rvec *x, rvec *v,
-                                  gmx_mtop_t *mtop)
+int completeTprDeserialization(PartialDeserializedTprFile *partialDeserializedTpr,
+                               t_inputrec                 *ir,
+                               t_state                    *state,
+                               rvec                       *x,
+                               rvec                       *v,
+                               gmx_mtop_t                 *mtop)
 {
-    int ePBC;
-    if (tpx->fileVersion >= tpxv_AddSizeField && tpx->fileGeneration >= 27)
-    {
-        std::vector<char>         tprBody(tpx->sizeOfTprBody);
-        doTpxBodyBuffer(serializer, tprBody);
-        gmx::InMemoryDeserializer tprBodyDeserializer(tprBody, isDouble);
-
-        ePBC = do_tpx_body(&tprBodyDeserializer,
-                           tpx,
-                           ir,
-                           state,
-                           x,
-                           v,
-                           mtop);
-    }
-    else
-    {
-        ePBC = do_tpx_body(serializer,
-                           tpx,
-                           ir,
-                           state,
-                           x,
-                           v,
-                           mtop);
-    }
-    return ePBC;
+    gmx::InMemoryDeserializer tprBodyDeserializer(partialDeserializedTpr->body,
+                                                  partialDeserializedTpr->header.isDouble);
+    return do_tpx_body(&tprBodyDeserializer,
+                       &partialDeserializedTpr->header,
+                       ir,
+                       state,
+                       x,
+                       v,
+                       mtop);
 }
 
-
-void read_tpx_state(const char *fn,
-                    t_inputrec *ir, t_state *state, gmx_mtop_t *mtop)
+int completeTprDeserialization(PartialDeserializedTprFile *partialDeserializedTpr,
+                               t_inputrec                 *ir,
+                               gmx_mtop_t                 *mtop)
 {
-    t_fileio                *fio;
+    return completeTprDeserialization(partialDeserializedTpr, ir, nullptr, nullptr, nullptr, mtop);
+}
 
-    TpxFileHeader            tpx;
+PartialDeserializedTprFile read_tpx_state(const char *fn,
+                                          t_inputrec *ir,
+                                          t_state    *state,
+                                          gmx_mtop_t *mtop)
+{
+    t_fileio                   *fio;
     fio = open_tpx(fn, "r");
-    gmx::FileIOXdrSerializer serializer(fio);
+    gmx::FileIOXdrSerializer    serializer(fio);
+    PartialDeserializedTprFile  partialDeserializedTpr;
     do_tpxheader(&serializer,
-                 &tpx,
+                 &partialDeserializedTpr.header,
                  fn,
                  fio,
                  ir == nullptr);
-    do_tpx_body_dispatcher(&tpx, &serializer, gmx_fio_is_double(fio),
-                           ir, state, nullptr, nullptr, mtop);
+    partialDeserializedTpr = readTpxBody(&partialDeserializedTpr.header,
+                                         &serializer,
+                                         ir,
+                                         state,
+                                         nullptr,
+                                         nullptr,
+                                         mtop);
     close_tpx(fio);
+    return partialDeserializedTpr;
 }
 
 int read_tpx(const char *fn,
@@ -3214,7 +3377,6 @@ int read_tpx(const char *fn,
 {
     t_fileio                *fio;
     t_state                  state;
-    int                      ePBC;
 
     TpxFileHeader            tpx;
     fio     = open_tpx(fn, "r");
@@ -3224,8 +3386,9 @@ int read_tpx(const char *fn,
                  fn,
                  fio,
                  ir == nullptr);
-    ePBC = do_tpx_body_dispatcher(&tpx, &serializer, gmx_fio_is_double(fio),
-                                  ir, &state, x, v, mtop);
+    PartialDeserializedTprFile partialDeserializedTpr
+        = readTpxBody(&tpx, &serializer,
+                      ir, &state, x, v, mtop);
     close_tpx(fio);
     if (mtop != nullptr && natoms != nullptr)
     {
@@ -3235,7 +3398,7 @@ int read_tpx(const char *fn,
     {
         copy_mat(state.box, box);
     }
-    return ePBC;
+    return partialDeserializedTpr.ePBC;
 }
 
 int read_tpx_top(const char *fn,
index 4a08730c0a87d3392df1e1727064b7be68272d2e..8d4b008fb047178936277afd1bdebefc25598f4a 100644 (file)
 
 #include <cstdio>
 
+#include <vector>
+
 #include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/basedefinitions.h"
 #include "gromacs/utility/real.h"
 
@@ -88,6 +91,25 @@ struct TpxFileHeader
     int     fileVersion = 0;
     //! File generation.
     int     fileGeneration = 0;
+    //! If the tpr file was written in double precision.
+    bool    isDouble = false;
+};
+
+/*! \brief
+ * Contains the partly deserialized contents of a TPR file.
+ *
+ * Convenience struct that holds a fully deserialized TPR file header,
+ * and the body of the TPR file as char buffer that can be deserialized
+ * independently from the header.
+ */
+struct PartialDeserializedTprFile
+{
+    //! The file header.
+    TpxFileHeader     header;
+    //! The file body.
+    std::vector<char> body;
+    //! Flag for PBC needed by legacy implementation.
+    int               ePBC = -1;
 };
 
 /*
@@ -118,9 +140,51 @@ void write_tpx_state(const char *fn,
 /* Write a file, and close it again.
  */
 
-void read_tpx_state(const char *fn,
-                    t_inputrec *ir, t_state *state,
-                    gmx_mtop_t *mtop);
+/*! \brief
+ * Complete deserialization of TPR file into the individual data structures.
+ *
+ * If \p state is nullptr, only populates ir and mtop.
+ *
+ * \param[in] partialDeserializedTpr Struct with header and char buffer needed to populate system.
+ * \param[out] ir Input rec to populate.
+ * \param[out] state System state variables to populate.
+ * \param[out] x Separate vector for coordinates, deprecated.
+ * \param[out] v Separate vector for velocities, deprecated.
+ * \param[out] mtop Global topology to populate.
+ *
+ * \returns PBC flag.
+ */
+int completeTprDeserialization(PartialDeserializedTprFile *partialDeserializedTpr,
+                               t_inputrec                 *ir,
+                               t_state                    *state,
+                               rvec                       *x,
+                               rvec                       *v,
+                               gmx_mtop_t                 *mtop);
+
+//! Overload for final TPR deserialization when not using state vectors.
+int completeTprDeserialization(PartialDeserializedTprFile *partialDeserializedTpr,
+                               t_inputrec                 *ir,
+                               gmx_mtop_t                 *mtop);
+
+/*! \brief
+ * Read a file to set up a simulation and close it after reading.
+ *
+ * Main function used to initialize simulations. Reads the input \p fn
+ * to populate the \p state, \p ir and \p mtop needed to run a simulations.
+ *
+ * This function returns the partial deserialized TPR file
+ * that can then be communicated to set up non-master nodes to run simulations.
+ *
+ * \param[in] fn Input file name.
+ * \param[out] ir Input parameters to be set, or nullptr.
+ * \param[out] state State variables for the simulation.
+ * \param[out] mtop Global simulation topolgy.
+ * \returns Struct with header and body in char vector.
+ */
+PartialDeserializedTprFile read_tpx_state(const char *fn,
+                                          t_inputrec *ir,
+                                          t_state    *state,
+                                          gmx_mtop_t *mtop);
 
 /*! \brief
  * Read a file and close it again.
index 7d67cf00ff21e9e0e5fc66331941fa963edf4fdb..a62979aa79eea1d94cb96321e5a63429e24f9f05 100644 (file)
 
 #include "broadcaststructs.h"
 
-#include <cstring>
-
-#include <memory>
-
+#include "gromacs/fileio/tpxio.h"
 #include "gromacs/gmxlib/network.h"
-#include "gromacs/math/vec.h"
-#include "gromacs/mdlib/tgroup.h"
-#include "gromacs/mdtypes/awh_params.h"
 #include "gromacs/mdtypes/commrec.h"
-#include "gromacs/mdtypes/inputrec.h"
-#include "gromacs/mdtypes/md_enums.h"
-#include "gromacs/mdtypes/pull_params.h"
 #include "gromacs/mdtypes/state.h"
-#include "gromacs/topology/mtop_util.h"
-#include "gromacs/topology/symtab.h"
-#include "gromacs/topology/topology.h"
-#include "gromacs/utility/fatalerror.h"
-#include "gromacs/utility/inmemoryserializer.h"
-#include "gromacs/utility/keyvaluetree.h"
-#include "gromacs/utility/keyvaluetreeserializer.h"
-#include "gromacs/utility/smalloc.h"
-
-static void bc_cstring(const t_commrec *cr, char **s)
-{
-    int size = 0;
-
-    if (MASTER(cr) && *s != nullptr)
-    {
-        /* Size of the char buffer is string length + 1 for '\0' */
-        size = strlen(*s) + 1;
-    }
-    block_bc(cr, size);
-    if (size > 0)
-    {
-        if (!MASTER(cr))
-        {
-            srenew(*s, size);
-        }
-        nblock_bc(cr, size, *s);
-    }
-    else if (!MASTER(cr) && *s != nullptr)
-    {
-        sfree(*s);
-        *s = nullptr;
-    }
-}
-
-static void bc_string(const t_commrec *cr, t_symtab *symtab, char ***s)
-{
-    int handle;
-
-    if (MASTER(cr))
-    {
-        handle = lookup_symtab(symtab, *s);
-    }
-    block_bc(cr, handle);
-    if (!MASTER(cr))
-    {
-        *s = get_symtab_handle(symtab, handle);
-    }
-}
-
-static void bc_strings(const t_commrec *cr, t_symtab *symtab, int nr, char ****nm)
-{
-    int     i;
-    int    *handle;
-
-    snew(handle, nr);
-    if (MASTER(cr))
-    {
-        for (i = 0; (i < nr); i++)
-        {
-            handle[i] = lookup_symtab(symtab, (*nm)[i]);
-        }
-    }
-    nblock_bc(cr, nr, handle);
-
-    if (!MASTER(cr))
-    {
-        snew_bc(cr, *nm, nr);
-        for (i = 0; (i < nr); i++)
-        {
-            (*nm)[i] = get_symtab_handle(symtab, handle[i]);
-        }
-    }
-    sfree(handle);
-}
-
-static void bc_strings_container(const t_commrec      *cr,
-                                 t_symtab             *symtab,
-                                 int                   nr,
-                                 std::vector<char **> *nm)
-{
-    std::vector<int> handle;
-    if (MASTER(cr))
-    {
-        for (int i = 0; (i < nr); i++)
-        {
-            handle.emplace_back(lookup_symtab(symtab, (*nm)[i]));
-        }
-    }
-    block_bc(cr, nr);
-    nblock_abc(cr, nr, &handle);
-
-    if (!MASTER(cr))
-    {
-        nm->resize(nr);
-        for (int i = 0; (i < nr); i++)
-        {
-            (*nm)[i] = get_symtab_handle(symtab, handle[i]);
-        }
-    }
-}
-
-static void bc_strings_resinfo(const t_commrec *cr, t_symtab *symtab,
-                               int nr, t_resinfo *resinfo)
-{
-    int   i;
-    int  *handle;
-
-    snew(handle, nr);
-    if (MASTER(cr))
-    {
-        for (i = 0; (i < nr); i++)
-        {
-            handle[i] = lookup_symtab(symtab, resinfo[i].name);
-        }
-    }
-    nblock_bc(cr, nr, handle);
-
-    if (!MASTER(cr))
-    {
-        for (i = 0; (i < nr); i++)
-        {
-            resinfo[i].name = get_symtab_handle(symtab, handle[i]);
-        }
-    }
-    sfree(handle);
-}
-
-static void bc_symtab(const t_commrec *cr, t_symtab *symtab)
-{
-    int       i, nr, len;
-    t_symbuf *symbuf;
-
-    block_bc(cr, symtab->nr);
-    nr = symtab->nr;
-    snew_bc(cr, symtab->symbuf, 1);
-    symbuf          = symtab->symbuf;
-    symbuf->bufsize = nr;
-    snew_bc(cr, symbuf->buf, nr);
-    for (i = 0; i < nr; i++)
-    {
-        if (MASTER(cr))
-        {
-            len = strlen(symbuf->buf[i]) + 1;
-        }
-        block_bc(cr, len);
-        snew_bc(cr, symbuf->buf[i], len);
-        nblock_bc(cr, len, symbuf->buf[i]);
-    }
-}
-
-static void bc_block(const t_commrec *cr, t_block *block)
-{
-    block_bc(cr, block->nr);
-    snew_bc(cr, block->index, block->nr+1);
-    nblock_bc(cr, block->nr+1, block->index);
-}
-
-static void bc_blocka(const t_commrec *cr, t_blocka *block)
-{
-    block_bc(cr, block->nr);
-    snew_bc(cr, block->index, block->nr+1);
-    nblock_bc(cr, block->nr+1, block->index);
-    block_bc(cr, block->nra);
-    if (block->nra)
-    {
-        snew_bc(cr, block->a, block->nra);
-        nblock_bc(cr, block->nra, block->a);
-    }
-}
-
-static void bc_grps(const t_commrec *cr, gmx::ArrayRef<AtomGroupIndices> grps)
-{
-    for (auto &group : grps)
-    {
-        int size = group.size();
-        block_bc(cr, size);
-        nblock_abc(cr, size, &group);
-    }
-}
-
-static void bc_atoms(const t_commrec *cr, t_symtab *symtab, t_atoms *atoms)
-{
-    block_bc(cr, atoms->nr);
-    snew_bc(cr, atoms->atom, atoms->nr);
-    nblock_bc(cr, atoms->nr, atoms->atom);
-    bc_strings(cr, symtab, atoms->nr, &atoms->atomname);
-    block_bc(cr, atoms->nres);
-    snew_bc(cr, atoms->resinfo, atoms->nres);
-    nblock_bc(cr, atoms->nres, atoms->resinfo);
-    bc_strings_resinfo(cr, symtab, atoms->nres, atoms->resinfo);
-    /* QMMM requires atomtypes to be known on all nodes as well */
-    bc_strings(cr, symtab, atoms->nr, &atoms->atomtype);
-    bc_strings(cr, symtab, atoms->nr, &atoms->atomtypeB);
-}
-
-static void bc_groups(const t_commrec *cr, t_symtab *symtab,
-                      int natoms, SimulationGroups *groups)
-{
-    int n;
-
-    bc_grps(cr, groups->groups);
-    bc_strings_container(cr, symtab, groups->groupNames.size(), &groups->groupNames);
-    for (auto group : gmx::keysOf(groups->groups))
-    {
-        if (MASTER(cr))
-        {
-            if (!groups->groupNumbers[group].empty())
-            {
-                n = natoms;
-            }
-            else
-            {
-                n = 0;
-            }
-        }
-        block_bc(cr, n);
-        if (n != 0)
-        {
-            nblock_abc(cr, n, &groups->groupNumbers[group]);
-        }
-    }
-    if (debug)
-    {
-        fprintf(debug, "after bc_groups\n");
-    }
-}
 
 template <typename AllocatorType>
 static void bcastPaddedRVecVector(const t_commrec *cr, gmx::PaddedVector<gmx::RVec, AllocatorType> *v, int numAtoms)
@@ -327,562 +92,43 @@ void broadcastStateWithoutDynamics(const t_commrec *cr, t_state *state)
     }
 }
 
-static void bc_ilists(const t_commrec *cr, InteractionLists *ilist)
-{
-    int ftype;
-
-    /* Here we only communicate the non-zero length ilists */
-    if (MASTER(cr))
-    {
-        for (ftype = 0; ftype < F_NRE; ftype++)
-        {
-            if ((*ilist)[ftype].size() > 0)
-            {
-                block_bc(cr, ftype);
-                int nr = (*ilist)[ftype].size();
-                block_bc(cr, nr);
-                nblock_bc(cr, nr, (*ilist)[ftype].iatoms.data());
-            }
-        }
-        ftype = -1;
-        block_bc(cr, ftype);
-    }
-    else
-    {
-        for (ftype = 0; ftype < F_NRE; ftype++)
-        {
-            (*ilist)[ftype].iatoms.clear();
-        }
-        do
-        {
-            block_bc(cr, ftype);
-            if (ftype >= 0)
-            {
-                int nr;
-                block_bc(cr, nr);
-                (*ilist)[ftype].iatoms.resize(nr);
-                nblock_bc(cr, nr, (*ilist)[ftype].iatoms.data());
-            }
-        }
-        while (ftype >= 0);
-    }
-
-    if (debug)
-    {
-        fprintf(debug, "after bc_ilists\n");
-    }
-}
-
-static void bc_cmap(const t_commrec *cr, gmx_cmap_t *cmap_grid)
-{
-    int ngrid = cmap_grid->cmapdata.size();
-    block_bc(cr, ngrid);
-    block_bc(cr, cmap_grid->grid_spacing);
-
-    int nelem = cmap_grid->grid_spacing * cmap_grid->grid_spacing;
-
-    if (ngrid > 0)
-    {
-        if (!MASTER(cr))
-        {
-            cmap_grid->cmapdata.resize(ngrid);
-        }
-
-        for (int i = 0; i < ngrid; i++)
-        {
-            nblock_abc(cr, 4*nelem, &cmap_grid->cmapdata[i].cmap);
-        }
-    }
-}
-
-static void bc_ffparams(const t_commrec *cr, gmx_ffparams_t *ffp)
-{
-    int numTypes = ffp->numTypes();
-    block_bc(cr, numTypes);
-    block_bc(cr, ffp->atnr);
-    nblock_abc(cr, numTypes, &ffp->functype);
-    nblock_abc(cr, numTypes, &ffp->iparams);
-    block_bc(cr, ffp->reppow);
-    block_bc(cr, ffp->fudgeQQ);
-    bc_cmap(cr, &ffp->cmap_grid);
-}
-
-static void bc_grpopts(const t_commrec *cr, t_grpopts *g)
-{
-    int i, n;
-
-    block_bc(cr, g->ngtc);
-    block_bc(cr, g->ngacc);
-    block_bc(cr, g->ngfrz);
-    block_bc(cr, g->ngener);
-    snew_bc(cr, g->nrdf, g->ngtc);
-    snew_bc(cr, g->tau_t, g->ngtc);
-    snew_bc(cr, g->ref_t, g->ngtc);
-    snew_bc(cr, g->acc, g->ngacc);
-    snew_bc(cr, g->nFreeze, g->ngfrz);
-    snew_bc(cr, g->egp_flags, g->ngener*g->ngener);
-
-    nblock_bc(cr, g->ngtc, g->nrdf);
-    nblock_bc(cr, g->ngtc, g->tau_t);
-    nblock_bc(cr, g->ngtc, g->ref_t);
-    nblock_bc(cr, g->ngacc, g->acc);
-    nblock_bc(cr, g->ngfrz, g->nFreeze);
-    nblock_bc(cr, g->ngener*g->ngener, g->egp_flags);
-    snew_bc(cr, g->annealing, g->ngtc);
-    snew_bc(cr, g->anneal_npoints, g->ngtc);
-    snew_bc(cr, g->anneal_time, g->ngtc);
-    snew_bc(cr, g->anneal_temp, g->ngtc);
-    nblock_bc(cr, g->ngtc, g->annealing);
-    nblock_bc(cr, g->ngtc, g->anneal_npoints);
-    for (i = 0; (i < g->ngtc); i++)
-    {
-        n = g->anneal_npoints[i];
-        if (n > 0)
-        {
-            snew_bc(cr, g->anneal_time[i], n);
-            snew_bc(cr, g->anneal_temp[i], n);
-            nblock_bc(cr, n, g->anneal_time[i]);
-            nblock_bc(cr, n, g->anneal_temp[i]);
-        }
-    }
-
-    /* QMMM stuff, see inputrec */
-    block_bc(cr, g->ngQM);
-    snew_bc(cr, g->QMmethod, g->ngQM);
-    snew_bc(cr, g->QMbasis, g->ngQM);
-    snew_bc(cr, g->QMcharge, g->ngQM);
-    snew_bc(cr, g->QMmult, g->ngQM);
-    snew_bc(cr, g->bSH, g->ngQM);
-    snew_bc(cr, g->CASorbitals, g->ngQM);
-    snew_bc(cr, g->CASelectrons, g->ngQM);
-    snew_bc(cr, g->SAon, g->ngQM);
-    snew_bc(cr, g->SAoff, g->ngQM);
-    snew_bc(cr, g->SAsteps, g->ngQM);
-
-    if (g->ngQM)
-    {
-        nblock_bc(cr, g->ngQM, g->QMmethod);
-        nblock_bc(cr, g->ngQM, g->QMbasis);
-        nblock_bc(cr, g->ngQM, g->QMcharge);
-        nblock_bc(cr, g->ngQM, g->QMmult);
-        nblock_bc(cr, g->ngQM, g->bSH);
-        nblock_bc(cr, g->ngQM, g->CASorbitals);
-        nblock_bc(cr, g->ngQM, g->CASelectrons);
-        nblock_bc(cr, g->ngQM, g->SAon);
-        nblock_bc(cr, g->ngQM, g->SAoff);
-        nblock_bc(cr, g->ngQM, g->SAsteps);
-        /* end of QMMM stuff */
-    }
-}
-
-static void bc_awhBias(const t_commrec *cr, gmx::AwhBiasParams *awhBiasParams)
-{
-    block_bc(cr, *awhBiasParams);
-
-    snew_bc(cr, awhBiasParams->dimParams, awhBiasParams->ndim);
-    nblock_bc(cr, awhBiasParams->ndim, awhBiasParams->dimParams);
-}
-
-static void bc_awh(const t_commrec *cr, gmx::AwhParams *awhParams)
-{
-    int k;
-
-    block_bc(cr, *awhParams);
-    snew_bc(cr, awhParams->awhBiasParams, awhParams->numBias);
-    for (k = 0; k < awhParams->numBias; k++)
-    {
-        bc_awhBias(cr, &awhParams->awhBiasParams[k]);
-    }
-}
-
-static void bc_pull_group(const t_commrec *cr, t_pull_group *pgrp)
-{
-    block_bc(cr, *pgrp);
-    if (pgrp->nat > 0)
-    {
-        snew_bc(cr, pgrp->ind, pgrp->nat);
-        nblock_bc(cr, pgrp->nat, pgrp->ind);
-    }
-    if (pgrp->nweight > 0)
-    {
-        snew_bc(cr, pgrp->weight, pgrp->nweight);
-        nblock_bc(cr, pgrp->nweight, pgrp->weight);
-    }
-}
-
-static void bc_pull(const t_commrec *cr, pull_params_t *pull)
-{
-    int g;
-
-    block_bc(cr, *pull);
-    snew_bc(cr, pull->group, pull->ngroup);
-    for (g = 0; g < pull->ngroup; g++)
-    {
-        bc_pull_group(cr, &pull->group[g]);
-    }
-    snew_bc(cr, pull->coord, pull->ncoord);
-    nblock_bc(cr, pull->ncoord, pull->coord);
-    for (int c = 0; c < pull->ncoord; c++)
-    {
-        if (!MASTER(cr))
-        {
-            pull->coord[c].externalPotentialProvider = nullptr;
-        }
-        if (pull->coord[c].eType == epullEXTERNAL)
-        {
-            bc_cstring(cr, &pull->coord[c].externalPotentialProvider);
-        }
-    }
-}
-
-static void bc_rotgrp(const t_commrec *cr, t_rotgrp *rotg)
-{
-    block_bc(cr, *rotg);
-    if (rotg->nat > 0)
-    {
-        snew_bc(cr, rotg->ind, rotg->nat);
-        nblock_bc(cr, rotg->nat, rotg->ind);
-        snew_bc(cr, rotg->x_ref, rotg->nat);
-        nblock_bc(cr, rotg->nat, rotg->x_ref);
-    }
-}
-
-static void bc_rot(const t_commrec *cr, t_rot *rot)
-{
-    int g;
-
-    block_bc(cr, *rot);
-    snew_bc(cr, rot->grp, rot->ngrp);
-    for (g = 0; g < rot->ngrp; g++)
-    {
-        bc_rotgrp(cr, &rot->grp[g]);
-    }
-}
-
-static void bc_imd(const t_commrec *cr, t_IMD *imd)
-{
-    block_bc(cr, *imd);
-    snew_bc(cr, imd->ind, imd->nat);
-    nblock_bc(cr, imd->nat, imd->ind);
-}
-
-static void bc_fepvals(const t_commrec *cr, t_lambda *fep)
-{
-    int      i;
-
-    block_bc(cr, fep->nstdhdl);
-    block_bc(cr, fep->init_lambda);
-    block_bc(cr, fep->init_fep_state);
-    block_bc(cr, fep->delta_lambda);
-    block_bc(cr, fep->edHdLPrintEnergy);
-    block_bc(cr, fep->n_lambda);
-    if (fep->n_lambda > 0)
-    {
-        snew_bc(cr, fep->all_lambda, efptNR);
-        nblock_bc(cr, efptNR, fep->all_lambda);
-        for (i = 0; i < efptNR; i++)
-        {
-            snew_bc(cr, fep->all_lambda[i], fep->n_lambda);
-            nblock_bc(cr, fep->n_lambda, fep->all_lambda[i]);
-        }
-    }
-    block_bc(cr, fep->sc_alpha);
-    block_bc(cr, fep->sc_power);
-    block_bc(cr, fep->sc_r_power);
-    block_bc(cr, fep->sc_sigma);
-    block_bc(cr, fep->sc_sigma_min);
-    block_bc(cr, fep->bScCoul);
-    nblock_bc(cr, efptNR, &(fep->separate_dvdl[0]));
-    block_bc(cr, fep->dhdl_derivatives);
-    block_bc(cr, fep->dh_hist_size);
-    block_bc(cr, fep->dh_hist_spacing);
-    if (debug)
-    {
-        fprintf(debug, "after bc_fepvals\n");
-    }
-}
-
-static void bc_expandedvals(const t_commrec *cr, t_expanded *expand, int n_lambda)
-{
-    block_bc(cr, expand->nstexpanded);
-    block_bc(cr, expand->elamstats);
-    block_bc(cr, expand->elmcmove);
-    block_bc(cr, expand->elmceq);
-    block_bc(cr, expand->equil_n_at_lam);
-    block_bc(cr, expand->equil_wl_delta);
-    block_bc(cr, expand->equil_ratio);
-    block_bc(cr, expand->equil_steps);
-    block_bc(cr, expand->equil_samples);
-    block_bc(cr, expand->lmc_seed);
-    block_bc(cr, expand->minvar);
-    block_bc(cr, expand->minvar_const);
-    block_bc(cr, expand->c_range);
-    block_bc(cr, expand->bSymmetrizedTMatrix);
-    block_bc(cr, expand->nstTij);
-    block_bc(cr, expand->lmc_repeats);
-    block_bc(cr, expand->lmc_forced_nstart);
-    block_bc(cr, expand->gibbsdeltalam);
-    block_bc(cr, expand->wl_scale);
-    block_bc(cr, expand->wl_ratio);
-    block_bc(cr, expand->init_wl_delta);
-    block_bc(cr, expand->bInit_weights);
-    snew_bc(cr, expand->init_lambda_weights, n_lambda);
-    nblock_bc(cr, n_lambda, expand->init_lambda_weights);
-    block_bc(cr, expand->mc_temp);
-    if (debug)
-    {
-        fprintf(debug, "after bc_expandedvals\n");
-    }
-}
-
-static void bc_simtempvals(const t_commrec *cr, t_simtemp *simtemp, int n_lambda)
-{
-    block_bc(cr, simtemp->simtemp_low);
-    block_bc(cr, simtemp->simtemp_high);
-    block_bc(cr, simtemp->eSimTempScale);
-    snew_bc(cr, simtemp->temperatures, n_lambda);
-    nblock_bc(cr, n_lambda, simtemp->temperatures);
-    if (debug)
-    {
-        fprintf(debug, "after bc_simtempvals\n");
-    }
-}
-
-
-static void bc_swapions(const t_commrec *cr, t_swapcoords *swap)
-{
-    block_bc(cr, *swap);
-
-    /* Broadcast atom indices for split groups, solvent group, and for all user-defined swap groups */
-    snew_bc(cr, swap->grp, swap->ngrp);
-    for (int i = 0; i < swap->ngrp; i++)
-    {
-        t_swapGroup *g = &swap->grp[i];
-
-        block_bc(cr, *g);
-        snew_bc(cr, g->ind, g->nat);
-        nblock_bc(cr, g->nat, g->ind);
-
-        int len = 0;
-        if (MASTER(cr))
-        {
-            len = strlen(g->molname);
-        }
-        block_bc(cr, len);
-        snew_bc(cr, g->molname, len);
-        nblock_bc(cr, len, g->molname);
-    }
-}
-
-
-static void bc_inputrec(const t_commrec *cr, t_inputrec *inputrec)
+static void bc_tpxheader(const t_commrec *cr, TpxFileHeader *tpx)
 {
-    // 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);
-    if (SIMMASTER(cr))
-    {
-        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);
-        nblock_bc(cr, size, buffer.data());
-    }
-    else
-    {
-        // 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;
-        size_t            size;
-        block_bc(cr, size);
-        nblock_abc(cr, size, &buffer);
-        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));
-
-    /* even if efep is efepNO, we need to initialize to make sure that
-     * n_lambda is set to zero */
-
-    snew_bc(cr, inputrec->fepvals, 1);
-    if (inputrec->efep != efepNO || inputrec->bSimTemp)
-    {
-        bc_fepvals(cr, inputrec->fepvals);
-    }
-    /* need to initialize this as well because of data checked for in the logic */
-    snew_bc(cr, inputrec->expandedvals, 1);
-    if (inputrec->bExpanded)
-    {
-        bc_expandedvals(cr, inputrec->expandedvals, inputrec->fepvals->n_lambda);
-    }
-    snew_bc(cr, inputrec->simtempvals, 1);
-    if (inputrec->bSimTemp)
-    {
-        bc_simtempvals(cr, inputrec->simtempvals, inputrec->fepvals->n_lambda);
-    }
-    if (inputrec->bPull)
-    {
-        snew_bc(cr, inputrec->pull, 1);
-        bc_pull(cr, inputrec->pull);
-    }
-    if (inputrec->bDoAwh)
-    {
-        snew_bc(cr, inputrec->awhParams, 1);
-        bc_awh(cr, inputrec->awhParams);
-    }
-
-    if (inputrec->bRot)
-    {
-        snew_bc(cr, inputrec->rot, 1);
-        bc_rot(cr, inputrec->rot);
-    }
-    if (inputrec->bIMD)
-    {
-        snew_bc(cr, inputrec->imd, 1);
-        bc_imd(cr, inputrec->imd);
-    }
-    if (inputrec->eSwapCoords != eswapNO)
-    {
-        snew_bc(cr, inputrec->swap, 1);
-        bc_swapions(cr, inputrec->swap);
-    }
+    block_bc(cr, tpx->bIr);
+    block_bc(cr, tpx->bBox);
+    block_bc(cr, tpx->bTop);
+    block_bc(cr, tpx->bX);
+    block_bc(cr, tpx->bV);
+    block_bc(cr, tpx->bF);
+    block_bc(cr, tpx->natoms);
+    block_bc(cr, tpx->ngtc);
+    block_bc(cr, tpx->lambda);
+    block_bc(cr, tpx->fep_state);
+    block_bc(cr, tpx->sizeOfTprBody);
+    block_bc(cr, tpx->fileVersion);
+    block_bc(cr, tpx->fileGeneration);
+    block_bc(cr, tpx->isDouble);
 }
 
-static void bc_moltype(const t_commrec *cr, t_symtab *symtab,
-                       gmx_moltype_t *moltype)
+static void bc_tprCharBuffer(const t_commrec *cr, std::vector<char> *charBuffer)
 {
-    bc_string(cr, symtab, &moltype->name);
-    bc_atoms(cr, symtab, &moltype->atoms);
-    if (debug)
-    {
-        fprintf(debug, "after bc_atoms\n");
-    }
+    int elements = charBuffer->size();
+    block_bc(cr, elements);
 
-    bc_ilists(cr, &moltype->ilist);
-    bc_block(cr, &moltype->cgs);
-    bc_blocka(cr, &moltype->excls);
+    nblock_abc(cr, elements, charBuffer);
 }
 
-static void bc_vector_of_rvec(const t_commrec *cr, std::vector<gmx::RVec> *vec)
+void init_parallel(t_commrec                  *cr,
+                   t_inputrec                 *inputrec,
+                   gmx_mtop_t                 *mtop,
+                   PartialDeserializedTprFile *partialDeserializedTpr)
 {
-    int numElements = vec->size();
-    block_bc(cr, numElements);
+    bc_tpxheader(cr, &partialDeserializedTpr->header);
+    bc_tprCharBuffer(cr, &partialDeserializedTpr->body);
     if (!MASTER(cr))
     {
-        vec->resize(numElements);
-    }
-    if (numElements > 0)
-    {
-        nblock_bc(cr, numElements, as_rvec_array(vec->data()));
-    }
-}
-
-static void bc_molblock(const t_commrec *cr, gmx_molblock_t *molb)
-{
-    block_bc(cr, molb->type);
-    block_bc(cr, molb->nmol);
-    bc_vector_of_rvec(cr, &molb->posres_xA);
-    bc_vector_of_rvec(cr, &molb->posres_xB);
-    if (debug)
-    {
-        fprintf(debug, "after bc_molblock\n");
+        completeTprDeserialization(partialDeserializedTpr,
+                                   inputrec,
+                                   mtop);
     }
 }
-
-static void bc_atomtypes(const t_commrec *cr, t_atomtypes *atomtypes)
-{
-    block_bc(cr, atomtypes->nr);
-}
-
-/*! \brief Broadcasts ir and mtop from the master to all nodes in
- * cr->mpi_comm_mygroup. */
-static
-void bcast_ir_mtop(const t_commrec *cr, t_inputrec *inputrec, gmx_mtop_t *mtop)
-{
-    if (debug)
-    {
-        fprintf(debug, "in bc_data\n");
-    }
-    bc_inputrec(cr, inputrec);
-    if (debug)
-    {
-        fprintf(debug, "after bc_inputrec\n");
-    }
-    bc_symtab(cr, &mtop->symtab);
-    if (debug)
-    {
-        fprintf(debug, "after bc_symtab\n");
-    }
-    bc_string(cr, &mtop->symtab, &mtop->name);
-    if (debug)
-    {
-        fprintf(debug, "after bc_name\n");
-    }
-
-    bc_ffparams(cr, &mtop->ffparams);
-
-    int nmoltype = mtop->moltype.size();
-    block_bc(cr, nmoltype);
-    mtop->moltype.resize(nmoltype);
-    for (gmx_moltype_t &moltype : mtop->moltype)
-    {
-        bc_moltype(cr, &mtop->symtab, &moltype);
-    }
-
-    block_bc(cr, mtop->bIntermolecularInteractions);
-    if (mtop->bIntermolecularInteractions)
-    {
-        if (!MASTER(cr))
-        {
-            mtop->intermolecular_ilist = std::make_unique<InteractionLists>();
-        }
-        bc_ilists(cr, mtop->intermolecular_ilist.get());
-    }
-
-    int nmolblock = mtop->molblock.size();
-    block_bc(cr, nmolblock);
-    mtop->molblock.resize(nmolblock);
-    for (gmx_molblock_t &molblock : mtop->molblock)
-    {
-        bc_molblock(cr, &molblock);
-    }
-
-    block_bc(cr, mtop->natoms);
-
-    bc_atomtypes(cr, &mtop->atomtypes);
-
-    bc_groups(cr, &mtop->symtab, mtop->natoms, &mtop->groups);
-
-    GMX_RELEASE_ASSERT(!MASTER(cr) || mtop->haveMoleculeIndices, "mtop should have valid molecule indices");
-    if (!MASTER(cr))
-    {
-        mtop->haveMoleculeIndices = true;
-
-        gmx_mtop_finalize(mtop);
-    }
-}
-
-void init_parallel(t_commrec *cr, t_inputrec *inputrec,
-                   gmx_mtop_t *mtop)
-{
-    bcast_ir_mtop(cr, inputrec, mtop);
-}
index e4273078ba9fb85a04025084c8b8aaec5f65298e..3f8003d77b343d06cd65f64dac07765d95653c81 100644 (file)
@@ -55,6 +55,7 @@
 struct gmx_mtop_t;
 struct t_commrec;
 struct t_inputrec;
+struct PartialDeserializedTprFile;
 class t_state;
 
 //! Convenience wrapper for gmx_bcast of a single value.
@@ -109,7 +110,9 @@ void nblock_abc(const t_commrec *cr, int numElements, std::vector<T> *v)
 void broadcastStateWithoutDynamics(const t_commrec *cr, t_state *state);
 
 //! \brief Broadcast inputrec and mtop and allocate node-specific settings
-void init_parallel(t_commrec *cr, t_inputrec *inputrec,
-                   gmx_mtop_t *mtop);
+void init_parallel(t_commrec                  *cr,
+                   t_inputrec                 *inputrec,
+                   gmx_mtop_t                 *mtop,
+                   PartialDeserializedTprFile *partialDeserializedTpr);
 
 #endif
index a46ded32e14d489521aed16db10bdf607933661b..67d5ec5bf5f0567ba1e6fdf3c291875726baba34 100644 (file)
@@ -696,15 +696,18 @@ int Mdrunner::mdrunner()
     t_inputrec              *inputrec = nullptr;
     std::unique_ptr<t_state> globalState;
 
+    auto                     partialDeserializedTpr = std::make_unique<PartialDeserializedTprFile>();
+
     if (SIMMASTER(cr))
     {
         /* Only the master rank has the global state */
         globalState = std::make_unique<t_state>();
 
-        /* Read (nearly) all data required for the simulation */
-        read_tpx_state(ftp2fn(efTPR, filenames.size(), filenames.data()),
-                       &inputrecInstance, globalState.get(), &mtop);
-        inputrec = &inputrecInstance;
+        /* Read (nearly) all data required for the simulation
+         * and keep the partly serialized tpr contents to send to other ranks later
+         */
+        *partialDeserializedTpr = read_tpx_state(ftp2fn(efTPR, filenames.size(), filenames.data()), &inputrecInstance, globalState.get(), &mtop);
+        inputrec                = &inputrecInstance;
     }
 
     /* Check and update the hardware options for internal consistency */
@@ -766,9 +769,10 @@ int Mdrunner::mdrunner()
         {
             inputrec = &inputrecInstance;
         }
-        init_parallel(cr, inputrec, &mtop);
+        init_parallel(cr, inputrec, &mtop, partialDeserializedTpr.get());
     }
-    GMX_RELEASE_ASSERT(inputrec != nullptr, "All range should have a valid inputrec now");
+    GMX_RELEASE_ASSERT(inputrec != nullptr, "All ranks should have a valid inputrec now");
+    partialDeserializedTpr.reset(nullptr);
 
     // Now each rank knows the inputrec that SIMMASTER read and used,
     // and (if applicable) cr->nnodes has been assigned the number of
index f22bf37de2626a36aba9bd5623c1b7926cf4233d..d607c9849ce64a2704ddc7ba5015277152226a38 100644 (file)
@@ -191,7 +191,7 @@ void InMemorySerializer::doString(std::string *value)
 class InMemoryDeserializer::Impl
 {
     public:
-        explicit Impl(const std::vector<char> &buffer, bool sourceIsDouble)
+        explicit Impl(ArrayRef<const char> buffer, bool sourceIsDouble)
             : buffer_(buffer), sourceIsDouble_(sourceIsDouble), pos_(0)
         {
         }
@@ -210,12 +210,12 @@ class InMemoryDeserializer::Impl
             pos_  += size;
         }
 
-        const std::vector<char> &buffer_;
+        ArrayRef<const char>     buffer_;
         bool                     sourceIsDouble_;
         size_t                   pos_;
 };
 
-InMemoryDeserializer::InMemoryDeserializer(const std::vector<char> &buffer, bool sourceIsDouble)
+InMemoryDeserializer::InMemoryDeserializer(ArrayRef<const char> buffer, bool sourceIsDouble)
     : impl_(new Impl(buffer, sourceIsDouble))
 {
 }
index a053e4198da66fe641ad25f6b6e9343ff6128ab9..6e89b68091128ad69dd060a91a5e86a7667a43f2 100644 (file)
@@ -45,6 +45,7 @@
 
 #include <vector>
 
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/classhelpers.h"
 #include "gromacs/utility/iserializer.h"
 
@@ -84,7 +85,7 @@ class InMemorySerializer : public ISerializer
 class InMemoryDeserializer : public ISerializer
 {
     public:
-        explicit InMemoryDeserializer(const std::vector<char> &buffer, bool sourceIsDouble);
+        explicit InMemoryDeserializer(ArrayRef<const char> buffer, bool sourceIsDouble);
         ~InMemoryDeserializer() override;
 
         //! Get if the source data was written in double precsion