Also use DD partitioning in serial
authorBerk Hess <hess@kth.se>
Fri, 24 Sep 2021 19:24:23 +0000 (19:24 +0000)
committerMark Abraham <mark.j.abraham@gmail.com>
Fri, 24 Sep 2021 19:24:23 +0000 (19:24 +0000)
15 files changed:
src/gromacs/domdec/domdec.cpp
src/gromacs/domdec/domdec_network.cpp
src/gromacs/domdec/domdec_struct.h
src/gromacs/domdec/localtopologychecker.cpp
src/gromacs/domdec/localtopologychecker.h
src/gromacs/gmxlib/network.cpp
src/gromacs/imd/imd.cpp
src/gromacs/mdlib/constr.cpp
src/gromacs/mdlib/sim_util.cpp
src/gromacs/mdrun/minimize.cpp
src/gromacs/mdrun/runner.cpp
src/gromacs/mdrun/shellfc.cpp
src/gromacs/mdtypes/commrec.h
src/gromacs/nbnxm/gridset.cpp
src/gromacs/taskassignment/resourcedivision.cpp

index f4ddcc70eeb077242840b56b7f2ed27aadbd2d30..8a634b7c07594c99bb43fb46e62557beaeacab4e 100644 (file)
@@ -1063,7 +1063,7 @@ static void make_load_communicator(gmx_domdec_t* dd, int dim_ind, ivec loc)
 void dd_setup_dlb_resource_sharing(const t_commrec* cr, int gpu_id)
 {
 #if GMX_MPI
-    MPI_Comm mpi_comm_pp_physicalnode = MPI_COMM_NULL;
+    gmx_domdec_t* dd = cr->dd;
 
     if (!thisRankHasDuty(cr, DUTY_PP) || gpu_id < 0)
     {
@@ -1073,9 +1073,14 @@ void dd_setup_dlb_resource_sharing(const t_commrec* cr, int gpu_id)
         return;
     }
 
-    const int physicalnode_id_hash = gmx_physicalnode_id_hash();
+    if (cr->nnodes == 1)
+    {
+        dd->comm->nrank_gpu_shared = 1;
 
-    gmx_domdec_t* dd = cr->dd;
+        return;
+    }
+
+    const int physicalnode_id_hash = gmx_physicalnode_id_hash();
 
     if (debug)
     {
@@ -1088,6 +1093,7 @@ void dd_setup_dlb_resource_sharing(const t_commrec* cr, int gpu_id)
      */
     // TODO PhysicalNodeCommunicator could be extended/used to handle
     // the need for per-node per-group communicators.
+    MPI_Comm mpi_comm_pp_physicalnode;
     MPI_Comm_split(dd->mpi_comm_all, physicalnode_id_hash, dd->rank, &mpi_comm_pp_physicalnode);
     MPI_Comm_split(mpi_comm_pp_physicalnode, gpu_id, dd->rank, &dd->comm->mpi_comm_gpu_shared);
     MPI_Comm_free(&mpi_comm_pp_physicalnode);
@@ -1528,6 +1534,7 @@ static CartesianRankSetup split_communicator(const gmx::MDLogger& mdlog,
                        getThisRankDuties(cr),
                        dd_index(cartSetup.ntot, ddCellIndex),
                        &cr->mpi_comm_mygroup);
+        MPI_Comm_size(cr->mpi_comm_mygroup, &cr->sizeOfMyGroupCommunicator);
 #else
         GMX_UNUSED_VALUE(ddCellIndex);
 #endif
@@ -1559,6 +1566,7 @@ static CartesianRankSetup split_communicator(const gmx::MDLogger& mdlog,
 #if GMX_MPI
         /* Split the sim communicator into PP and PME only nodes */
         MPI_Comm_split(cr->mpi_comm_mysim, getThisRankDuties(cr), cr->nodeid, &cr->mpi_comm_mygroup);
+        MPI_Comm_size(cr->mpi_comm_mygroup, &cr->sizeOfMyGroupCommunicator);
         MPI_Comm_rank(cr->mpi_comm_mygroup, &cr->nodeid);
 #endif
     }
@@ -1628,14 +1636,18 @@ static void setupGroupCommunication(const gmx::MDLogger&     mdlog,
 
     if (thisRankHasDuty(cr, DUTY_PP))
     {
-        /* Copy or make a new PP communicator */
+        if (dd->nnodes > 1)
+        {
+            /* Copy or make a new PP communicator */
 
-        /* We (possibly) reordered the nodes in split_communicator,
-         * so it is no longer required in make_pp_communicator.
-         */
-        const bool useCartesianReorder = (ddSettings.useCartesianReorder && !cartSetup.bCartesianPP_PME);
+            /* We (possibly) reordered the nodes in split_communicator,
+             * so it is no longer required in make_pp_communicator.
+             */
+            const bool useCartesianReorder =
+                    (ddSettings.useCartesianReorder && !cartSetup.bCartesianPP_PME);
 
-        make_pp_communicator(mdlog, dd, cr, useCartesianReorder);
+            make_pp_communicator(mdlog, dd, cr, useCartesianReorder);
+        }
     }
     else
     {
index b5c01f85e073535c4cf80593732ba09085864d2f..ead1f5282ee2cc0e190968d744b7d828effddfd1 100644 (file)
@@ -280,9 +280,16 @@ void dd_gather(const gmx_domdec_t gmx_unused* dd,
                void gmx_unused* dest)
 {
 #if GMX_MPI
-    /* Some MPI implementions don't specify const */
-    MPI_Gather(const_cast<void*>(src), nbytes, MPI_BYTE, dest, nbytes, MPI_BYTE, DDMASTERRANK(dd), dd->mpi_comm_all);
+    if (dd->nnodes > 1)
+    {
+        /* Some MPI implementions don't specify const */
+        MPI_Gather(const_cast<void*>(src), nbytes, MPI_BYTE, dest, nbytes, MPI_BYTE, DDMASTERRANK(dd), dd->mpi_comm_all);
+    }
+    else
 #endif
+    {
+        memcpy(dest, src, nbytes);
+    }
 }
 
 void dd_scatterv(const gmx_domdec_t gmx_unused* dd,
@@ -325,15 +332,22 @@ void dd_gatherv(const gmx_domdec_t gmx_unused* dd,
                 void gmx_unused* rbuf)
 {
 #if GMX_MPI
-    int dum = 0;
-
-    if (scount == 0)
+    if (dd->nnodes > 1)
     {
-        /* MPI does not allow NULL pointers */
-        sbuf = &dum;
+        int dum;
+
+        if (scount == 0)
+        {
+            /* MPI does not allow NULL pointers */
+            sbuf = &dum;
+        }
+        /* Some MPI implementions don't specify const */
+        MPI_Gatherv(
+                const_cast<void*>(sbuf), scount, MPI_BYTE, rbuf, rcounts, disps, MPI_BYTE, DDMASTERRANK(dd), dd->mpi_comm_all);
     }
-    /* Some MPI implementions don't specify const */
-    MPI_Gatherv(
-            const_cast<void*>(sbuf), scount, MPI_BYTE, rbuf, rcounts, disps, MPI_BYTE, DDMASTERRANK(dd), dd->mpi_comm_all);
+    else
 #endif
+    {
+        memcpy(rbuf, sbuf, rcounts[0]);
+    }
 }
index 613a62e4665b47e429f837dc9dcf386e3aa7fa77..911f1e7333f6fc67f1d8fb24ffc9db5597e1c275 100644 (file)
@@ -170,7 +170,7 @@ struct gmx_domdec_t
     /* The communication setup within the communicator all
      * defined in dd->comm in domdec.c
      */
-    int      nnodes       = 0;
+    int      nnodes       = 1;
     MPI_Comm mpi_comm_all = MPI_COMM_NULL;
     /* The local DD cell index and rank */
     gmx::IVec ci         = { 0, 0, 0 };
index 8db5e261be92d565b3bc36a21b1c55b253ea4268..9fabbc10bf1a29d3e3d3d828d733a1052db963a4 100644 (file)
@@ -480,25 +480,36 @@ LocalTopologyChecker& LocalTopologyChecker::operator=(LocalTopologyChecker&& oth
 
 void LocalTopologyChecker::scheduleCheckOfLocalTopology(const int numBondedInteractionsToReduce)
 {
-    // Fill the reduction buffer with the value from this domain to reduce
-    impl_->reductionBuffer_[0] = double(numBondedInteractionsToReduce);
-
-    // Pass the post-reduction callback to the ObservablesReducer via
-    // the callback it gave us for the purpose.
-    //
-    // Note that it's possible that the callbackAfterReduction is already
-    // outstanding, e.g. if repartitioning was triggered before
-    // observables were reduced. This could happen for example when
-    // replica exchange took place soon after a partition. If so, the
-    // callback will be called again. So long as there is no race
-    // between the calls to this function and the calls to
-    // ObservablesReducer for reduction, this will work correctly. It
-    // could be made safer e.g. with checks against duplicate
-    // callbacks, but there is no problem to solve.
-    //
-    // There is no need to check the return value from this callback,
-    // as it is not an error to request reduction at a future step.
-    impl_->callbackToRequireReduction_(ReductionRequirement::Eventually);
+    // When we have a single domain, we don't need to reduce and we algorithmically can not miss
+    // any interactions, so we can assert here.
+    if (!havePPDomainDecomposition(impl_->cr_))
+    {
+        GMX_RELEASE_ASSERT(numBondedInteractionsToReduce == impl_->expectedNumGlobalBondedInteractions_,
+                           "With a single domain the number of assigned bonded interactions should "
+                           "always match the global number");
+    }
+    else
+    {
+        // Fill the reduction buffer with the value from this domain to reduce
+        impl_->reductionBuffer_[0] = double(numBondedInteractionsToReduce);
+
+        // Pass the post-reduction callback to the ObservablesReducer via
+        // the callback it gave us for the purpose.
+        //
+        // Note that it's possible that the callbackAfterReduction is already
+        // outstanding, e.g. if repartitioning was triggered before
+        // observables were reduced. This could happen for example when
+        // replica exchange took place soon after a partition. If so, the
+        // callback will be called again. So long as there is no race
+        // between the calls to this function and the calls to
+        // ObservablesReducer for reduction, this will work correctly. It
+        // could be made safer e.g. with checks against duplicate
+        // callbacks, but there is no problem to solve.
+        //
+        // There is no need to check the return value from this callback,
+        // as it is not an error to request reduction at a future step.
+        impl_->callbackToRequireReduction_(ReductionRequirement::Eventually);
+    }
 }
 
 } // namespace gmx
index 253879021b589287c7f8418bddb0d74879c16df7..5ea7d634e2ff3ca01046a595ccf1a3fbe72234e2 100644 (file)
@@ -106,7 +106,9 @@ public:
 
     /*! \brief Set that the local topology should be checked via
      * observables reduction whenever that reduction is required by
-     * another module. */
+     * another module. In case of a single domain a direct assertion
+     * is performed instead.
+     */
     void scheduleCheckOfLocalTopology(int numBondedInteractionsToReduce);
 
 private:
index eec49f099aa39b63045483c94dbd0e615bb7988c..c5edd4eb51eea884d5dc6f9ff190f4a0a5abae60 100644 (file)
@@ -87,11 +87,12 @@ CommrecHandle init_commrec(MPI_Comm communicator)
 
     // For now, we want things to go horribly wrong if this is used too early...
     // TODO: Remove when communicators are removed from commrec (#2395)
-    cr->nnodes           = -1;
-    cr->nodeid           = -1;
-    cr->sim_nodeid       = -1;
-    cr->mpi_comm_mysim   = MPI_COMM_NULL;
-    cr->mpi_comm_mygroup = MPI_COMM_NULL;
+    cr->nnodes                    = -1;
+    cr->sizeOfMyGroupCommunicator = -1;
+    cr->nodeid                    = -1;
+    cr->sim_nodeid                = -1;
+    cr->mpi_comm_mysim            = MPI_COMM_NULL;
+    cr->mpi_comm_mygroup          = MPI_COMM_NULL;
 
     // TODO cr->duty should not be initialized here
     cr->duty = (DUTY_PP | DUTY_PME);
@@ -221,6 +222,10 @@ void gmx_setup_nodecomm(FILE gmx_unused* fplog, t_commrec* cr)
 
 void gmx_barrier(MPI_Comm gmx_unused communicator)
 {
+    if (communicator == MPI_COMM_NULL)
+    {
+        return;
+    }
 #if !GMX_MPI
     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_barrier");
 #else
@@ -239,6 +244,11 @@ void gmx_bcast(int gmx_unused nbytes, void gmx_unused* b, MPI_Comm gmx_unused co
 
 void gmx_sumd(int gmx_unused nr, double gmx_unused r[], const t_commrec gmx_unused* cr)
 {
+    if (cr->sizeOfMyGroupCommunicator == 1)
+    {
+        return;
+    }
+
 #if !GMX_MPI
     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_sumd");
 #else
@@ -268,6 +278,11 @@ void gmx_sumd(int gmx_unused nr, double gmx_unused r[], const t_commrec gmx_unus
 
 void gmx_sumf(int gmx_unused nr, float gmx_unused r[], const t_commrec gmx_unused* cr)
 {
+    if (cr->sizeOfMyGroupCommunicator == 1)
+    {
+        return;
+    }
+
 #if !GMX_MPI
     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_sumf");
 #else
@@ -297,6 +312,11 @@ void gmx_sumf(int gmx_unused nr, float gmx_unused r[], const t_commrec gmx_unuse
 
 void gmx_sumi(int gmx_unused nr, int gmx_unused r[], const t_commrec gmx_unused* cr)
 {
+    if (cr->sizeOfMyGroupCommunicator == 1)
+    {
+        return;
+    }
+
 #if !GMX_MPI
     GMX_RELEASE_ASSERT(false, "Invalid call to gmx_sumi");
 #else
index 7050b67956f87a67b0a987c5667cb64cf063452d..7d0cef345cc04697934f42cdbd15d35524d1b2dc 100644 (file)
@@ -1268,7 +1268,7 @@ void ImdSession::Impl::prepareForPositionAssembly(const t_commrec* cr, gmx::Arra
         }
     }
 
-    if (!PAR(cr))
+    if (!DOMAINDECOMP(cr))
     {
         nat_loc = nat;
         ind_loc = ind;
@@ -1281,7 +1281,7 @@ void ImdSession::Impl::prepareForPositionAssembly(const t_commrec* cr, gmx::Arra
     }
 
     /* Communicate initial coordinates xa_old to all processes */
-    if (PAR(cr))
+    if (cr && havePPDomainDecomposition(cr))
     {
         gmx_bcast(nat * sizeof(xa_old[0]), xa_old, cr->mpi_comm_mygroup);
     }
index 17fa1b19ff77b5009dfcec74f74c41c3e1a4738a..255dfb77a3528121ad8fa4a17f6cb018155e445f 100644 (file)
@@ -489,7 +489,7 @@ bool Constraints::Impl::apply(bool                      bLog,
     /* Communicate the coordinates required for the non-local constraints
      * for LINCS and/or SETTLE.
      */
-    if (cr->dd)
+    if (havePPDomainDecomposition(cr))
     {
         dd_move_x_constraints(cr->dd,
                               box,
index 9b7affec5eecd654a972b30af4408422d9baeced..429b91c6b14497d44c9a9f760267047f3f912a0f 100644 (file)
@@ -2127,8 +2127,9 @@ void do_force(FILE*                               fplog,
     // With both nonbonded and PME offloaded a GPU on the same rank, we use
     // an alternating wait/reduction scheme.
     bool alternateGpuWait =
-            (!c_disableAlternatingWait && stepWork.haveGpuPmeOnThisRank
-             && simulationWork.useGpuNonbonded && !DOMAINDECOMP(cr) && !stepWork.useGpuFBufferOps);
+            (!c_disableAlternatingWait && stepWork.haveGpuPmeOnThisRank && simulationWork.useGpuNonbonded
+             && !simulationWork.havePpDomainDecomposition && !stepWork.useGpuFBufferOps);
+
     if (alternateGpuWait)
     {
         alternatePmeNbGpuWaitReduce(fr->nbv.get(),
index 07e9d1c7e538a2e5730ea896e8e9571e6451f638..27a7bf1fea371909c42b0eb81dae30c99796c514 100644 (file)
@@ -1975,6 +1975,10 @@ void LegacySimulator::do_lbfgs()
                     "be available in a different form in a future version of GROMACS, "
                     "e.g. gmx minimize and an .mdp option.");
 
+    if (DOMAINDECOMP(cr))
+    {
+        gmx_fatal(FARGS, "L_BFGS is currently not supported");
+    }
     if (PAR(cr))
     {
         gmx_fatal(FARGS, "L-BFGS minimization only supports a single rank");
index 17db7f419bdfc5a14de39586087c03375584b880..d409b82136c434a6951334d5bf55d53fed9a49a7 100644 (file)
@@ -974,12 +974,17 @@ int Mdrunner::mdrunner()
             "Linear acceleration has been removed in GROMACS 2022, and was broken for many years "
             "before that. Use GROMACS 4.5 or earlier if you need this feature.");
 
+    // Now we decide whether to use the domain decomposition machinery.
+    // Note that this does not necessarily imply actually using multiple domains.
     // Now the number of ranks is known to all ranks, and each knows
     // the inputrec read by the master rank. The ranks can now all run
     // the task-deciding functions and will agree on the result
     // without needing to communicate.
+    // The LBFGS minimizer, test-particle insertion, normal modes and shell dynamics don't support DD
     const bool useDomainDecomposition =
-            (PAR(cr) && !(EI_TPI(inputrec->eI) || inputrec->eI == IntegrationAlgorithm::NM));
+            !(inputrec->eI == IntegrationAlgorithm::LBFGS || EI_TPI(inputrec->eI)
+              || inputrec->eI == IntegrationAlgorithm::NM
+              || gmx_mtop_particletype_count(mtop)[ParticleType::Shell] > 0);
 
     // Note that these variables describe only their own node.
     //
@@ -1502,7 +1507,7 @@ int Mdrunner::mdrunner()
 
     if (deviceInfo != nullptr)
     {
-        if (DOMAINDECOMP(cr) && thisRankHasDuty(cr, DUTY_PP))
+        if (runScheduleWork.simulationWork.havePpDomainDecomposition && thisRankHasDuty(cr, DUTY_PP))
         {
             dd_setup_dlb_resource_sharing(cr, deviceId);
         }
index 1284b62fb5187d95cc4a705c80b113fe677680c9..7f15b180bc8bbe9d811eb54b6d7ced370665eebf 100644 (file)
@@ -287,7 +287,7 @@ gmx_shellfc_t* init_shell_flexcon(FILE*             fplog,
                   "not supported in combination with shell particles.\nPlease make a new tpr file.",
                   nstcalcenergy);
     }
-    if (usingDomainDecomposition)
+    if (nshell > 0 && usingDomainDecomposition)
     {
         gmx_fatal(
                 FARGS,
index 9213187567f21ef625c1cd5d5e2120ea2f620ab8..0307de3d5f7a315ad8301a36c4aedd266ce27c6f 100644 (file)
@@ -77,11 +77,14 @@ struct t_commrec
      * All communication within some simulation should happen
      * in mpi_comm_mysim, or its subset mpi_comm_mygroup.
      */
-    int sim_nodeid, nnodes, npmenodes;
-
-    /* thread numbers: */
-    /* Not used yet: int threadid, nthreads; */
-    /* The nodeid in the PP/PME, PP or PME group */
+    //! The rank-id in mpi_comm_mysim;
+    int sim_nodeid;
+    //! The number of ranks in mpi_comm_mysim
+    int nnodes;
+    //! The number of separate PME ranks, 0 when no separate PME ranks are used
+    int npmenodes;
+
+    //! The rank-id in mpi_comm_mygroup;
     int nodeid;
 
     /* MPI communicators within a single simulation
@@ -91,6 +94,9 @@ struct t_commrec
                                   a single simulation */
     MPI_Comm mpi_comm_mygroup; /* subset of mpi_comm_mysim including only
                                   the ranks in the same group (PP or PME) */
+    //! The number of ranks in mpi_comm_mygroup
+    int sizeOfMyGroupCommunicator;
+
     //! The communicator used before DD was initialized
     MPI_Comm mpiDefaultCommunicator;
     int      sizeOfDefaultCommunicator;
@@ -152,21 +158,16 @@ inline bool thisRankHasDuty(const t_commrec* cr, int duty)
 //! The node id for the master
 #define MASTERRANK(cr) (0)
 
-/*! \brief Do we decompose the work of this simulation?
- *
- * True if this simulation uses more than one PP rank, or if this simulation
- * uses at least one PME-only rank.
+/*! \brief Returns whether the domain decomposition machinery is active
  *
- * PAR(cr) is true if this is true, but the converse does not apply (see docs
- * of PAR(cr)).
- *
- * This is true if havePPDomainDecomposition is true, but the converse does not
- * apply (see docs of havePpDomainDecomposition()).
- *
- * \todo As part of Issue #2395, replace calls to this with
- * havePPDomainDecomposition or a call of some other/new function, as
- * appropriate to each case. Then eliminate this macro. */
-#define DOMAINDECOMP(cr) (((cr)->dd != nullptr) && PAR(cr))
+ * Note that when the return value is true, there are not necessarily
+ * multiple domains. The domain decomposition machinery is also active and
+ * reorders the atoms also with a single MPI rank, or 1 PP and 1 PME rank,
+ * with most integrators. Only a few special non-integrator "integrators"
+ * do not (yet) support the domain decomposition machinery and therefore
+ * this macro is still needed.
+ */
+#define DOMAINDECOMP(cr) ((cr)->dd != nullptr)
 
 /*! \brief Returns whether we have actual domain decomposition for the particle-particle interactions
  *
index c3a1a7ec269539e73cc67d35fcadf95ac075206d..a301092e368aa1b9eb91179327f61d35d248330e 100644 (file)
@@ -83,7 +83,8 @@ GridSet::DomainSetup::DomainSetup(const PbcType             pbcType,
                                   const gmx_domdec_zones_t* ddZones) :
     pbcType(pbcType),
     doTestParticleInsertion(doTestParticleInsertion),
-    haveMultipleDomains(numDDCells != nullptr),
+    haveMultipleDomains(numDDCells != nullptr
+                        && (*numDDCells)[XX] * (*numDDCells)[YY] * (*numDDCells)[ZZ] > 1),
     zones(ddZones)
 {
     for (int d = 0; d < DIM; d++)
index 42c5b64a06b8fe793d9abddda2279ecd7807903e..2108d1e7bdeadf76ba4f5c561ce60d1aa0298584 100644 (file)
@@ -617,7 +617,7 @@ void check_resource_division_efficiency(const gmx_hw_info_t* hwinfo,
         nthreads_omp_mpi_ok_min = nthreads_omp_mpi_ok_min_gpu;
     }
 
-    if (DOMAINDECOMP(cr))
+    if (cr && cr->nnodes > 1)
     {
         if (nth_omp_max < nthreads_omp_mpi_ok_min || nth_omp_max > nthreads_omp_mpi_ok_max)
         {