2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
53 #include "gromacs/domdec/collect.h"
54 #include "gromacs/domdec/dlb.h"
55 #include "gromacs/domdec/dlbtiming.h"
56 #include "gromacs/domdec/domdec_network.h"
57 #include "gromacs/domdec/ga2la.h"
58 #include "gromacs/domdec/options.h"
59 #include "gromacs/domdec/partition.h"
60 #include "gromacs/gmxlib/network.h"
61 #include "gromacs/gmxlib/nrnb.h"
62 #include "gromacs/gpu_utils/gpu_utils.h"
63 #include "gromacs/hardware/hw_info.h"
64 #include "gromacs/listed_forces/manage_threading.h"
65 #include "gromacs/math/vec.h"
66 #include "gromacs/math/vectypes.h"
67 #include "gromacs/mdlib/calc_verletbuf.h"
68 #include "gromacs/mdlib/constr.h"
69 #include "gromacs/mdlib/constraintrange.h"
70 #include "gromacs/mdlib/updategroups.h"
71 #include "gromacs/mdlib/vsite.h"
72 #include "gromacs/mdtypes/commrec.h"
73 #include "gromacs/mdtypes/forceoutput.h"
74 #include "gromacs/mdtypes/inputrec.h"
75 #include "gromacs/mdtypes/mdrunoptions.h"
76 #include "gromacs/mdtypes/state.h"
77 #include "gromacs/pbcutil/ishift.h"
78 #include "gromacs/pbcutil/pbc.h"
79 #include "gromacs/pulling/pull.h"
80 #include "gromacs/timing/wallcycle.h"
81 #include "gromacs/topology/block.h"
82 #include "gromacs/topology/idef.h"
83 #include "gromacs/topology/ifunc.h"
84 #include "gromacs/topology/mtop_lookup.h"
85 #include "gromacs/topology/mtop_util.h"
86 #include "gromacs/topology/topology.h"
87 #include "gromacs/utility/basedefinitions.h"
88 #include "gromacs/utility/basenetwork.h"
89 #include "gromacs/utility/cstringutil.h"
90 #include "gromacs/utility/exceptions.h"
91 #include "gromacs/utility/fatalerror.h"
92 #include "gromacs/utility/gmxmpi.h"
93 #include "gromacs/utility/logger.h"
94 #include "gromacs/utility/real.h"
95 #include "gromacs/utility/smalloc.h"
96 #include "gromacs/utility/strconvert.h"
97 #include "gromacs/utility/stringstream.h"
98 #include "gromacs/utility/stringutil.h"
99 #include "gromacs/utility/textwriter.h"
101 #include "atomdistribution.h"
103 #include "cellsizes.h"
104 #include "distribute.h"
105 #include "domdec_constraints.h"
106 #include "domdec_internal.h"
107 #include "domdec_vsite.h"
108 #include "redistribute.h"
111 // TODO remove this when moving domdec into gmx namespace
112 using gmx::DdRankOrder;
113 using gmx::DlbOption;
114 using gmx::DomdecOptions;
116 static const char *edlbs_names[int(DlbState::nr)] = { "off", "auto", "locked", "on", "on" };
118 /* The size per atom group of the cggl_flag buffer in gmx_domdec_comm_t */
121 /* The flags for the cggl_flag buffer in gmx_domdec_comm_t */
122 #define DD_FLAG_NRCG 65535
123 #define DD_FLAG_FW(d) (1<<(16+(d)*2))
124 #define DD_FLAG_BW(d) (1<<(16+(d)*2+1))
126 /* The DD zone order */
127 static const ivec dd_zo[DD_MAXZONE] =
128 {{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}, {0, 1, 1}, {0, 0, 1}, {1, 0, 1}, {1, 1, 1}};
130 /* The non-bonded zone-pair setup for domain decomposition
131 * The first number is the i-zone, the second number the first j-zone seen by
132 * this i-zone, the third number the last+1 j-zone seen by this i-zone.
133 * As is, this is for 3D decomposition, where there are 4 i-zones.
134 * With 2D decomposition use only the first 2 i-zones and a last+1 j-zone of 4.
135 * With 1D decomposition use only the first i-zone and a last+1 j-zone of 2.
138 ddNonbondedZonePairRanges[DD_MAXIZONE][3] = {{0, 0, 8},
145 #define dd_index(n,i) ((((i)[ZZ]*(n)[YY] + (i)[YY])*(n)[XX]) + (i)[XX])
147 static void index2xyz(ivec nc,int ind,ivec xyz)
149 xyz[XX] = ind % nc[XX];
150 xyz[YY] = (ind / nc[XX]) % nc[YY];
151 xyz[ZZ] = ind / (nc[YY]*nc[XX]);
155 static void ddindex2xyz(const ivec nc, int ind, ivec xyz)
157 xyz[XX] = ind / (nc[YY]*nc[ZZ]);
158 xyz[YY] = (ind / nc[ZZ]) % nc[YY];
159 xyz[ZZ] = ind % nc[ZZ];
162 static int ddcoord2ddnodeid(gmx_domdec_t *dd, ivec c)
166 const DDRankSetup &ddRankSetup = dd->comm->ddRankSetup;
167 const int ddindex = dd_index(dd->nc, c);
168 if (ddRankSetup.bCartesianPP_PME)
170 ddnodeid = ddRankSetup.ddindex2ddnodeid[ddindex];
172 else if (ddRankSetup.bCartesianPP)
175 MPI_Cart_rank(dd->mpi_comm_all, c, &ddnodeid);
186 int ddglatnr(const gmx_domdec_t *dd, int i)
196 if (i >= dd->comm->atomRanges.numAtomsTotal())
198 gmx_fatal(FARGS, "glatnr called with %d, which is larger than the local number of atoms (%d)", i, dd->comm->atomRanges.numAtomsTotal());
200 atnr = dd->globalAtomIndices[i] + 1;
206 t_block *dd_charge_groups_global(gmx_domdec_t *dd)
208 return &dd->comm->cgs_gl;
211 gmx::ArrayRef<const gmx::RangePartitioning> getUpdateGroupingPerMoleculetype(const gmx_domdec_t &dd)
213 GMX_RELEASE_ASSERT(dd.comm, "Need a valid dd.comm");
214 return dd.comm->systemInfo.updateGroupingPerMoleculetype;
217 void dd_store_state(gmx_domdec_t *dd, t_state *state)
221 if (state->ddp_count != dd->ddp_count)
223 gmx_incons("The MD state does not match the domain decomposition state");
226 state->cg_gl.resize(dd->ncg_home);
227 for (i = 0; i < dd->ncg_home; i++)
229 state->cg_gl[i] = dd->globalAtomGroupIndices[i];
232 state->ddp_count_cg_gl = dd->ddp_count;
235 gmx_domdec_zones_t *domdec_zones(gmx_domdec_t *dd)
237 return &dd->comm->zones;
240 void dd_get_ns_ranges(const gmx_domdec_t *dd, int icg,
241 int *jcg0, int *jcg1, ivec shift0, ivec shift1)
243 gmx_domdec_zones_t *zones;
246 zones = &dd->comm->zones;
249 while (icg >= zones->izone[izone].cg1)
258 else if (izone < zones->nizone)
260 *jcg0 = zones->izone[izone].jcg0;
264 gmx_fatal(FARGS, "DD icg %d out of range: izone (%d) >= nizone (%d)",
265 icg, izone, zones->nizone);
268 *jcg1 = zones->izone[izone].jcg1;
270 for (d = 0; d < dd->ndim; d++)
273 shift0[dim] = zones->izone[izone].shift0[dim];
274 shift1[dim] = zones->izone[izone].shift1[dim];
275 if (dd->comm->tric_dir[dim] || (isDlbOn(dd->comm) && d > 0))
277 /* A conservative approach, this can be optimized */
284 int dd_numHomeAtoms(const gmx_domdec_t &dd)
286 return dd.comm->atomRanges.numHomeAtoms();
289 int dd_natoms_mdatoms(const gmx_domdec_t *dd)
291 /* We currently set mdatoms entries for all atoms:
292 * local + non-local + communicated for vsite + constraints
295 return dd->comm->atomRanges.numAtomsTotal();
298 int dd_natoms_vsite(const gmx_domdec_t *dd)
300 return dd->comm->atomRanges.end(DDAtomRanges::Type::Vsites);
303 void dd_get_constraint_range(const gmx_domdec_t *dd, int *at_start, int *at_end)
305 *at_start = dd->comm->atomRanges.start(DDAtomRanges::Type::Constraints);
306 *at_end = dd->comm->atomRanges.end(DDAtomRanges::Type::Constraints);
309 void dd_move_x(gmx_domdec_t *dd,
311 gmx::ArrayRef<gmx::RVec> x,
312 gmx_wallcycle *wcycle)
314 wallcycle_start(wcycle, ewcMOVEX);
317 gmx_domdec_comm_t *comm;
318 gmx_domdec_comm_dim_t *cd;
319 rvec shift = {0, 0, 0};
320 gmx_bool bPBC, bScrew;
325 nat_tot = comm->atomRanges.numHomeAtoms();
326 for (int d = 0; d < dd->ndim; d++)
328 bPBC = (dd->ci[dd->dim[d]] == 0);
329 bScrew = (bPBC && dd->unitCellInfo.haveScrewPBC && dd->dim[d] == XX);
332 copy_rvec(box[dd->dim[d]], shift);
335 for (const gmx_domdec_ind_t &ind : cd->ind)
337 DDBufferAccess<gmx::RVec> sendBufferAccess(comm->rvecBuffer, ind.nsend[nzone + 1]);
338 gmx::ArrayRef<gmx::RVec> &sendBuffer = sendBufferAccess.buffer;
342 for (int j : ind.index)
344 sendBuffer[n] = x[j];
350 for (int j : ind.index)
352 /* We need to shift the coordinates */
353 for (int d = 0; d < DIM; d++)
355 sendBuffer[n][d] = x[j][d] + shift[d];
362 for (int j : ind.index)
365 sendBuffer[n][XX] = x[j][XX] + shift[XX];
367 * This operation requires a special shift force
368 * treatment, which is performed in calc_vir.
370 sendBuffer[n][YY] = box[YY][YY] - x[j][YY];
371 sendBuffer[n][ZZ] = box[ZZ][ZZ] - x[j][ZZ];
376 DDBufferAccess<gmx::RVec> receiveBufferAccess(comm->rvecBuffer2, cd->receiveInPlace ? 0 : ind.nrecv[nzone + 1]);
378 gmx::ArrayRef<gmx::RVec> receiveBuffer;
379 if (cd->receiveInPlace)
381 receiveBuffer = gmx::arrayRefFromArray(x.data() + nat_tot, ind.nrecv[nzone + 1]);
385 receiveBuffer = receiveBufferAccess.buffer;
387 /* Send and receive the coordinates */
388 ddSendrecv(dd, d, dddirBackward,
389 sendBuffer, receiveBuffer);
391 if (!cd->receiveInPlace)
394 for (int zone = 0; zone < nzone; zone++)
396 for (int i = ind.cell2at0[zone]; i < ind.cell2at1[zone]; i++)
398 x[i] = receiveBuffer[j++];
402 nat_tot += ind.nrecv[nzone+1];
407 wallcycle_stop(wcycle, ewcMOVEX);
410 void dd_move_f(gmx_domdec_t *dd,
411 gmx::ForceWithShiftForces *forceWithShiftForces,
412 gmx_wallcycle *wcycle)
414 wallcycle_start(wcycle, ewcMOVEF);
416 gmx::ArrayRef<gmx::RVec> f = forceWithShiftForces->force();
417 gmx::ArrayRef<gmx::RVec> fshift = forceWithShiftForces->shiftForces();
419 gmx_domdec_comm_t &comm = *dd->comm;
420 int nzone = comm.zones.n/2;
421 int nat_tot = comm.atomRanges.end(DDAtomRanges::Type::Zones);
422 for (int d = dd->ndim-1; d >= 0; d--)
424 /* Only forces in domains near the PBC boundaries need to
425 consider PBC in the treatment of fshift */
426 const bool shiftForcesNeedPbc = (forceWithShiftForces->computeVirial() && dd->ci[dd->dim[d]] == 0);
427 const bool applyScrewPbc = (shiftForcesNeedPbc && dd->unitCellInfo.haveScrewPBC && dd->dim[d] == XX);
428 /* Determine which shift vector we need */
429 ivec vis = { 0, 0, 0 };
431 const int is = IVEC2IS(vis);
433 /* Loop over the pulses */
434 const gmx_domdec_comm_dim_t &cd = comm.cd[d];
435 for (int p = cd.numPulses() - 1; p >= 0; p--)
437 const gmx_domdec_ind_t &ind = cd.ind[p];
438 DDBufferAccess<gmx::RVec> receiveBufferAccess(comm.rvecBuffer, ind.nsend[nzone + 1]);
439 gmx::ArrayRef<gmx::RVec> &receiveBuffer = receiveBufferAccess.buffer;
441 nat_tot -= ind.nrecv[nzone+1];
443 DDBufferAccess<gmx::RVec> sendBufferAccess(comm.rvecBuffer2, cd.receiveInPlace ? 0 : ind.nrecv[nzone + 1]);
445 gmx::ArrayRef<gmx::RVec> sendBuffer;
446 if (cd.receiveInPlace)
448 sendBuffer = gmx::arrayRefFromArray(f.data() + nat_tot, ind.nrecv[nzone + 1]);
452 sendBuffer = sendBufferAccess.buffer;
454 for (int zone = 0; zone < nzone; zone++)
456 for (int i = ind.cell2at0[zone]; i < ind.cell2at1[zone]; i++)
458 sendBuffer[j++] = f[i];
462 /* Communicate the forces */
463 ddSendrecv(dd, d, dddirForward,
464 sendBuffer, receiveBuffer);
465 /* Add the received forces */
467 if (!shiftForcesNeedPbc)
469 for (int j : ind.index)
471 for (int d = 0; d < DIM; d++)
473 f[j][d] += receiveBuffer[n][d];
478 else if (!applyScrewPbc)
480 for (int j : ind.index)
482 for (int d = 0; d < DIM; d++)
484 f[j][d] += receiveBuffer[n][d];
486 /* Add this force to the shift force */
487 for (int d = 0; d < DIM; d++)
489 fshift[is][d] += receiveBuffer[n][d];
496 for (int j : ind.index)
498 /* Rotate the force */
499 f[j][XX] += receiveBuffer[n][XX];
500 f[j][YY] -= receiveBuffer[n][YY];
501 f[j][ZZ] -= receiveBuffer[n][ZZ];
502 if (shiftForcesNeedPbc)
504 /* Add this force to the shift force */
505 for (int d = 0; d < DIM; d++)
507 fshift[is][d] += receiveBuffer[n][d];
516 wallcycle_stop(wcycle, ewcMOVEF);
519 /* Convenience function for extracting a real buffer from an rvec buffer
521 * To reduce the number of temporary communication buffers and avoid
522 * cache polution, we reuse gmx::RVec buffers for storing reals.
523 * This functions return a real buffer reference with the same number
524 * of elements as the gmx::RVec buffer (so 1/3 of the size in bytes).
526 static gmx::ArrayRef<real>
527 realArrayRefFromRvecArrayRef(gmx::ArrayRef<gmx::RVec> arrayRef)
529 return gmx::arrayRefFromArray(as_rvec_array(arrayRef.data())[0],
533 void dd_atom_spread_real(gmx_domdec_t *dd, real v[])
536 gmx_domdec_comm_t *comm;
537 gmx_domdec_comm_dim_t *cd;
542 nat_tot = comm->atomRanges.numHomeAtoms();
543 for (int d = 0; d < dd->ndim; d++)
546 for (const gmx_domdec_ind_t &ind : cd->ind)
548 /* Note: We provision for RVec instead of real, so a factor of 3
549 * more than needed. The buffer actually already has this size
550 * and we pass a plain pointer below, so this does not matter.
552 DDBufferAccess<gmx::RVec> sendBufferAccess(comm->rvecBuffer, ind.nsend[nzone + 1]);
553 gmx::ArrayRef<real> sendBuffer = realArrayRefFromRvecArrayRef(sendBufferAccess.buffer);
555 for (int j : ind.index)
557 sendBuffer[n++] = v[j];
560 DDBufferAccess<gmx::RVec> receiveBufferAccess(comm->rvecBuffer2, cd->receiveInPlace ? 0 : ind.nrecv[nzone + 1]);
562 gmx::ArrayRef<real> receiveBuffer;
563 if (cd->receiveInPlace)
565 receiveBuffer = gmx::arrayRefFromArray(v + nat_tot, ind.nrecv[nzone + 1]);
569 receiveBuffer = realArrayRefFromRvecArrayRef(receiveBufferAccess.buffer);
571 /* Send and receive the data */
572 ddSendrecv(dd, d, dddirBackward,
573 sendBuffer, receiveBuffer);
574 if (!cd->receiveInPlace)
577 for (int zone = 0; zone < nzone; zone++)
579 for (int i = ind.cell2at0[zone]; i < ind.cell2at1[zone]; i++)
581 v[i] = receiveBuffer[j++];
585 nat_tot += ind.nrecv[nzone+1];
591 void dd_atom_sum_real(gmx_domdec_t *dd, real v[])
594 gmx_domdec_comm_t *comm;
595 gmx_domdec_comm_dim_t *cd;
599 nzone = comm->zones.n/2;
600 nat_tot = comm->atomRanges.end(DDAtomRanges::Type::Zones);
601 for (int d = dd->ndim-1; d >= 0; d--)
604 for (int p = cd->numPulses() - 1; p >= 0; p--)
606 const gmx_domdec_ind_t &ind = cd->ind[p];
608 /* Note: We provision for RVec instead of real, so a factor of 3
609 * more than needed. The buffer actually already has this size
610 * and we typecast, so this works as intended.
612 DDBufferAccess<gmx::RVec> receiveBufferAccess(comm->rvecBuffer, ind.nsend[nzone + 1]);
613 gmx::ArrayRef<real> receiveBuffer = realArrayRefFromRvecArrayRef(receiveBufferAccess.buffer);
614 nat_tot -= ind.nrecv[nzone + 1];
616 DDBufferAccess<gmx::RVec> sendBufferAccess(comm->rvecBuffer2, cd->receiveInPlace ? 0 : ind.nrecv[nzone + 1]);
618 gmx::ArrayRef<real> sendBuffer;
619 if (cd->receiveInPlace)
621 sendBuffer = gmx::arrayRefFromArray(v + nat_tot, ind.nrecv[nzone + 1]);
625 sendBuffer = realArrayRefFromRvecArrayRef(sendBufferAccess.buffer);
627 for (int zone = 0; zone < nzone; zone++)
629 for (int i = ind.cell2at0[zone]; i < ind.cell2at1[zone]; i++)
631 sendBuffer[j++] = v[i];
635 /* Communicate the forces */
636 ddSendrecv(dd, d, dddirForward,
637 sendBuffer, receiveBuffer);
638 /* Add the received forces */
640 for (int j : ind.index)
642 v[j] += receiveBuffer[n];
650 real dd_cutoff_multibody(const gmx_domdec_t *dd)
652 const gmx_domdec_comm_t &comm = *dd->comm;
653 const DDSystemInfo &systemInfo = comm.systemInfo;
656 if (systemInfo.haveInterDomainMultiBodyBondeds)
658 if (comm.cutoff_mbody > 0)
660 r = comm.cutoff_mbody;
664 /* cutoff_mbody=0 means we do not have DLB */
665 r = comm.cellsize_min[dd->dim[0]];
666 for (int di = 1; di < dd->ndim; di++)
668 r = std::min(r, comm.cellsize_min[dd->dim[di]]);
670 if (comm.systemInfo.filterBondedCommunication)
672 r = std::max(r, comm.cutoff_mbody);
676 r = std::min(r, systemInfo.cutoff);
684 real dd_cutoff_twobody(const gmx_domdec_t *dd)
688 r_mb = dd_cutoff_multibody(dd);
690 return std::max(dd->comm->systemInfo.cutoff, r_mb);
694 static void dd_cart_coord2pmecoord(const DDRankSetup &ddRankSetup,
698 const int nc = ddRankSetup.numPPCells[ddRankSetup.cartpmedim];
699 const int ntot = ddRankSetup.ntot[ddRankSetup.cartpmedim];
700 copy_ivec(coord, coord_pme);
701 coord_pme[ddRankSetup.cartpmedim] =
702 nc + (coord[ddRankSetup.cartpmedim]*(ntot - nc) + (ntot - nc)/2)/nc;
705 /* Returns the PME rank index in 0...npmenodes-1 for the PP cell with index ddCellIndex */
706 static int ddindex2pmeindex(const DDRankSetup &ddRankSetup,
707 const int ddCellIndex)
709 const int npp = ddRankSetup.numPPRanks;
710 const int npme = ddRankSetup.npmenodes;
712 /* Here we assign a PME node to communicate with this DD node
713 * by assuming that the major index of both is x.
714 * We add npme/2 to obtain an even distribution.
716 return (ddCellIndex*npme + npme/2)/npp;
719 static int *dd_interleaved_pme_ranks(const DDRankSetup &ddRankSetup)
724 snew(pme_rank, ddRankSetup.npmenodes);
726 for (i = 0; i < ddRankSetup.numPPRanks; i++)
728 p0 = ddindex2pmeindex(ddRankSetup, i);
729 p1 = ddindex2pmeindex(ddRankSetup, i + 1);
730 if (i + 1 == ddRankSetup.numPPRanks || p1 > p0)
734 fprintf(debug, "pme_rank[%d] = %d\n", n, i+1+n);
736 pme_rank[n] = i + 1 + n;
744 static int gmx_ddcoord2pmeindex(const t_commrec *cr, int x, int y, int z)
752 if (dd->comm->bCartesian) {
753 gmx_ddindex2xyz(dd->nc,ddindex,coords);
754 dd_coords2pmecoords(dd,coords,coords_pme);
755 copy_ivec(dd->ntot,nc);
756 nc[dd->cartpmedim] -= dd->nc[dd->cartpmedim];
757 coords_pme[dd->cartpmedim] -= dd->nc[dd->cartpmedim];
759 slab = (coords_pme[XX]*nc[YY] + coords_pme[YY])*nc[ZZ] + coords_pme[ZZ];
761 slab = (ddindex*cr->npmenodes + cr->npmenodes/2)/dd->nnodes;
767 slab = ddindex2pmeindex(dd->comm->ddRankSetup, dd_index(dd->nc, coords));
772 static int ddcoord2simnodeid(const t_commrec *cr, int x, int y, int z)
774 const DDRankSetup &ddRankSetup = cr->dd->comm->ddRankSetup;
776 int ddindex, nodeid = -1;
781 if (ddRankSetup.bCartesianPP_PME)
784 MPI_Cart_rank(cr->mpi_comm_mysim, coords, &nodeid);
789 ddindex = dd_index(cr->dd->nc, coords);
790 if (ddRankSetup.bCartesianPP)
792 nodeid = ddRankSetup.ddindex2simnodeid[ddindex];
796 if (ddRankSetup.pmenodes)
798 nodeid = ddindex + gmx_ddcoord2pmeindex(cr, x, y, z);
810 static int dd_simnode2pmenode(const DDRankSetup &ddRankSetup,
811 const t_commrec gmx_unused *cr,
812 const int sim_nodeid)
816 /* This assumes a uniform x domain decomposition grid cell size */
817 if (ddRankSetup.bCartesianPP_PME)
820 ivec coord, coord_pme;
821 MPI_Cart_coords(cr->mpi_comm_mysim, sim_nodeid, DIM, coord);
822 if (coord[ddRankSetup.cartpmedim] < ddRankSetup.numPPCells[ddRankSetup.cartpmedim])
824 /* This is a PP rank */
825 dd_cart_coord2pmecoord(ddRankSetup, coord, coord_pme);
826 MPI_Cart_rank(cr->mpi_comm_mysim, coord_pme, &pmenode);
830 else if (ddRankSetup.bCartesianPP)
832 if (sim_nodeid < ddRankSetup.numPPRanks)
834 pmenode = ddRankSetup.numPPRanks + ddindex2pmeindex(ddRankSetup, sim_nodeid);
839 /* This assumes DD cells with identical x coordinates
840 * are numbered sequentially.
842 if (ddRankSetup.pmenodes == nullptr)
844 if (sim_nodeid < ddRankSetup.numPPRanks)
846 /* The DD index equals the nodeid */
847 pmenode = ddRankSetup.numPPRanks + ddindex2pmeindex(ddRankSetup, sim_nodeid);
853 while (sim_nodeid > ddRankSetup.pmenodes[i])
857 if (sim_nodeid < ddRankSetup.pmenodes[i])
859 pmenode = ddRankSetup.pmenodes[i];
867 NumPmeDomains getNumPmeDomains(const gmx_domdec_t *dd)
872 dd->comm->ddRankSetup.npmenodes_x,
873 dd->comm->ddRankSetup.npmenodes_y
884 std::vector<int> get_pme_ddranks(const t_commrec *cr,
887 const DDRankSetup &ddRankSetup = cr->dd->comm->ddRankSetup;
888 GMX_RELEASE_ASSERT(ddRankSetup.npmenodes > 0, "This function should only be called when PME-only ranks are in use");
889 std::vector<int> ddranks;
890 ddranks.reserve((ddRankSetup.numPPRanks + ddRankSetup.npmenodes - 1)/ddRankSetup.npmenodes);
892 for (int x = 0; x < ddRankSetup.numPPCells[XX]; x++)
894 for (int y = 0; y < ddRankSetup.numPPCells[YY]; y++)
896 for (int z = 0; z < ddRankSetup.numPPCells[ZZ]; z++)
898 if (ddRankSetup.bCartesianPP_PME)
900 ivec coord = { x, y, z };
902 dd_cart_coord2pmecoord(ddRankSetup, coord, coord_pme);
903 if (cr->dd->ci[XX] == coord_pme[XX] &&
904 cr->dd->ci[YY] == coord_pme[YY] &&
905 cr->dd->ci[ZZ] == coord_pme[ZZ])
907 ddranks.push_back(ddcoord2simnodeid(cr, x, y, z));
912 /* The slab corresponds to the nodeid in the PME group */
913 if (gmx_ddcoord2pmeindex(cr, x, y, z) == pmenodeid)
915 ddranks.push_back(ddcoord2simnodeid(cr, x, y, z));
924 static gmx_bool receive_vir_ener(const gmx_domdec_t *dd, const t_commrec *cr)
926 gmx_bool bReceive = TRUE;
928 if (cr->npmenodes < dd->nnodes)
930 const DDRankSetup &ddRankSetup = dd->comm->ddRankSetup;
931 if (ddRankSetup.bCartesianPP_PME)
934 int pmenode = dd_simnode2pmenode(ddRankSetup, cr, cr->sim_nodeid);
936 MPI_Cart_coords(cr->mpi_comm_mysim, cr->sim_nodeid, DIM, coords);
937 coords[ddRankSetup.cartpmedim]++;
938 if (coords[ddRankSetup.cartpmedim] < dd->nc[ddRankSetup.cartpmedim])
941 MPI_Cart_rank(cr->mpi_comm_mysim, coords, &rank);
942 if (dd_simnode2pmenode(ddRankSetup, cr, rank) == pmenode)
944 /* This is not the last PP node for pmenode */
949 GMX_RELEASE_ASSERT(false, "Without MPI we should not have Cartesian PP-PME with #PMEnodes < #DDnodes");
954 int pmenode = dd_simnode2pmenode(ddRankSetup, cr, cr->sim_nodeid);
955 if (cr->sim_nodeid+1 < cr->nnodes &&
956 dd_simnode2pmenode(ddRankSetup, cr, cr->sim_nodeid + 1) == pmenode)
958 /* This is not the last PP node for pmenode */
967 static void set_slb_pme_dim_f(gmx_domdec_t *dd, int dim, real **dim_f)
969 gmx_domdec_comm_t *comm;
974 snew(*dim_f, dd->nc[dim]+1);
976 for (i = 1; i < dd->nc[dim]; i++)
978 if (comm->slb_frac[dim])
980 (*dim_f)[i] = (*dim_f)[i-1] + comm->slb_frac[dim][i-1];
984 (*dim_f)[i] = static_cast<real>(i)/static_cast<real>(dd->nc[dim]);
987 (*dim_f)[dd->nc[dim]] = 1;
990 static void init_ddpme(gmx_domdec_t *dd, gmx_ddpme_t *ddpme, int dimind)
992 const DDRankSetup &ddRankSetup = dd->comm->ddRankSetup;
994 if (dimind == 0 && dd->dim[0] == YY && ddRankSetup.npmenodes_x == 1)
1000 ddpme->dim = dimind;
1002 ddpme->dim_match = (ddpme->dim == dd->dim[dimind]);
1004 ddpme->nslab = (ddpme->dim == 0 ?
1005 ddRankSetup.npmenodes_x :
1006 ddRankSetup.npmenodes_y);
1008 if (ddpme->nslab <= 1)
1013 const int nso = ddRankSetup.npmenodes/ddpme->nslab;
1014 /* Determine for each PME slab the PP location range for dimension dim */
1015 snew(ddpme->pp_min, ddpme->nslab);
1016 snew(ddpme->pp_max, ddpme->nslab);
1017 for (int slab = 0; slab < ddpme->nslab; slab++)
1019 ddpme->pp_min[slab] = dd->nc[dd->dim[dimind]] - 1;
1020 ddpme->pp_max[slab] = 0;
1022 for (int i = 0; i < dd->nnodes; i++)
1025 ddindex2xyz(dd->nc, i, xyz);
1026 /* For y only use our y/z slab.
1027 * This assumes that the PME x grid size matches the DD grid size.
1029 if (dimind == 0 || xyz[XX] == dd->ci[XX])
1031 const int pmeindex = ddindex2pmeindex(ddRankSetup, i);
1035 slab = pmeindex/nso;
1039 slab = pmeindex % ddpme->nslab;
1041 ddpme->pp_min[slab] = std::min(ddpme->pp_min[slab], xyz[dimind]);
1042 ddpme->pp_max[slab] = std::max(ddpme->pp_max[slab], xyz[dimind]);
1046 set_slb_pme_dim_f(dd, ddpme->dim, &ddpme->slb_dim_f);
1049 int dd_pme_maxshift_x(const gmx_domdec_t *dd)
1051 const DDRankSetup &ddRankSetup = dd->comm->ddRankSetup;
1053 if (ddRankSetup.ddpme[0].dim == XX)
1055 return ddRankSetup.ddpme[0].maxshift;
1063 int dd_pme_maxshift_y(const gmx_domdec_t *dd)
1065 const DDRankSetup &ddRankSetup = dd->comm->ddRankSetup;
1067 if (ddRankSetup.ddpme[0].dim == YY)
1069 return ddRankSetup.ddpme[0].maxshift;
1071 else if (ddRankSetup.npmedecompdim >= 2 && ddRankSetup.ddpme[1].dim == YY)
1073 return ddRankSetup.ddpme[1].maxshift;
1081 bool ddHaveSplitConstraints(const gmx_domdec_t &dd)
1083 return dd.comm->systemInfo.haveSplitConstraints;
1086 void dd_cycles_add(const gmx_domdec_t *dd, float cycles, int ddCycl)
1088 /* Note that the cycles value can be incorrect, either 0 or some
1089 * extremely large value, when our thread migrated to another core
1090 * with an unsynchronized cycle counter. If this happens less often
1091 * that once per nstlist steps, this will not cause issues, since
1092 * we later subtract the maximum value from the sum over nstlist steps.
1093 * A zero count will slightly lower the total, but that's a small effect.
1094 * Note that the main purpose of the subtraction of the maximum value
1095 * is to avoid throwing off the load balancing when stalls occur due
1096 * e.g. system activity or network congestion.
1098 dd->comm->cycl[ddCycl] += cycles;
1099 dd->comm->cycl_n[ddCycl]++;
1100 if (cycles > dd->comm->cycl_max[ddCycl])
1102 dd->comm->cycl_max[ddCycl] = cycles;
1107 static void make_load_communicator(gmx_domdec_t *dd, int dim_ind, ivec loc)
1112 gmx_bool bPartOfGroup = FALSE;
1114 dim = dd->dim[dim_ind];
1115 copy_ivec(loc, loc_c);
1116 for (i = 0; i < dd->nc[dim]; i++)
1119 rank = dd_index(dd->nc, loc_c);
1120 if (rank == dd->rank)
1122 /* This process is part of the group */
1123 bPartOfGroup = TRUE;
1126 MPI_Comm_split(dd->mpi_comm_all, bPartOfGroup ? 0 : MPI_UNDEFINED, dd->rank,
1130 dd->comm->mpi_comm_load[dim_ind] = c_row;
1131 if (!isDlbDisabled(dd->comm))
1133 DDCellsizesWithDlb &cellsizes = dd->comm->cellsizesWithDlb[dim_ind];
1135 if (dd->ci[dim] == dd->master_ci[dim])
1137 /* This is the root process of this row */
1138 cellsizes.rowMaster = std::make_unique<RowMaster>();
1140 RowMaster &rowMaster = *cellsizes.rowMaster;
1141 rowMaster.cellFrac.resize(ddCellFractionBufferSize(dd, dim_ind));
1142 rowMaster.oldCellFrac.resize(dd->nc[dim] + 1);
1143 rowMaster.isCellMin.resize(dd->nc[dim]);
1146 rowMaster.bounds.resize(dd->nc[dim]);
1148 rowMaster.buf_ncd.resize(dd->nc[dim]);
1152 /* This is not a root process, we only need to receive cell_f */
1153 cellsizes.fracRow.resize(ddCellFractionBufferSize(dd, dim_ind));
1156 if (dd->ci[dim] == dd->master_ci[dim])
1158 snew(dd->comm->load[dim_ind].load, dd->nc[dim]*DD_NLOAD_MAX);
1164 void dd_setup_dlb_resource_sharing(t_commrec *cr,
1168 int physicalnode_id_hash;
1170 MPI_Comm mpi_comm_pp_physicalnode;
1172 if (!thisRankHasDuty(cr, DUTY_PP) || gpu_id < 0)
1174 /* Only ranks with short-ranged tasks (currently) use GPUs.
1175 * If we don't have GPUs assigned, there are no resources to share.
1180 physicalnode_id_hash = gmx_physicalnode_id_hash();
1186 fprintf(debug, "dd_setup_dd_dlb_gpu_sharing:\n");
1187 fprintf(debug, "DD PP rank %d physical node hash %d gpu_id %d\n",
1188 dd->rank, physicalnode_id_hash, gpu_id);
1190 /* Split the PP communicator over the physical nodes */
1191 /* TODO: See if we should store this (before), as it's also used for
1192 * for the nodecomm summation.
1194 // TODO PhysicalNodeCommunicator could be extended/used to handle
1195 // the need for per-node per-group communicators.
1196 MPI_Comm_split(dd->mpi_comm_all, physicalnode_id_hash, dd->rank,
1197 &mpi_comm_pp_physicalnode);
1198 MPI_Comm_split(mpi_comm_pp_physicalnode, gpu_id, dd->rank,
1199 &dd->comm->mpi_comm_gpu_shared);
1200 MPI_Comm_free(&mpi_comm_pp_physicalnode);
1201 MPI_Comm_size(dd->comm->mpi_comm_gpu_shared, &dd->comm->nrank_gpu_shared);
1205 fprintf(debug, "nrank_gpu_shared %d\n", dd->comm->nrank_gpu_shared);
1208 /* Note that some ranks could share a GPU, while others don't */
1210 if (dd->comm->nrank_gpu_shared == 1)
1212 MPI_Comm_free(&dd->comm->mpi_comm_gpu_shared);
1215 GMX_UNUSED_VALUE(cr);
1216 GMX_UNUSED_VALUE(gpu_id);
1220 static void make_load_communicators(gmx_domdec_t gmx_unused *dd)
1223 int dim0, dim1, i, j;
1228 fprintf(debug, "Making load communicators\n");
1231 dd->comm->load = new domdec_load_t[std::max(dd->ndim, 1)];
1232 snew(dd->comm->mpi_comm_load, std::max(dd->ndim, 1));
1240 make_load_communicator(dd, 0, loc);
1244 for (i = 0; i < dd->nc[dim0]; i++)
1247 make_load_communicator(dd, 1, loc);
1253 for (i = 0; i < dd->nc[dim0]; i++)
1257 for (j = 0; j < dd->nc[dim1]; j++)
1260 make_load_communicator(dd, 2, loc);
1267 fprintf(debug, "Finished making load communicators\n");
1272 /*! \brief Sets up the relation between neighboring domains and zones */
1273 static void setup_neighbor_relations(gmx_domdec_t *dd)
1275 int d, dim, i, j, m;
1277 gmx_domdec_zones_t *zones;
1278 gmx_domdec_ns_ranges_t *izone;
1279 GMX_ASSERT(dd->ndim >= 0, "Must have non-negative number of dimensions for DD");
1281 for (d = 0; d < dd->ndim; d++)
1284 copy_ivec(dd->ci, tmp);
1285 tmp[dim] = (tmp[dim] + 1) % dd->nc[dim];
1286 dd->neighbor[d][0] = ddcoord2ddnodeid(dd, tmp);
1287 copy_ivec(dd->ci, tmp);
1288 tmp[dim] = (tmp[dim] - 1 + dd->nc[dim]) % dd->nc[dim];
1289 dd->neighbor[d][1] = ddcoord2ddnodeid(dd, tmp);
1292 fprintf(debug, "DD rank %d neighbor ranks in dir %d are + %d - %d\n",
1295 dd->neighbor[d][1]);
1299 int nzone = (1 << dd->ndim);
1300 GMX_ASSERT(dd->ndim < DIM, "Invalid number of dimensions");
1301 int nizone = (1 << std::max(dd->ndim - 1, 0));
1302 assert(nizone >= 1 && nizone <= DD_MAXIZONE);
1304 zones = &dd->comm->zones;
1306 for (i = 0; i < nzone; i++)
1309 clear_ivec(zones->shift[i]);
1310 for (d = 0; d < dd->ndim; d++)
1312 zones->shift[i][dd->dim[d]] = dd_zo[i][m++];
1317 for (i = 0; i < nzone; i++)
1319 for (d = 0; d < DIM; d++)
1321 s[d] = dd->ci[d] - zones->shift[i][d];
1326 else if (s[d] >= dd->nc[d])
1332 zones->nizone = nizone;
1333 for (i = 0; i < zones->nizone; i++)
1335 assert(ddNonbondedZonePairRanges[i][0] == i);
1337 izone = &zones->izone[i];
1338 /* dd_zp3 is for 3D decomposition, for fewer dimensions use only
1339 * j-zones up to nzone.
1341 izone->j0 = std::min(ddNonbondedZonePairRanges[i][1], nzone);
1342 izone->j1 = std::min(ddNonbondedZonePairRanges[i][2], nzone);
1343 for (dim = 0; dim < DIM; dim++)
1345 if (dd->nc[dim] == 1)
1347 /* All shifts should be allowed */
1348 izone->shift0[dim] = -1;
1349 izone->shift1[dim] = 1;
1353 /* Determine the min/max j-zone shift wrt the i-zone */
1354 izone->shift0[dim] = 1;
1355 izone->shift1[dim] = -1;
1356 for (j = izone->j0; j < izone->j1; j++)
1358 int shift_diff = zones->shift[j][dim] - zones->shift[i][dim];
1359 if (shift_diff < izone->shift0[dim])
1361 izone->shift0[dim] = shift_diff;
1363 if (shift_diff > izone->shift1[dim])
1365 izone->shift1[dim] = shift_diff;
1372 if (!isDlbDisabled(dd->comm))
1374 dd->comm->cellsizesWithDlb.resize(dd->ndim);
1377 if (dd->comm->ddSettings.recordLoad)
1379 make_load_communicators(dd);
1383 static void make_pp_communicator(const gmx::MDLogger &mdlog,
1385 t_commrec gmx_unused *cr,
1386 bool gmx_unused reorder)
1389 gmx_domdec_comm_t *comm = dd->comm;
1390 DDRankSetup &ddRankSetup = comm->ddRankSetup;
1392 if (ddRankSetup.bCartesianPP)
1394 /* Set up cartesian communication for the particle-particle part */
1395 GMX_LOG(mdlog.info).appendTextFormatted(
1396 "Will use a Cartesian communicator: %d x %d x %d",
1397 dd->nc[XX], dd->nc[YY], dd->nc[ZZ]);
1400 for (int i = 0; i < DIM; i++)
1405 MPI_Cart_create(cr->mpi_comm_mygroup, DIM, dd->nc, periods, static_cast<int>(reorder),
1407 /* We overwrite the old communicator with the new cartesian one */
1408 cr->mpi_comm_mygroup = comm_cart;
1411 dd->mpi_comm_all = cr->mpi_comm_mygroup;
1412 MPI_Comm_rank(dd->mpi_comm_all, &dd->rank);
1414 if (ddRankSetup.bCartesianPP_PME)
1416 /* Since we want to use the original cartesian setup for sim,
1417 * and not the one after split, we need to make an index.
1419 snew(ddRankSetup.ddindex2ddnodeid, dd->nnodes);
1420 ddRankSetup.ddindex2ddnodeid[dd_index(dd->nc, dd->ci)] = dd->rank;
1421 gmx_sumi(dd->nnodes, ddRankSetup.ddindex2ddnodeid, cr);
1422 /* Get the rank of the DD master,
1423 * above we made sure that the master node is a PP node.
1434 MPI_Allreduce(&rank, &dd->masterrank, 1, MPI_INT, MPI_SUM, dd->mpi_comm_all);
1436 else if (ddRankSetup.bCartesianPP)
1438 if (cr->npmenodes == 0)
1440 /* The PP communicator is also
1441 * the communicator for this simulation
1443 cr->mpi_comm_mysim = cr->mpi_comm_mygroup;
1445 cr->nodeid = dd->rank;
1447 MPI_Cart_coords(dd->mpi_comm_all, dd->rank, DIM, dd->ci);
1449 /* We need to make an index to go from the coordinates
1450 * to the nodeid of this simulation.
1452 snew(ddRankSetup.ddindex2simnodeid, dd->nnodes);
1453 std::vector<int> buf(dd->nnodes);
1454 if (thisRankHasDuty(cr, DUTY_PP))
1456 buf[dd_index(dd->nc, dd->ci)] = cr->sim_nodeid;
1458 /* Communicate the ddindex to simulation nodeid index */
1459 MPI_Allreduce(buf.data(), ddRankSetup.ddindex2simnodeid, dd->nnodes, MPI_INT, MPI_SUM,
1460 cr->mpi_comm_mysim);
1462 /* Determine the master coordinates and rank.
1463 * The DD master should be the same node as the master of this sim.
1465 for (int i = 0; i < dd->nnodes; i++)
1467 if (ddRankSetup.ddindex2simnodeid[i] == 0)
1469 ddindex2xyz(dd->nc, i, dd->master_ci);
1470 MPI_Cart_rank(dd->mpi_comm_all, dd->master_ci, &dd->masterrank);
1475 fprintf(debug, "The master rank is %d\n", dd->masterrank);
1480 /* No Cartesian communicators */
1481 /* We use the rank in dd->comm->all as DD index */
1482 ddindex2xyz(dd->nc, dd->rank, dd->ci);
1483 /* The simulation master nodeid is 0, so the DD master rank is also 0 */
1485 clear_ivec(dd->master_ci);
1489 GMX_LOG(mdlog.info).appendTextFormatted(
1490 "Domain decomposition rank %d, coordinates %d %d %d\n",
1491 dd->rank, dd->ci[XX], dd->ci[YY], dd->ci[ZZ]);
1495 "Domain decomposition rank %d, coordinates %d %d %d\n\n",
1496 dd->rank, dd->ci[XX], dd->ci[YY], dd->ci[ZZ]);
1500 static void receive_ddindex2simnodeid(gmx_domdec_t *dd,
1504 DDRankSetup &ddRankSetup = dd->comm->ddRankSetup;
1506 if (!ddRankSetup.bCartesianPP_PME && ddRankSetup.bCartesianPP)
1509 snew(ddRankSetup.ddindex2simnodeid, dd->nnodes);
1510 snew(buf, dd->nnodes);
1511 if (thisRankHasDuty(cr, DUTY_PP))
1513 buf[dd_index(dd->nc, dd->ci)] = cr->sim_nodeid;
1515 /* Communicate the ddindex to simulation nodeid index */
1516 MPI_Allreduce(buf, ddRankSetup.ddindex2simnodeid, dd->nnodes, MPI_INT, MPI_SUM,
1517 cr->mpi_comm_mysim);
1521 GMX_UNUSED_VALUE(dd);
1522 GMX_UNUSED_VALUE(cr);
1526 static void split_communicator(const gmx::MDLogger &mdlog,
1528 DdRankOrder gmx_unused rankOrder,
1529 bool gmx_unused reorder,
1530 DDRankSetup *ddRankSetup,
1533 const ivec &numDDCells = ddRankSetup->numPPCells;
1535 if (ddRankSetup->bCartesianPP)
1537 const int numDDCellsTot = ddRankSetup->numPPRanks;
1539 for (int i = 1; i < DIM; i++)
1541 bDiv[i] = ((cr->npmenodes*numDDCells[i]) % numDDCellsTot == 0);
1543 if (bDiv[YY] || bDiv[ZZ])
1545 ddRankSetup->bCartesianPP_PME = TRUE;
1546 /* If we have 2D PME decomposition, which is always in x+y,
1547 * we stack the PME only nodes in z.
1548 * Otherwise we choose the direction that provides the thinnest slab
1549 * of PME only nodes as this will have the least effect
1550 * on the PP communication.
1551 * But for the PME communication the opposite might be better.
1553 if (bDiv[ZZ] && (ddRankSetup->npmenodes_y > 1 ||
1555 numDDCells[YY] > numDDCells[ZZ]))
1557 ddRankSetup->cartpmedim = ZZ;
1561 ddRankSetup->cartpmedim = YY;
1563 ddRankSetup->ntot[ddRankSetup->cartpmedim]
1564 += (cr->npmenodes*numDDCells[ddRankSetup->cartpmedim])/numDDCellsTot;
1568 GMX_LOG(mdlog.info).appendTextFormatted(
1569 "Number of PME-only ranks (%d) is not a multiple of nx*ny (%d*%d) or nx*nz (%d*%d)",
1570 cr->npmenodes, numDDCells[XX], numDDCells[YY], numDDCells[XX], numDDCells[ZZ]);
1571 GMX_LOG(mdlog.info).appendText("Will not use a Cartesian communicator for PP <-> PME\n");
1575 if (ddRankSetup->bCartesianPP_PME)
1581 GMX_LOG(mdlog.info).appendTextFormatted(
1582 "Will use a Cartesian communicator for PP <-> PME: %d x %d x %d",
1583 ddRankSetup->ntot[XX], ddRankSetup->ntot[YY], ddRankSetup->ntot[ZZ]);
1585 for (int i = 0; i < DIM; i++)
1590 MPI_Cart_create(cr->mpi_comm_mysim, DIM, ddRankSetup->ntot, periods, static_cast<int>(reorder),
1592 MPI_Comm_rank(comm_cart, &rank);
1593 if (MASTER(cr) && rank != 0)
1595 gmx_fatal(FARGS, "MPI rank 0 was renumbered by MPI_Cart_create, we do not allow this");
1598 /* With this assigment we loose the link to the original communicator
1599 * which will usually be MPI_COMM_WORLD, unless have multisim.
1601 cr->mpi_comm_mysim = comm_cart;
1602 cr->sim_nodeid = rank;
1604 MPI_Cart_coords(cr->mpi_comm_mysim, cr->sim_nodeid, DIM, ddCellIndex);
1606 GMX_LOG(mdlog.info).appendTextFormatted(
1607 "Cartesian rank %d, coordinates %d %d %d\n",
1608 cr->sim_nodeid, ddCellIndex[XX], ddCellIndex[YY], ddCellIndex[ZZ]);
1610 if (ddCellIndex[ddRankSetup->cartpmedim] < numDDCells[ddRankSetup->cartpmedim])
1614 if (cr->npmenodes == 0 ||
1615 ddCellIndex[ddRankSetup->cartpmedim] >= numDDCells[ddRankSetup->cartpmedim])
1617 cr->duty = DUTY_PME;
1620 /* Split the sim communicator into PP and PME only nodes */
1621 MPI_Comm_split(cr->mpi_comm_mysim,
1622 getThisRankDuties(cr),
1623 dd_index(ddRankSetup->ntot, ddCellIndex),
1624 &cr->mpi_comm_mygroup);
1631 case DdRankOrder::pp_pme:
1632 GMX_LOG(mdlog.info).appendText("Order of the ranks: PP first, PME last");
1634 case DdRankOrder::interleave:
1635 /* Interleave the PP-only and PME-only ranks */
1636 GMX_LOG(mdlog.info).appendText("Interleaving PP and PME ranks");
1637 ddRankSetup->pmenodes = dd_interleaved_pme_ranks(*ddRankSetup);
1639 case DdRankOrder::cartesian:
1642 gmx_fatal(FARGS, "Invalid ddRankOrder=%d", static_cast<int>(rankOrder));
1645 if (dd_simnode2pmenode(*ddRankSetup, cr, cr->sim_nodeid) == -1)
1647 cr->duty = DUTY_PME;
1654 /* Split the sim communicator into PP and PME only nodes */
1655 MPI_Comm_split(cr->mpi_comm_mysim,
1656 getThisRankDuties(cr),
1658 &cr->mpi_comm_mygroup);
1659 MPI_Comm_rank(cr->mpi_comm_mygroup, &cr->nodeid);
1663 GMX_LOG(mdlog.info).appendTextFormatted(
1664 "This rank does only %s work.\n",
1665 thisRankHasDuty(cr, DUTY_PP) ? "particle-particle" : "PME-mesh");
1668 /*! \brief Makes the PP communicator and the PME communicator, when needed
1670 * Updates \p ddRankSetup for Cartesian communication aspects.
1671 * For PP ranks, sets the DD PP cell index in \p ddCellIndex.
1673 static void makeGroupCommunicators(const gmx::MDLogger &mdlog,
1674 const DDSettings &ddSettings,
1675 const DDSetup &ddSetup,
1676 const DdRankOrder ddRankOrder,
1677 DDRankSetup *ddRankSetup,
1681 /* Initially we set ntot to the number of PP cells,
1682 * This will be increased with PME cells when using Cartesian communicators.
1684 copy_ivec(ddSetup.numDomains, ddRankSetup->ntot);
1686 ddRankSetup->bCartesianPP = (ddRankOrder == DdRankOrder::cartesian);
1687 ddRankSetup->bCartesianPP_PME = FALSE;
1689 if (ddSetup.numPmeRanks > 0)
1691 /* Split the communicator into a PP and PME part */
1692 split_communicator(mdlog, cr, ddRankOrder, ddSettings.useCartesianReorder,
1693 ddRankSetup, ddCellIndex);
1697 /* All nodes do PP and PME */
1698 /* We do not require separate communicators */
1699 cr->mpi_comm_mygroup = cr->mpi_comm_mysim;
1703 /*! \brief For PP ranks, sets or makes the communicator
1705 * For PME ranks get the rank id.
1706 * For PP only ranks, sets the PME-only rank.
1708 static void setupGroupCommunication(const gmx::MDLogger &mdlog,
1709 const DDSettings &ddSettings,
1713 DDRankSetup &ddRankSetup = dd->comm->ddRankSetup;
1715 if (thisRankHasDuty(cr, DUTY_PP))
1717 /* Copy or make a new PP communicator */
1719 /* We (possibly) reordered the nodes in split_communicator,
1720 * so it is no longer required in make_pp_communicator.
1722 const bool useCartesianReorder =
1723 (ddSettings.useCartesianReorder &&
1724 !ddRankSetup.bCartesianPP_PME);
1726 make_pp_communicator(mdlog, dd, cr, useCartesianReorder);
1730 receive_ddindex2simnodeid(dd, cr);
1733 if (!thisRankHasDuty(cr, DUTY_PME))
1735 /* Set up the commnuication to our PME node */
1736 dd->pme_nodeid = dd_simnode2pmenode(ddRankSetup, cr, cr->sim_nodeid);
1737 dd->pme_receive_vir_ener = receive_vir_ener(dd, cr);
1740 fprintf(debug, "My pme_nodeid %d receive ener %s\n",
1741 dd->pme_nodeid, gmx::boolToString(dd->pme_receive_vir_ener));
1746 dd->pme_nodeid = -1;
1749 /* We can not use DDMASTER(dd), because dd->masterrank is set later */
1752 dd->ma = std::make_unique<AtomDistribution>(dd->nc,
1753 dd->comm->cgs_gl.nr,
1754 dd->comm->cgs_gl.index[dd->comm->cgs_gl.nr]);
1758 static real *get_slb_frac(const gmx::MDLogger &mdlog,
1759 const char *dir, int nc, const char *size_string)
1761 real *slb_frac, tot;
1766 if (nc > 1 && size_string != nullptr)
1768 GMX_LOG(mdlog.info).appendTextFormatted(
1769 "Using static load balancing for the %s direction", dir);
1772 for (i = 0; i < nc; i++)
1775 sscanf(size_string, "%20lf%n", &dbl, &n);
1778 gmx_fatal(FARGS, "Incorrect or not enough DD cell size entries for direction %s: '%s'", dir, size_string);
1785 std::string relativeCellSizes = "Relative cell sizes:";
1786 for (i = 0; i < nc; i++)
1789 relativeCellSizes += gmx::formatString(" %5.3f", slb_frac[i]);
1791 GMX_LOG(mdlog.info).appendText(relativeCellSizes);
1797 static int multi_body_bondeds_count(const gmx_mtop_t *mtop)
1800 gmx_mtop_ilistloop_t iloop = gmx_mtop_ilistloop_init(mtop);
1802 while (const InteractionLists *ilists = gmx_mtop_ilistloop_next(iloop, &nmol))
1804 for (auto &ilist : extractILists(*ilists, IF_BOND))
1806 if (NRAL(ilist.functionType) > 2)
1808 n += nmol*(ilist.iatoms.size()/ilistStride(ilist));
1816 static int dd_getenv(const gmx::MDLogger &mdlog,
1817 const char *env_var, int def)
1823 val = getenv(env_var);
1826 if (sscanf(val, "%20d", &nst) <= 0)
1830 GMX_LOG(mdlog.info).appendTextFormatted(
1831 "Found env.var. %s = %s, using value %d",
1838 static void check_dd_restrictions(const gmx_domdec_t *dd,
1839 const t_inputrec *ir,
1840 const gmx::MDLogger &mdlog)
1842 if (ir->ePBC == epbcSCREW &&
1843 (dd->nc[XX] == 1 || dd->nc[YY] > 1 || dd->nc[ZZ] > 1))
1845 gmx_fatal(FARGS, "With pbc=%s can only do domain decomposition in the x-direction", epbc_names[ir->ePBC]);
1848 if (ir->ns_type == ensSIMPLE)
1850 gmx_fatal(FARGS, "Domain decomposition does not support simple neighbor searching, use grid searching or run with one MPI rank");
1853 if (ir->nstlist == 0)
1855 gmx_fatal(FARGS, "Domain decomposition does not work with nstlist=0");
1858 if (ir->comm_mode == ecmANGULAR && ir->ePBC != epbcNONE)
1860 GMX_LOG(mdlog.warning).appendText("comm-mode angular will give incorrect results when the comm group partially crosses a periodic boundary");
1864 static real average_cellsize_min(const gmx_ddbox_t &ddbox,
1865 const ivec numDomains)
1867 real r = ddbox.box_size[XX];
1868 for (int d = 0; d < DIM; d++)
1870 if (numDomains[d] > 1)
1872 /* Check using the initial average cell size */
1873 r = std::min(r, ddbox.box_size[d]*ddbox.skew_fac[d]/numDomains[d]);
1880 /*! \brief Depending on the DLB initial value return the DLB switched off state or issue an error.
1882 static DlbState forceDlbOffOrBail(DlbState cmdlineDlbState,
1883 const std::string &reasonStr,
1884 const gmx::MDLogger &mdlog)
1886 std::string dlbNotSupportedErr = "Dynamic load balancing requested, but ";
1887 std::string dlbDisableNote = "NOTE: disabling dynamic load balancing as ";
1889 if (cmdlineDlbState == DlbState::onUser)
1891 gmx_fatal(FARGS, "%s", (dlbNotSupportedErr + reasonStr).c_str());
1893 else if (cmdlineDlbState == DlbState::offCanTurnOn)
1895 GMX_LOG(mdlog.info).appendText(dlbDisableNote + reasonStr);
1897 return DlbState::offForever;
1900 /*! \brief Return the dynamic load balancer's initial state based on initial conditions and user inputs.
1902 * This function parses the parameters of "-dlb" command line option setting
1903 * corresponding state values. Then it checks the consistency of the determined
1904 * state with other run parameters and settings. As a result, the initial state
1905 * may be altered or an error may be thrown if incompatibility of options is detected.
1907 * \param [in] mdlog Logger.
1908 * \param [in] dlbOption Enum value for the DLB option.
1909 * \param [in] bRecordLoad True if the load balancer is recording load information.
1910 * \param [in] mdrunOptions Options for mdrun.
1911 * \param [in] ir Pointer mdrun to input parameters.
1912 * \returns DLB initial/startup state.
1914 static DlbState determineInitialDlbState(const gmx::MDLogger &mdlog,
1915 DlbOption dlbOption, gmx_bool bRecordLoad,
1916 const gmx::MdrunOptions &mdrunOptions,
1917 const t_inputrec *ir)
1919 DlbState dlbState = DlbState::offCanTurnOn;
1923 case DlbOption::turnOnWhenUseful: dlbState = DlbState::offCanTurnOn; break;
1924 case DlbOption::no: dlbState = DlbState::offUser; break;
1925 case DlbOption::yes: dlbState = DlbState::onUser; break;
1926 default: gmx_incons("Invalid dlbOption enum value");
1929 /* Reruns don't support DLB: bail or override auto mode */
1930 if (mdrunOptions.rerun)
1932 std::string reasonStr = "it is not supported in reruns.";
1933 return forceDlbOffOrBail(dlbState, reasonStr, mdlog);
1936 /* Unsupported integrators */
1937 if (!EI_DYNAMICS(ir->eI))
1939 auto reasonStr = gmx::formatString("it is only supported with dynamics, not with integrator '%s'.", EI(ir->eI));
1940 return forceDlbOffOrBail(dlbState, reasonStr, mdlog);
1943 /* Without cycle counters we can't time work to balance on */
1946 std::string reasonStr = "cycle counters unsupported or not enabled in the operating system kernel.";
1947 return forceDlbOffOrBail(dlbState, reasonStr, mdlog);
1950 if (mdrunOptions.reproducible)
1952 std::string reasonStr = "you started a reproducible run.";
1955 case DlbState::offUser:
1957 case DlbState::offForever:
1958 GMX_RELEASE_ASSERT(false, "DlbState::offForever is not a valid initial state");
1960 case DlbState::offCanTurnOn:
1961 return forceDlbOffOrBail(dlbState, reasonStr, mdlog);
1962 case DlbState::onCanTurnOff:
1963 GMX_RELEASE_ASSERT(false, "DlbState::offCanTurnOff is not a valid initial state");
1965 case DlbState::onUser:
1966 return forceDlbOffOrBail(dlbState, reasonStr + " In load balanced runs binary reproducibility cannot be ensured.", mdlog);
1968 gmx_fatal(FARGS, "Death horror: undefined case (%d) for load balancing choice", static_cast<int>(dlbState));
1975 /* Sets the order of the DD dimensions, returns the number of DD dimensions */
1976 static int set_dd_dim(const ivec numDDCells,
1977 const DDSettings &ddSettings,
1981 if (ddSettings.useDDOrderZYX)
1983 /* Decomposition order z,y,x */
1984 for (int dim = DIM - 1; dim >= 0; dim--)
1986 if (numDDCells[dim] > 1)
1994 /* Decomposition order x,y,z */
1995 for (int dim = 0; dim < DIM; dim++)
1997 if (numDDCells[dim] > 1)
2006 /* Set dim[0] to avoid extra checks on ndim in several places */
2013 static gmx_domdec_comm_t *init_dd_comm()
2015 gmx_domdec_comm_t *comm = new gmx_domdec_comm_t;
2017 comm->n_load_have = 0;
2018 comm->n_load_collect = 0;
2020 comm->haveTurnedOffDlb = false;
2022 for (int i = 0; i < static_cast<int>(DDAtomRanges::Type::Number); i++)
2024 comm->sum_nat[i] = 0;
2028 comm->load_step = 0;
2031 clear_ivec(comm->load_lim);
2035 /* This should be replaced by a unique pointer */
2036 comm->balanceRegion = ddBalanceRegionAllocate();
2041 /* Returns whether mtop contains constraints and/or vsites */
2042 static bool systemHasConstraintsOrVsites(const gmx_mtop_t &mtop)
2044 auto ilistLoop = gmx_mtop_ilistloop_init(mtop);
2046 while (const InteractionLists *ilists = gmx_mtop_ilistloop_next(ilistLoop, &nmol))
2048 if (!extractILists(*ilists, IF_CONSTRAINT | IF_VSITE).empty())
2057 static void setupUpdateGroups(const gmx::MDLogger &mdlog,
2058 const gmx_mtop_t &mtop,
2059 const t_inputrec &inputrec,
2060 const real cutoffMargin,
2061 DDSystemInfo *systemInfo)
2063 /* When we have constraints and/or vsites, it is beneficial to use
2064 * update groups (when possible) to allow independent update of groups.
2066 if (!systemHasConstraintsOrVsites(mtop))
2068 /* No constraints or vsites, atoms can be updated independently */
2072 systemInfo->updateGroupingPerMoleculetype = gmx::makeUpdateGroups(mtop);
2073 systemInfo->useUpdateGroups =
2074 (!systemInfo->updateGroupingPerMoleculetype.empty() &&
2075 getenv("GMX_NO_UPDATEGROUPS") == nullptr);
2077 if (systemInfo->useUpdateGroups)
2079 int numUpdateGroups = 0;
2080 for (const auto &molblock : mtop.molblock)
2082 numUpdateGroups += molblock.nmol*systemInfo->updateGroupingPerMoleculetype[molblock.type].numBlocks();
2085 systemInfo->maxUpdateGroupRadius =
2086 computeMaxUpdateGroupRadius(mtop,
2087 systemInfo->updateGroupingPerMoleculetype,
2088 maxReferenceTemperature(inputrec));
2090 /* To use update groups, the large domain-to-domain cutoff distance
2091 * should be compatible with the box size.
2093 systemInfo->useUpdateGroups = (atomToAtomIntoDomainToDomainCutoff(*systemInfo, 0) < cutoffMargin);
2095 if (systemInfo->useUpdateGroups)
2097 GMX_LOG(mdlog.info).appendTextFormatted(
2098 "Using update groups, nr %d, average size %.1f atoms, max. radius %.3f nm\n",
2100 mtop.natoms/static_cast<double>(numUpdateGroups),
2101 systemInfo->maxUpdateGroupRadius);
2105 GMX_LOG(mdlog.info).appendTextFormatted("The combination of rlist and box size prohibits the use of update groups\n");
2106 systemInfo->updateGroupingPerMoleculetype.clear();
2111 UnitCellInfo::UnitCellInfo(const t_inputrec &ir) :
2112 npbcdim(ePBC2npbcdim(ir.ePBC)),
2113 numBoundedDimensions(inputrec2nboundeddim(&ir)),
2114 ddBoxIsDynamic(numBoundedDimensions < DIM || inputrecDynamicBox(&ir)),
2115 haveScrewPBC(ir.ePBC == epbcSCREW)
2119 /*! \brief Generate the simulation system information */
2121 getSystemInfo(const gmx::MDLogger &mdlog,
2123 const DomdecOptions &options,
2124 const gmx_mtop_t *mtop,
2125 const t_inputrec *ir,
2127 gmx::ArrayRef<const gmx::RVec> xGlobal)
2129 const real tenPercentMargin = 1.1;
2131 DDSystemInfo systemInfo;
2133 /* We need to decide on update groups early, as this affects communication distances */
2134 systemInfo.useUpdateGroups = false;
2135 if (ir->cutoff_scheme == ecutsVERLET)
2137 real cutoffMargin = std::sqrt(max_cutoff2(ir->ePBC, box)) - ir->rlist;
2138 setupUpdateGroups(mdlog, *mtop, *ir, cutoffMargin, &systemInfo);
2141 // TODO: Check whether all bondeds are within update groups
2142 systemInfo.haveInterDomainBondeds = (mtop->natoms > gmx_mtop_num_molecules(*mtop) ||
2143 mtop->bIntermolecularInteractions);
2144 systemInfo.haveInterDomainMultiBodyBondeds = (multi_body_bondeds_count(mtop) > 0);
2146 if (systemInfo.useUpdateGroups)
2148 systemInfo.haveSplitConstraints = false;
2149 systemInfo.haveSplitSettles = false;
2153 systemInfo.haveSplitConstraints = gmx::inter_charge_group_constraints(*mtop);
2154 systemInfo.haveSplitSettles = gmx::inter_charge_group_settles(*mtop);
2159 /* Set the cut-off to some very large value,
2160 * so we don't need if statements everywhere in the code.
2161 * We use sqrt, since the cut-off is squared in some places.
2163 systemInfo.cutoff = GMX_CUTOFF_INF;
2167 systemInfo.cutoff = atomToAtomIntoDomainToDomainCutoff(systemInfo, ir->rlist);
2169 systemInfo.minCutoffForMultiBody = 0;
2171 /* Determine the minimum cell size limit, affected by many factors */
2172 systemInfo.cellsizeLimit = 0;
2173 systemInfo.filterBondedCommunication = false;
2175 /* We do not allow home atoms to move beyond the neighboring domain
2176 * between domain decomposition steps, which limits the cell size.
2177 * Get an estimate of cell size limit due to atom displacement.
2178 * In most cases this is a large overestimate, because it assumes
2179 * non-interaction atoms.
2180 * We set the chance to 1 in a trillion steps.
2182 constexpr real c_chanceThatAtomMovesBeyondDomain = 1e-12;
2183 const real limitForAtomDisplacement =
2184 minCellSizeForAtomDisplacement(*mtop, *ir,
2185 systemInfo.updateGroupingPerMoleculetype,
2186 c_chanceThatAtomMovesBeyondDomain);
2187 GMX_LOG(mdlog.info).appendTextFormatted(
2188 "Minimum cell size due to atom displacement: %.3f nm",
2189 limitForAtomDisplacement);
2191 systemInfo.cellsizeLimit = std::max(systemInfo.cellsizeLimit,
2192 limitForAtomDisplacement);
2194 /* TODO: PME decomposition currently requires atoms not to be more than
2195 * 2/3 of comm->cutoff, which is >=rlist, outside of their domain.
2196 * In nearly all cases, limitForAtomDisplacement will be smaller
2197 * than 2/3*rlist, so the PME requirement is satisfied.
2198 * But it would be better for both correctness and performance
2199 * to use limitForAtomDisplacement instead of 2/3*comm->cutoff.
2200 * Note that we would need to improve the pairlist buffer case.
2203 if (systemInfo.haveInterDomainBondeds)
2205 if (options.minimumCommunicationRange > 0)
2207 systemInfo.minCutoffForMultiBody =
2208 atomToAtomIntoDomainToDomainCutoff(systemInfo, options.minimumCommunicationRange);
2209 if (options.useBondedCommunication)
2211 systemInfo.filterBondedCommunication = (systemInfo.minCutoffForMultiBody > systemInfo.cutoff);
2215 systemInfo.cutoff = std::max(systemInfo.cutoff,
2216 systemInfo.minCutoffForMultiBody);
2219 else if (ir->bPeriodicMols)
2221 /* Can not easily determine the required cut-off */
2222 GMX_LOG(mdlog.warning).appendText("NOTE: Periodic molecules are present in this system. Because of this, the domain decomposition algorithm cannot easily determine the minimum cell size that it requires for treating bonded interactions. Instead, domain decomposition will assume that half the non-bonded cut-off will be a suitable lower bound.");
2223 systemInfo.minCutoffForMultiBody = systemInfo.cutoff/2;
2231 dd_bonded_cg_distance(mdlog, mtop, ir, as_rvec_array(xGlobal.data()), box,
2232 options.checkBondedInteractions,
2235 gmx_bcast(sizeof(r_2b), &r_2b, cr);
2236 gmx_bcast(sizeof(r_mb), &r_mb, cr);
2238 /* We use an initial margin of 10% for the minimum cell size,
2239 * except when we are just below the non-bonded cut-off.
2241 if (options.useBondedCommunication)
2243 if (std::max(r_2b, r_mb) > systemInfo.cutoff)
2245 const real r_bonded = std::max(r_2b, r_mb);
2246 systemInfo.minCutoffForMultiBody = tenPercentMargin*r_bonded;
2247 /* This is the (only) place where we turn on the filtering */
2248 systemInfo.filterBondedCommunication = true;
2252 const real r_bonded = r_mb;
2253 systemInfo.minCutoffForMultiBody = std::min(tenPercentMargin*r_bonded,
2256 /* We determine cutoff_mbody later */
2257 systemInfo.increaseMultiBodyCutoff = true;
2261 /* No special bonded communication,
2262 * simply increase the DD cut-off.
2264 systemInfo.minCutoffForMultiBody = tenPercentMargin*std::max(r_2b, r_mb);
2265 systemInfo.cutoff = std::max(systemInfo.cutoff,
2266 systemInfo.minCutoffForMultiBody);
2269 GMX_LOG(mdlog.info).appendTextFormatted(
2270 "Minimum cell size due to bonded interactions: %.3f nm",
2271 systemInfo.minCutoffForMultiBody);
2273 systemInfo.cellsizeLimit = std::max(systemInfo.cellsizeLimit,
2274 systemInfo.minCutoffForMultiBody);
2277 systemInfo.constraintCommunicationRange = 0;
2278 if (systemInfo.haveSplitConstraints && options.constraintCommunicationRange <= 0)
2280 /* There is a cell size limit due to the constraints (P-LINCS) */
2281 systemInfo.constraintCommunicationRange = gmx::constr_r_max(mdlog, mtop, ir);
2282 GMX_LOG(mdlog.info).appendTextFormatted(
2283 "Estimated maximum distance required for P-LINCS: %.3f nm",
2284 systemInfo.constraintCommunicationRange);
2285 if (systemInfo.constraintCommunicationRange > systemInfo.cellsizeLimit)
2287 GMX_LOG(mdlog.info).appendText("This distance will limit the DD cell size, you can override this with -rcon");
2290 else if (options.constraintCommunicationRange > 0)
2292 /* Here we do not check for dd->splitConstraints.
2293 * because one can also set a cell size limit for virtual sites only
2294 * and at this point we don't know yet if there are intercg v-sites.
2296 GMX_LOG(mdlog.info).appendTextFormatted(
2297 "User supplied maximum distance required for P-LINCS: %.3f nm",
2298 options.constraintCommunicationRange);
2299 systemInfo.constraintCommunicationRange = options.constraintCommunicationRange;
2301 systemInfo.cellsizeLimit = std::max(systemInfo.cellsizeLimit,
2302 systemInfo.constraintCommunicationRange);
2307 /*! \brief Set the cell size and interaction limits, as well as the DD grid
2309 * Also computes the initial ddbox.
2312 getDDSetup(const gmx::MDLogger &mdlog,
2314 const DomdecOptions &options,
2315 const DDSettings &ddSettings,
2316 const DDSystemInfo &systemInfo,
2317 const gmx_mtop_t *mtop,
2318 const t_inputrec *ir,
2320 gmx::ArrayRef<const gmx::RVec> xGlobal,
2325 if (options.numCells[XX] > 0)
2327 copy_ivec(options.numCells, ddSetup.numDomains);
2328 set_ddbox_cr(*cr, &ddSetup.numDomains, *ir, box, xGlobal, ddbox);
2330 if (options.numPmeRanks >= 0)
2332 ddSetup.numPmeRanks = options.numPmeRanks;
2336 /* When the DD grid is set explicitly and -npme is set to auto,
2337 * don't use PME ranks. We check later if the DD grid is
2338 * compatible with the total number of ranks.
2340 ddSetup.numPmeRanks = 0;
2345 set_ddbox_cr(*cr, nullptr, *ir, box, xGlobal, ddbox);
2347 /* We need to choose the optimal DD grid and possibly PME nodes */
2349 dd_choose_grid(mdlog, cr, ir, mtop, box, ddbox,
2350 options.numPmeRanks,
2351 !isDlbDisabled(ddSettings.initialDlbState),
2355 if (ddSetup.numDomains[XX] == 0)
2358 gmx_bool bC = (systemInfo.haveSplitConstraints &&
2359 systemInfo.constraintCommunicationRange > systemInfo.minCutoffForMultiBody);
2360 sprintf(buf, "Change the number of ranks or mdrun option %s%s%s",
2361 !bC ? "-rdd" : "-rcon",
2362 ddSettings.initialDlbState != DlbState::offUser ? " or -dds" : "",
2363 bC ? " or your LINCS settings" : "");
2365 gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
2366 "There is no domain decomposition for %d ranks that is compatible with the given box and a minimum cell size of %g nm\n"
2368 "Look in the log file for details on the domain decomposition",
2369 cr->nnodes - ddSetup.numPmeRanks,
2370 ddSetup.cellsizeLimit,
2375 const real acs = average_cellsize_min(*ddbox, ddSetup.numDomains);
2376 if (acs < systemInfo.cellsizeLimit)
2378 if (options.numCells[XX] <= 0)
2380 GMX_RELEASE_ASSERT(false, "dd_choose_grid() should return a grid that satisfies the cell size limits");
2384 gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
2385 "The initial cell size (%f) is smaller than the cell size limit (%f), change options -dd, -rdd or -rcon, see the log file for details",
2386 acs, systemInfo.cellsizeLimit);
2390 const int numPPRanks = ddSetup.numDomains[XX]*ddSetup.numDomains[YY]*ddSetup.numDomains[ZZ];
2391 if (cr->nnodes - numPPRanks != ddSetup.numPmeRanks)
2393 gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
2394 "The size of the domain decomposition grid (%d) does not match the number of ranks (%d). The total number of ranks is %d",
2395 numPPRanks, cr->nnodes - ddSetup.numPmeRanks, cr->nnodes);
2397 if (ddSetup.numPmeRanks > numPPRanks)
2399 gmx_fatal_collective(FARGS, cr->mpi_comm_mysim, MASTER(cr),
2400 "The number of separate PME ranks (%d) is larger than the number of PP ranks (%d), this is not supported.", ddSetup.numPmeRanks, numPPRanks);
2403 ddSetup.numDDDimensions = set_dd_dim(ddSetup.numDomains, ddSettings,
2404 ddSetup.ddDimensions);
2409 /*! \brief Set the cell size and interaction limits, as well as the DD grid */
2411 getDDRankSetup(const gmx::MDLogger &mdlog,
2413 const DDSetup &ddSetup,
2414 const t_inputrec &ir)
2416 GMX_LOG(mdlog.info).appendTextFormatted(
2417 "Domain decomposition grid %d x %d x %d, separate PME ranks %d",
2418 ddSetup.numDomains[XX], ddSetup.numDomains[YY], ddSetup.numDomains[ZZ],
2419 ddSetup.numPmeRanks);
2421 DDRankSetup ddRankSetup;
2423 ddRankSetup.numPPRanks = cr->nnodes - cr->npmenodes;
2424 copy_ivec(ddSetup.numDomains, ddRankSetup.numPPCells);
2426 if (cr->npmenodes > 0)
2428 ddRankSetup.npmenodes = cr->npmenodes;
2432 ddRankSetup.npmenodes = ddSetup.numDomains[XX]*ddSetup.numDomains[YY]*ddSetup.numDomains[ZZ];
2435 if (EEL_PME(ir.coulombtype) || EVDW_PME(ir.vdwtype))
2437 /* The following choices should match those
2438 * in comm_cost_est in domdec_setup.c.
2439 * Note that here the checks have to take into account
2440 * that the decomposition might occur in a different order than xyz
2441 * (for instance through the env.var. GMX_DD_ORDER_ZYX),
2442 * in which case they will not match those in comm_cost_est,
2443 * but since that is mainly for testing purposes that's fine.
2445 if (ddSetup.numDDDimensions >= 2 &&
2446 ddSetup.ddDimensions[0] == XX &&
2447 ddSetup.ddDimensions[1] == YY &&
2448 ddRankSetup.npmenodes > ddSetup.numDomains[XX] &&
2449 ddRankSetup.npmenodes % ddSetup.numDomains[XX] == 0 &&
2450 getenv("GMX_PMEONEDD") == nullptr)
2452 ddRankSetup.npmedecompdim = 2;
2453 ddRankSetup.npmenodes_x = ddSetup.numDomains[XX];
2454 ddRankSetup.npmenodes_y = ddRankSetup.npmenodes/ddRankSetup.npmenodes_x;
2458 /* In case nc is 1 in both x and y we could still choose to
2459 * decompose pme in y instead of x, but we use x for simplicity.
2461 ddRankSetup.npmedecompdim = 1;
2462 if (ddSetup.ddDimensions[0] == YY)
2464 ddRankSetup.npmenodes_x = 1;
2465 ddRankSetup.npmenodes_y = ddRankSetup.npmenodes;
2469 ddRankSetup.npmenodes_x = ddRankSetup.npmenodes;
2470 ddRankSetup.npmenodes_y = 1;
2473 GMX_LOG(mdlog.info).appendTextFormatted(
2474 "PME domain decomposition: %d x %d x %d",
2475 ddRankSetup.npmenodes_x, ddRankSetup.npmenodes_y, 1);
2479 ddRankSetup.npmedecompdim = 0;
2480 ddRankSetup.npmenodes_x = 0;
2481 ddRankSetup.npmenodes_y = 0;
2487 /*! \brief Set the cell size and interaction limits */
2488 static void set_dd_limits(const gmx::MDLogger &mdlog,
2489 t_commrec *cr, gmx_domdec_t *dd,
2490 const DomdecOptions &options,
2491 const DDSettings &ddSettings,
2492 const DDSystemInfo &systemInfo,
2493 const DDSetup &ddSetup,
2494 const gmx_mtop_t *mtop,
2495 const t_inputrec *ir,
2496 const gmx_ddbox_t &ddbox)
2498 gmx_domdec_comm_t *comm = dd->comm;
2499 comm->ddSettings = ddSettings;
2501 /* Initialize to GPU share count to 0, might change later */
2502 comm->nrank_gpu_shared = 0;
2504 comm->dlbState = comm->ddSettings.initialDlbState;
2505 dd_dlb_set_should_check_whether_to_turn_dlb_on(dd, TRUE);
2506 /* To consider turning DLB on after 2*nstlist steps we need to check
2507 * at partitioning count 3. Thus we need to increase the first count by 2.
2509 comm->ddPartioningCountFirstDlbOff += 2;
2511 comm->bPMELoadBalDLBLimits = FALSE;
2513 /* Allocate the charge group/atom sorting struct */
2514 comm->sort = std::make_unique<gmx_domdec_sort_t>();
2516 comm->systemInfo = systemInfo;
2518 if (systemInfo.useUpdateGroups)
2520 /* Note: We would like to use dd->nnodes for the atom count estimate,
2521 * but that is not yet available here. But this anyhow only
2522 * affect performance up to the second dd_partition_system call.
2524 const int homeAtomCountEstimate = mtop->natoms/cr->nnodes;
2525 comm->updateGroupsCog =
2526 std::make_unique<gmx::UpdateGroupsCog>(*mtop,
2527 systemInfo.updateGroupingPerMoleculetype,
2528 maxReferenceTemperature(*ir),
2529 homeAtomCountEstimate);
2532 comm->cgs_gl = gmx_mtop_global_cgs(mtop);
2534 /* Set the DD setup given by ddSetup */
2535 cr->npmenodes = ddSetup.numPmeRanks;
2536 copy_ivec(ddSetup.numDomains, dd->nc);
2537 dd->ndim = ddSetup.numDDDimensions;
2538 copy_ivec(ddSetup.ddDimensions, dd->dim);
2540 dd->nnodes = dd->nc[XX]*dd->nc[YY]*dd->nc[ZZ];
2542 snew(comm->slb_frac, DIM);
2543 if (isDlbDisabled(comm))
2545 comm->slb_frac[XX] = get_slb_frac(mdlog, "x", dd->nc[XX], options.cellSizeX);
2546 comm->slb_frac[YY] = get_slb_frac(mdlog, "y", dd->nc[YY], options.cellSizeY);
2547 comm->slb_frac[ZZ] = get_slb_frac(mdlog, "z", dd->nc[ZZ], options.cellSizeZ);
2550 /* Set the multi-body cut-off and cellsize limit for DLB */
2551 comm->cutoff_mbody = systemInfo.minCutoffForMultiBody;
2552 comm->cellsize_limit = systemInfo.cellsizeLimit;
2553 if (systemInfo.haveInterDomainBondeds && systemInfo.increaseMultiBodyCutoff)
2555 if (systemInfo.filterBondedCommunication || !isDlbDisabled(comm))
2557 /* Set the bonded communication distance to halfway
2558 * the minimum and the maximum,
2559 * since the extra communication cost is nearly zero.
2561 real acs = average_cellsize_min(ddbox, dd->nc);
2562 comm->cutoff_mbody = 0.5*(systemInfo.minCutoffForMultiBody + acs);
2563 if (!isDlbDisabled(comm))
2565 /* Check if this does not limit the scaling */
2566 comm->cutoff_mbody = std::min(comm->cutoff_mbody,
2567 options.dlbScaling*acs);
2569 if (!systemInfo.filterBondedCommunication)
2571 /* Without bBondComm do not go beyond the n.b. cut-off */
2572 comm->cutoff_mbody = std::min(comm->cutoff_mbody, systemInfo.cutoff);
2573 if (comm->cellsize_limit >= systemInfo.cutoff)
2575 /* We don't loose a lot of efficieny
2576 * when increasing it to the n.b. cut-off.
2577 * It can even be slightly faster, because we need
2578 * less checks for the communication setup.
2580 comm->cutoff_mbody = systemInfo.cutoff;
2583 /* Check if we did not end up below our original limit */
2584 comm->cutoff_mbody = std::max(comm->cutoff_mbody,
2585 systemInfo.minCutoffForMultiBody);
2587 if (comm->cutoff_mbody > comm->cellsize_limit)
2589 comm->cellsize_limit = comm->cutoff_mbody;
2592 /* Without DLB and cutoff_mbody<cutoff, cutoff_mbody is dynamic */
2597 fprintf(debug, "Bonded atom communication beyond the cut-off: %s\n"
2598 "cellsize limit %f\n",
2599 gmx::boolToString(systemInfo.filterBondedCommunication),
2600 comm->cellsize_limit);
2605 check_dd_restrictions(dd, ir, mdlog);
2609 static char *init_bLocalCG(const gmx_mtop_t *mtop)
2614 ncg = ncg_mtop(mtop);
2615 snew(bLocalCG, ncg);
2616 for (cg = 0; cg < ncg; cg++)
2618 bLocalCG[cg] = FALSE;
2624 void dd_init_bondeds(FILE *fplog,
2626 const gmx_mtop_t *mtop,
2627 const gmx_vsite_t *vsite,
2628 const t_inputrec *ir,
2629 gmx_bool bBCheck, cginfo_mb_t *cginfo_mb)
2631 gmx_domdec_comm_t *comm;
2633 dd_make_reverse_top(fplog, dd, mtop, vsite, ir, bBCheck);
2637 if (comm->systemInfo.filterBondedCommunication)
2639 /* Communicate atoms beyond the cut-off for bonded interactions */
2642 comm->cglink = make_charge_group_links(mtop, dd, cginfo_mb);
2644 comm->bLocalCG = init_bLocalCG(mtop);
2648 /* Only communicate atoms based on cut-off */
2649 comm->cglink = nullptr;
2650 comm->bLocalCG = nullptr;
2654 static void writeSettings(gmx::TextWriter *log,
2656 const gmx_mtop_t *mtop,
2657 const t_inputrec *ir,
2658 gmx_bool bDynLoadBal,
2660 const gmx_ddbox_t *ddbox)
2662 gmx_domdec_comm_t *comm;
2671 log->writeString("The maximum number of communication pulses is:");
2672 for (d = 0; d < dd->ndim; d++)
2674 log->writeStringFormatted(" %c %d", dim2char(dd->dim[d]), comm->cd[d].np_dlb);
2676 log->ensureLineBreak();
2677 log->writeLineFormatted("The minimum size for domain decomposition cells is %.3f nm", comm->cellsize_limit);
2678 log->writeLineFormatted("The requested allowed shrink of DD cells (option -dds) is: %.2f", dlb_scale);
2679 log->writeString("The allowed shrink of domain decomposition cells is:");
2680 for (d = 0; d < DIM; d++)
2684 if (d >= ddbox->npbcdim && dd->nc[d] == 2)
2691 comm->cellsize_min_dlb[d]/
2692 (ddbox->box_size[d]*ddbox->skew_fac[d]/dd->nc[d]);
2694 log->writeStringFormatted(" %c %.2f", dim2char(d), shrink);
2697 log->ensureLineBreak();
2701 set_dd_cell_sizes_slb(dd, ddbox, setcellsizeslbPULSE_ONLY, np);
2702 log->writeString("The initial number of communication pulses is:");
2703 for (d = 0; d < dd->ndim; d++)
2705 log->writeStringFormatted(" %c %d", dim2char(dd->dim[d]), np[dd->dim[d]]);
2707 log->ensureLineBreak();
2708 log->writeString("The initial domain decomposition cell size is:");
2709 for (d = 0; d < DIM; d++)
2713 log->writeStringFormatted(" %c %.2f nm",
2714 dim2char(d), dd->comm->cellsize_min[d]);
2717 log->ensureLineBreak();
2721 const bool haveInterDomainVsites =
2722 (countInterUpdategroupVsites(*mtop, comm->systemInfo.updateGroupingPerMoleculetype) != 0);
2724 if (comm->systemInfo.haveInterDomainBondeds ||
2725 haveInterDomainVsites ||
2726 comm->systemInfo.haveSplitConstraints ||
2727 comm->systemInfo.haveSplitSettles)
2729 std::string decompUnits;
2730 if (comm->systemInfo.useUpdateGroups)
2732 decompUnits = "atom groups";
2736 decompUnits = "atoms";
2739 log->writeLineFormatted("The maximum allowed distance for %s involved in interactions is:", decompUnits.c_str());
2740 log->writeLineFormatted("%40s %-7s %6.3f nm", "non-bonded interactions", "", comm->systemInfo.cutoff);
2744 limit = dd->comm->cellsize_limit;
2748 if (dd->unitCellInfo.ddBoxIsDynamic)
2750 log->writeLine("(the following are initial values, they could change due to box deformation)");
2752 limit = dd->comm->cellsize_min[XX];
2753 for (d = 1; d < DIM; d++)
2755 limit = std::min(limit, dd->comm->cellsize_min[d]);
2759 if (comm->systemInfo.haveInterDomainBondeds)
2761 log->writeLineFormatted("%40s %-7s %6.3f nm",
2762 "two-body bonded interactions", "(-rdd)",
2763 std::max(comm->systemInfo.cutoff, comm->cutoff_mbody));
2764 log->writeLineFormatted("%40s %-7s %6.3f nm",
2765 "multi-body bonded interactions", "(-rdd)",
2766 (comm->systemInfo.filterBondedCommunication || isDlbOn(dd->comm)) ? comm->cutoff_mbody : std::min(comm->systemInfo.cutoff, limit));
2768 if (haveInterDomainVsites)
2770 log->writeLineFormatted("%40s %-7s %6.3f nm",
2771 "virtual site constructions", "(-rcon)", limit);
2773 if (comm->systemInfo.haveSplitConstraints || comm->systemInfo.haveSplitSettles)
2775 std::string separation = gmx::formatString("atoms separated by up to %d constraints",
2777 log->writeLineFormatted("%40s %-7s %6.3f nm\n",
2778 separation.c_str(), "(-rcon)", limit);
2780 log->ensureLineBreak();
2784 static void logSettings(const gmx::MDLogger &mdlog,
2786 const gmx_mtop_t *mtop,
2787 const t_inputrec *ir,
2789 const gmx_ddbox_t *ddbox)
2791 gmx::StringOutputStream stream;
2792 gmx::TextWriter log(&stream);
2793 writeSettings(&log, dd, mtop, ir, isDlbOn(dd->comm), dlb_scale, ddbox);
2794 if (dd->comm->dlbState == DlbState::offCanTurnOn)
2797 log.ensureEmptyLine();
2798 log.writeLine("When dynamic load balancing gets turned on, these settings will change to:");
2800 writeSettings(&log, dd, mtop, ir, true, dlb_scale, ddbox);
2802 GMX_LOG(mdlog.info).asParagraph().appendText(stream.toString());
2805 static void set_cell_limits_dlb(const gmx::MDLogger &mdlog,
2808 const t_inputrec *ir,
2809 const gmx_ddbox_t *ddbox)
2811 gmx_domdec_comm_t *comm;
2812 int d, dim, npulse, npulse_d_max, npulse_d;
2817 bNoCutOff = (ir->rvdw == 0 || ir->rcoulomb == 0);
2819 /* Determine the maximum number of comm. pulses in one dimension */
2821 comm->cellsize_limit = std::max(comm->cellsize_limit, comm->cutoff_mbody);
2823 /* Determine the maximum required number of grid pulses */
2824 if (comm->cellsize_limit >= comm->systemInfo.cutoff)
2826 /* Only a single pulse is required */
2829 else if (!bNoCutOff && comm->cellsize_limit > 0)
2831 /* We round down slightly here to avoid overhead due to the latency
2832 * of extra communication calls when the cut-off
2833 * would be only slightly longer than the cell size.
2834 * Later cellsize_limit is redetermined,
2835 * so we can not miss interactions due to this rounding.
2837 npulse = static_cast<int>(0.96 + comm->systemInfo.cutoff/comm->cellsize_limit);
2841 /* There is no cell size limit */
2842 npulse = std::max(dd->nc[XX]-1, std::max(dd->nc[YY]-1, dd->nc[ZZ]-1));
2845 if (!bNoCutOff && npulse > 1)
2847 /* See if we can do with less pulses, based on dlb_scale */
2849 for (d = 0; d < dd->ndim; d++)
2852 npulse_d = static_cast<int>(1 + dd->nc[dim]*comm->systemInfo.cutoff
2853 /(ddbox->box_size[dim]*ddbox->skew_fac[dim]*dlb_scale));
2854 npulse_d_max = std::max(npulse_d_max, npulse_d);
2856 npulse = std::min(npulse, npulse_d_max);
2859 /* This env var can override npulse */
2860 d = dd_getenv(mdlog, "GMX_DD_NPULSE", 0);
2867 comm->bVacDLBNoLimit = (ir->ePBC == epbcNONE);
2868 for (d = 0; d < dd->ndim; d++)
2870 comm->cd[d].np_dlb = std::min(npulse, dd->nc[dd->dim[d]]-1);
2871 comm->maxpulse = std::max(comm->maxpulse, comm->cd[d].np_dlb);
2872 if (comm->cd[d].np_dlb < dd->nc[dd->dim[d]]-1)
2874 comm->bVacDLBNoLimit = FALSE;
2878 /* cellsize_limit is set for LINCS in init_domain_decomposition */
2879 if (!comm->bVacDLBNoLimit)
2881 comm->cellsize_limit = std::max(comm->cellsize_limit,
2882 comm->systemInfo.cutoff/comm->maxpulse);
2884 comm->cellsize_limit = std::max(comm->cellsize_limit, comm->cutoff_mbody);
2885 /* Set the minimum cell size for each DD dimension */
2886 for (d = 0; d < dd->ndim; d++)
2888 if (comm->bVacDLBNoLimit ||
2889 comm->cd[d].np_dlb*comm->cellsize_limit >= comm->systemInfo.cutoff)
2891 comm->cellsize_min_dlb[dd->dim[d]] = comm->cellsize_limit;
2895 comm->cellsize_min_dlb[dd->dim[d]] =
2896 comm->systemInfo.cutoff/comm->cd[d].np_dlb;
2899 if (comm->cutoff_mbody <= 0)
2901 comm->cutoff_mbody = std::min(comm->systemInfo.cutoff, comm->cellsize_limit);
2909 gmx_bool dd_bonded_molpbc(const gmx_domdec_t *dd, int ePBC)
2911 /* If each molecule is a single charge group
2912 * or we use domain decomposition for each periodic dimension,
2913 * we do not need to take pbc into account for the bonded interactions.
2915 return (ePBC != epbcNONE && dd->comm->systemInfo.haveInterDomainBondeds &&
2918 (dd->nc[ZZ] > 1 || ePBC == epbcXY)));
2921 /*! \brief Sets grid size limits and PP-PME setup, prints settings to log */
2922 static void set_ddgrid_parameters(const gmx::MDLogger &mdlog,
2923 gmx_domdec_t *dd, real dlb_scale,
2924 const gmx_mtop_t *mtop, const t_inputrec *ir,
2925 const gmx_ddbox_t *ddbox)
2927 gmx_domdec_comm_t *comm = dd->comm;
2928 DDRankSetup &ddRankSetup = comm->ddRankSetup;
2930 if (EEL_PME(ir->coulombtype) || EVDW_PME(ir->vdwtype))
2932 init_ddpme(dd, &ddRankSetup.ddpme[0], 0);
2933 if (ddRankSetup.npmedecompdim >= 2)
2935 init_ddpme(dd, &ddRankSetup.ddpme[1], 1);
2940 ddRankSetup.npmenodes = 0;
2941 if (dd->pme_nodeid >= 0)
2943 gmx_fatal_collective(FARGS, dd->mpi_comm_all, DDMASTER(dd),
2944 "Can not have separate PME ranks without PME electrostatics");
2950 fprintf(debug, "The DD cut-off is %f\n", comm->systemInfo.cutoff);
2952 if (!isDlbDisabled(comm))
2954 set_cell_limits_dlb(mdlog, dd, dlb_scale, ir, ddbox);
2957 logSettings(mdlog, dd, mtop, ir, dlb_scale, ddbox);
2960 if (ir->ePBC == epbcNONE)
2962 vol_frac = 1 - 1/static_cast<double>(dd->nnodes);
2967 (1 + comm_box_frac(dd->nc, comm->systemInfo.cutoff, ddbox))/static_cast<double>(dd->nnodes);
2971 fprintf(debug, "Volume fraction for all DD zones: %f\n", vol_frac);
2973 int natoms_tot = comm->cgs_gl.index[comm->cgs_gl.nr];
2975 dd->ga2la = new gmx_ga2la_t(natoms_tot,
2976 static_cast<int>(vol_frac*natoms_tot));
2979 /*! \brief Get some important DD parameters which can be modified by env.vars */
2981 getDDSettings(const gmx::MDLogger &mdlog,
2982 const DomdecOptions &options,
2983 const gmx::MdrunOptions &mdrunOptions,
2984 const t_inputrec &ir)
2986 DDSettings ddSettings;
2988 ddSettings.useSendRecv2 = (dd_getenv(mdlog, "GMX_DD_USE_SENDRECV2", 0) != 0);
2989 ddSettings.dlb_scale_lim = dd_getenv(mdlog, "GMX_DLB_MAX_BOX_SCALING", 10);
2990 ddSettings.useDDOrderZYX = bool(dd_getenv(mdlog, "GMX_DD_ORDER_ZYX", 0));
2991 ddSettings.useCartesianReorder = bool(dd_getenv(mdlog, "GMX_NO_CART_REORDER", 1));
2992 ddSettings.eFlop = dd_getenv(mdlog, "GMX_DLB_BASED_ON_FLOPS", 0);
2993 const int recload = dd_getenv(mdlog, "GMX_DD_RECORD_LOAD", 1);
2994 ddSettings.nstDDDump = dd_getenv(mdlog, "GMX_DD_NST_DUMP", 0);
2995 ddSettings.nstDDDumpGrid = dd_getenv(mdlog, "GMX_DD_NST_DUMP_GRID", 0);
2996 ddSettings.DD_debug = dd_getenv(mdlog, "GMX_DD_DEBUG", 0);
2998 if (ddSettings.useSendRecv2)
3000 GMX_LOG(mdlog.info).appendText("Will use two sequential MPI_Sendrecv calls instead of two simultaneous non-blocking MPI_Irecv and MPI_Isend pairs for constraint and vsite communication");
3003 if (ddSettings.eFlop)
3005 GMX_LOG(mdlog.info).appendText("Will load balance based on FLOP count");
3006 ddSettings.recordLoad = true;
3010 ddSettings.recordLoad = (wallcycle_have_counter() && recload > 0);
3013 ddSettings.initialDlbState =
3014 determineInitialDlbState(mdlog, options.dlbOption, ddSettings.recordLoad, mdrunOptions, &ir);
3015 GMX_LOG(mdlog.info).appendTextFormatted("Dynamic load balancing: %s",
3016 edlbs_names[static_cast<int>(ddSettings.initialDlbState)]);
3021 gmx_domdec_t::gmx_domdec_t(const t_inputrec &ir) :
3026 gmx_domdec_t *init_domain_decomposition(const gmx::MDLogger &mdlog,
3028 const DomdecOptions &options,
3029 const gmx::MdrunOptions &mdrunOptions,
3030 const gmx_mtop_t *mtop,
3031 const t_inputrec *ir,
3033 gmx::ArrayRef<const gmx::RVec> xGlobal,
3034 gmx::LocalAtomSetManager *atomSets)
3036 GMX_LOG(mdlog.info).appendTextFormatted(
3037 "\nInitializing Domain Decomposition on %d ranks", cr->nnodes);
3039 DDSettings ddSettings = getDDSettings(mdlog, options, mdrunOptions, *ir);
3040 if (ddSettings.eFlop > 1)
3042 /* Ensure that we have different random flop counts on different ranks */
3043 srand(1 + cr->nodeid);
3046 DDSystemInfo systemInfo = getSystemInfo(mdlog, cr, options, mtop, ir, box, xGlobal);
3048 gmx_ddbox_t ddbox = {0};
3049 DDSetup ddSetup = getDDSetup(mdlog, cr, options, ddSettings, systemInfo,
3050 mtop, ir, box, xGlobal, &ddbox);
3052 cr->npmenodes = ddSetup.numPmeRanks;
3054 DDRankSetup ddRankSetup = getDDRankSetup(mdlog, cr, ddSetup, *ir);
3056 ivec ddCellIndex = { 0, 0, 0 };
3057 makeGroupCommunicators(mdlog, ddSettings, ddSetup, options.rankOrder,
3058 &ddRankSetup, cr, ddCellIndex);
3060 gmx_domdec_t *dd = new gmx_domdec_t(*ir);
3062 copy_ivec(ddCellIndex, dd->ci);
3064 dd->comm = init_dd_comm();
3066 dd->comm->ddRankSetup = ddRankSetup;
3068 set_dd_limits(mdlog, cr, dd, options,
3069 ddSettings, systemInfo, ddSetup,
3073 setupGroupCommunication(mdlog, ddSettings, cr, dd);
3075 if (thisRankHasDuty(cr, DUTY_PP))
3077 set_ddgrid_parameters(mdlog, dd, options.dlbScaling, mtop, ir, &ddbox);
3079 setup_neighbor_relations(dd);
3082 /* Set overallocation to avoid frequent reallocation of arrays */
3083 set_over_alloc_dd(TRUE);
3085 dd->atomSets = atomSets;
3090 static gmx_bool test_dd_cutoff(t_commrec *cr,
3092 gmx::ArrayRef<const gmx::RVec> x,
3093 real cutoffRequested)
3103 set_ddbox(*dd, false, box, true, x, &ddbox);
3107 for (d = 0; d < dd->ndim; d++)
3111 inv_cell_size = DD_CELL_MARGIN*dd->nc[dim]/ddbox.box_size[dim];
3112 if (dd->unitCellInfo.ddBoxIsDynamic)
3114 inv_cell_size *= DD_PRES_SCALE_MARGIN;
3117 np = 1 + static_cast<int>(cutoffRequested*inv_cell_size*ddbox.skew_fac[dim]);
3119 if (!isDlbDisabled(dd->comm) && (dim < ddbox.npbcdim) && (dd->comm->cd[d].np_dlb > 0))
3121 if (np > dd->comm->cd[d].np_dlb)
3126 /* If a current local cell size is smaller than the requested
3127 * cut-off, we could still fix it, but this gets very complicated.
3128 * Without fixing here, we might actually need more checks.
3130 real cellSizeAlongDim = (dd->comm->cell_x1[dim] - dd->comm->cell_x0[dim])*ddbox.skew_fac[dim];
3131 if (cellSizeAlongDim*dd->comm->cd[d].np_dlb < cutoffRequested)
3138 if (!isDlbDisabled(dd->comm))
3140 /* If DLB is not active yet, we don't need to check the grid jumps.
3141 * Actually we shouldn't, because then the grid jump data is not set.
3143 if (isDlbOn(dd->comm) &&
3144 check_grid_jump(0, dd, cutoffRequested, &ddbox, FALSE))
3149 gmx_sumi(1, &LocallyLimited, cr);
3151 if (LocallyLimited > 0)
3160 gmx_bool change_dd_cutoff(t_commrec *cr,
3162 gmx::ArrayRef<const gmx::RVec> x,
3163 real cutoffRequested)
3165 gmx_bool bCutoffAllowed;
3167 bCutoffAllowed = test_dd_cutoff(cr, box, x, cutoffRequested);
3171 cr->dd->comm->systemInfo.cutoff = cutoffRequested;
3174 return bCutoffAllowed;