Update instructions in containers.rst
[gromacs.git] / src / gromacs / domdec / distribute.cpp
blob062acc6b570c712254536c922220ad6dcb1a1adc
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2018,2019,2020, 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.
35 /* \internal \file
37 * \brief Implements atom distribution functions.
39 * \author Berk Hess <hess@kth.se>
40 * \ingroup module_domdec
43 #include "gmxpre.h"
45 #include "distribute.h"
47 #include "config.h"
49 #include <vector>
51 #include "gromacs/domdec/domdec_network.h"
52 #include "gromacs/math/vec.h"
53 #include "gromacs/mdtypes/commrec.h"
54 #include "gromacs/mdtypes/df_history.h"
55 #include "gromacs/mdtypes/state.h"
56 #include "gromacs/topology/topology.h"
57 #include "gromacs/utility/fatalerror.h"
58 #include "gromacs/utility/logger.h"
60 #include "atomdistribution.h"
61 #include "cellsizes.h"
62 #include "domdec_internal.h"
63 #include "utility.h"
65 static void distributeVecSendrecv(gmx_domdec_t* dd,
66 gmx::ArrayRef<const gmx::RVec> globalVec,
67 gmx::ArrayRef<gmx::RVec> localVec)
69 if (DDMASTER(dd))
71 std::vector<gmx::RVec> buffer;
73 for (int rank = 0; rank < dd->nnodes; rank++)
75 if (rank != dd->rank)
77 const auto& domainGroups = dd->ma->domainGroups[rank];
79 buffer.resize(domainGroups.numAtoms);
81 int localAtom = 0;
82 for (const int& globalAtom : domainGroups.atomGroups)
84 buffer[localAtom++] = globalVec[globalAtom];
86 GMX_RELEASE_ASSERT(localAtom == domainGroups.numAtoms,
87 "The index count and number of indices should match");
89 #if GMX_MPI
90 MPI_Send(buffer.data(), domainGroups.numAtoms * sizeof(gmx::RVec), MPI_BYTE, rank,
91 rank, dd->mpi_comm_all);
92 #endif
96 const auto& domainGroups = dd->ma->domainGroups[dd->masterrank];
97 int localAtom = 0;
98 for (const int& globalAtom : domainGroups.atomGroups)
100 localVec[localAtom++] = globalVec[globalAtom];
103 else
105 #if GMX_MPI
106 int numHomeAtoms = dd->comm->atomRanges.numHomeAtoms();
107 MPI_Recv(localVec.data(), numHomeAtoms * sizeof(gmx::RVec), MPI_BYTE, dd->masterrank,
108 MPI_ANY_TAG, dd->mpi_comm_all, MPI_STATUS_IGNORE);
109 #endif
113 static void distributeVecScatterv(gmx_domdec_t* dd,
114 gmx::ArrayRef<const gmx::RVec> globalVec,
115 gmx::ArrayRef<gmx::RVec> localVec)
117 int* sendCounts = nullptr;
118 int* displacements = nullptr;
120 if (DDMASTER(dd))
122 AtomDistribution& ma = *dd->ma;
124 get_commbuffer_counts(&ma, &sendCounts, &displacements);
126 gmx::ArrayRef<gmx::RVec> buffer = ma.rvecBuffer;
127 int localAtom = 0;
128 for (int rank = 0; rank < dd->nnodes; rank++)
130 const auto& domainGroups = ma.domainGroups[rank];
131 for (const int& globalAtom : domainGroups.atomGroups)
133 buffer[localAtom++] = globalVec[globalAtom];
138 int numHomeAtoms = dd->comm->atomRanges.numHomeAtoms();
139 dd_scatterv(dd, sendCounts, displacements, DDMASTER(dd) ? dd->ma->rvecBuffer.data() : nullptr,
140 numHomeAtoms * sizeof(gmx::RVec), localVec.data());
143 static void distributeVec(gmx_domdec_t* dd,
144 gmx::ArrayRef<const gmx::RVec> globalVec,
145 gmx::ArrayRef<gmx::RVec> localVec)
147 if (dd->nnodes <= c_maxNumRanksUseSendRecvForScatterAndGather)
149 distributeVecSendrecv(dd, globalVec, localVec);
151 else
153 distributeVecScatterv(dd, globalVec, localVec);
157 static void dd_distribute_dfhist(gmx_domdec_t* dd, df_history_t* dfhist)
159 if (dfhist == nullptr)
161 return;
164 dd_bcast(dd, sizeof(int), &dfhist->bEquil);
165 dd_bcast(dd, sizeof(int), &dfhist->nlambda);
166 dd_bcast(dd, sizeof(real), &dfhist->wl_delta);
168 if (dfhist->nlambda > 0)
170 int nlam = dfhist->nlambda;
171 dd_bcast(dd, sizeof(int) * nlam, dfhist->n_at_lam);
172 dd_bcast(dd, sizeof(real) * nlam, dfhist->wl_histo);
173 dd_bcast(dd, sizeof(real) * nlam, dfhist->sum_weights);
174 dd_bcast(dd, sizeof(real) * nlam, dfhist->sum_dg);
175 dd_bcast(dd, sizeof(real) * nlam, dfhist->sum_minvar);
176 dd_bcast(dd, sizeof(real) * nlam, dfhist->sum_variance);
178 for (int i = 0; i < nlam; i++)
180 dd_bcast(dd, sizeof(real) * nlam, dfhist->accum_p[i]);
181 dd_bcast(dd, sizeof(real) * nlam, dfhist->accum_m[i]);
182 dd_bcast(dd, sizeof(real) * nlam, dfhist->accum_p2[i]);
183 dd_bcast(dd, sizeof(real) * nlam, dfhist->accum_m2[i]);
184 dd_bcast(dd, sizeof(real) * nlam, dfhist->Tij[i]);
185 dd_bcast(dd, sizeof(real) * nlam, dfhist->Tij_empirical[i]);
190 static void dd_distribute_state(gmx_domdec_t* dd, const t_state* state, t_state* state_local)
192 int nh = state_local->nhchainlength;
194 if (DDMASTER(dd))
196 GMX_RELEASE_ASSERT(state->nhchainlength == nh,
197 "The global and local Nose-Hoover chain lengths should match");
199 for (int i = 0; i < efptNR; i++)
201 state_local->lambda[i] = state->lambda[i];
203 state_local->fep_state = state->fep_state;
204 state_local->veta = state->veta;
205 state_local->vol0 = state->vol0;
206 copy_mat(state->box, state_local->box);
207 copy_mat(state->box_rel, state_local->box_rel);
208 copy_mat(state->boxv, state_local->boxv);
209 copy_mat(state->svir_prev, state_local->svir_prev);
210 copy_mat(state->fvir_prev, state_local->fvir_prev);
211 if (state->dfhist != nullptr)
213 copy_df_history(state_local->dfhist, state->dfhist);
215 for (int i = 0; i < state_local->ngtc; i++)
217 for (int j = 0; j < nh; j++)
219 state_local->nosehoover_xi[i * nh + j] = state->nosehoover_xi[i * nh + j];
220 state_local->nosehoover_vxi[i * nh + j] = state->nosehoover_vxi[i * nh + j];
222 state_local->therm_integral[i] = state->therm_integral[i];
224 for (int i = 0; i < state_local->nnhpres; i++)
226 for (int j = 0; j < nh; j++)
228 state_local->nhpres_xi[i * nh + j] = state->nhpres_xi[i * nh + j];
229 state_local->nhpres_vxi[i * nh + j] = state->nhpres_vxi[i * nh + j];
232 state_local->baros_integral = state->baros_integral;
234 dd_bcast(dd, ((efptNR) * sizeof(real)), state_local->lambda.data());
235 dd_bcast(dd, sizeof(int), &state_local->fep_state);
236 dd_bcast(dd, sizeof(real), &state_local->veta);
237 dd_bcast(dd, sizeof(real), &state_local->vol0);
238 dd_bcast(dd, sizeof(state_local->box), state_local->box);
239 dd_bcast(dd, sizeof(state_local->box_rel), state_local->box_rel);
240 dd_bcast(dd, sizeof(state_local->boxv), state_local->boxv);
241 dd_bcast(dd, sizeof(state_local->svir_prev), state_local->svir_prev);
242 dd_bcast(dd, sizeof(state_local->fvir_prev), state_local->fvir_prev);
243 dd_bcast(dd, ((state_local->ngtc * nh) * sizeof(double)), state_local->nosehoover_xi.data());
244 dd_bcast(dd, ((state_local->ngtc * nh) * sizeof(double)), state_local->nosehoover_vxi.data());
245 dd_bcast(dd, state_local->ngtc * sizeof(double), state_local->therm_integral.data());
246 dd_bcast(dd, ((state_local->nnhpres * nh) * sizeof(double)), state_local->nhpres_xi.data());
247 dd_bcast(dd, ((state_local->nnhpres * nh) * sizeof(double)), state_local->nhpres_vxi.data());
249 /* communicate df_history -- required for restarting from checkpoint */
250 dd_distribute_dfhist(dd, state_local->dfhist);
252 state_change_natoms(state_local, dd->comm->atomRanges.numHomeAtoms());
254 if (state_local->flags & (1 << estX))
256 distributeVec(dd, DDMASTER(dd) ? state->x : gmx::ArrayRef<const gmx::RVec>(), state_local->x);
258 if (state_local->flags & (1 << estV))
260 distributeVec(dd, DDMASTER(dd) ? state->v : gmx::ArrayRef<const gmx::RVec>(), state_local->v);
262 if (state_local->flags & (1 << estCGP))
264 distributeVec(dd, DDMASTER(dd) ? state->cg_p : gmx::ArrayRef<const gmx::RVec>(), state_local->cg_p);
268 /* Computes and returns the domain index for the given atom group.
270 * Also updates the coordinates in pos for PBC, when necessary.
272 static inline int computeAtomGroupDomainIndex(const gmx_domdec_t& dd,
273 const gmx_ddbox_t& ddbox,
274 const matrix& triclinicCorrectionMatrix,
275 gmx::ArrayRef<const std::vector<real>> cellBoundaries,
276 int atomBegin,
277 int atomEnd,
278 const matrix box,
279 rvec* pos)
281 /* Set the reference location cg_cm for assigning the group */
282 rvec cog;
283 int numAtoms = atomEnd - atomBegin;
284 if (numAtoms == 1)
286 copy_rvec(pos[atomBegin], cog);
288 else
290 real invNumAtoms = 1 / static_cast<real>(numAtoms);
292 clear_rvec(cog);
293 for (int a = atomBegin; a < atomEnd; a++)
295 rvec_inc(cog, pos[a]);
297 for (int d = 0; d < DIM; d++)
299 cog[d] *= invNumAtoms;
302 /* Put the charge group in the box and determine the cell index ind */
303 ivec ind;
304 for (int d = DIM - 1; d >= 0; d--)
306 real pos_d = cog[d];
307 if (d < dd.unitCellInfo.npbcdim)
309 bool bScrew = (dd.unitCellInfo.haveScrewPBC && d == XX);
310 if (ddbox.tric_dir[d] && dd.numCells[d] > 1)
312 /* Use triclinic coordinates for this dimension */
313 for (int j = d + 1; j < DIM; j++)
315 pos_d += cog[j] * triclinicCorrectionMatrix[j][d];
318 while (pos_d >= box[d][d])
320 pos_d -= box[d][d];
321 rvec_dec(cog, box[d]);
322 if (bScrew)
324 cog[YY] = box[YY][YY] - cog[YY];
325 cog[ZZ] = box[ZZ][ZZ] - cog[ZZ];
327 for (int a = atomBegin; a < atomEnd; a++)
329 rvec_dec(pos[a], box[d]);
330 if (bScrew)
332 pos[a][YY] = box[YY][YY] - pos[a][YY];
333 pos[a][ZZ] = box[ZZ][ZZ] - pos[a][ZZ];
337 while (pos_d < 0)
339 pos_d += box[d][d];
340 rvec_inc(cog, box[d]);
341 if (bScrew)
343 cog[YY] = box[YY][YY] - cog[YY];
344 cog[ZZ] = box[ZZ][ZZ] - cog[ZZ];
346 for (int a = atomBegin; a < atomEnd; a++)
348 rvec_inc(pos[a], box[d]);
349 if (bScrew)
351 pos[a][YY] = box[YY][YY] - pos[a][YY];
352 pos[a][ZZ] = box[ZZ][ZZ] - pos[a][ZZ];
357 /* This could be done more efficiently */
358 ind[d] = 0;
359 while (ind[d] + 1 < dd.numCells[d] && pos_d >= cellBoundaries[d][ind[d] + 1])
361 ind[d]++;
365 return dd_index(dd.numCells, ind);
369 static std::vector<std::vector<int>> getAtomGroupDistribution(const gmx::MDLogger& mdlog,
370 const gmx_mtop_t& mtop,
371 const matrix box,
372 const gmx_ddbox_t& ddbox,
373 rvec pos[],
374 gmx_domdec_t* dd)
376 AtomDistribution& ma = *dd->ma;
378 /* Clear the count */
379 for (int rank = 0; rank < dd->nnodes; rank++)
381 ma.domainGroups[rank].numAtoms = 0;
384 matrix triclinicCorrectionMatrix;
385 make_tric_corr_matrix(dd->unitCellInfo.npbcdim, box, triclinicCorrectionMatrix);
387 ivec npulse;
388 const auto cellBoundaries = set_dd_cell_sizes_slb(dd, &ddbox, setcellsizeslbMASTER, npulse);
390 std::vector<std::vector<int>> indices(dd->nnodes);
392 if (dd->comm->systemInfo.useUpdateGroups)
394 int atomOffset = 0;
395 for (const gmx_molblock_t& molblock : mtop.molblock)
397 const auto& updateGrouping =
398 dd->comm->systemInfo.updateGroupingPerMoleculetype[molblock.type];
400 for (int mol = 0; mol < molblock.nmol; mol++)
402 for (int g = 0; g < updateGrouping.numBlocks(); g++)
404 const auto& block = updateGrouping.block(g);
405 const int atomBegin = atomOffset + block.begin();
406 const int atomEnd = atomOffset + block.end();
407 const int domainIndex =
408 computeAtomGroupDomainIndex(*dd, ddbox, triclinicCorrectionMatrix,
409 cellBoundaries, atomBegin, atomEnd, box, pos);
411 for (int atomIndex : block)
413 indices[domainIndex].push_back(atomOffset + atomIndex);
415 ma.domainGroups[domainIndex].numAtoms += block.size();
418 atomOffset += updateGrouping.fullRange().end();
422 GMX_RELEASE_ASSERT(atomOffset == mtop.natoms, "Should distribute all atoms");
424 else
426 /* Compute the center of geometry for all atoms */
427 for (int atom = 0; atom < mtop.natoms; atom++)
429 int domainIndex = computeAtomGroupDomainIndex(*dd, ddbox, triclinicCorrectionMatrix,
430 cellBoundaries, atom, atom + 1, box, pos);
432 indices[domainIndex].push_back(atom);
433 ma.domainGroups[domainIndex].numAtoms += 1;
438 // Use double for the sums to avoid natoms^2 overflowing
439 // (65537^2 > 2^32)
440 int nat_sum, nat_min, nat_max;
441 double nat2_sum;
443 nat_sum = 0;
444 nat2_sum = 0;
445 nat_min = ma.domainGroups[0].numAtoms;
446 nat_max = ma.domainGroups[0].numAtoms;
447 for (int rank = 0; rank < dd->nnodes; rank++)
449 int numAtoms = ma.domainGroups[rank].numAtoms;
450 nat_sum += numAtoms;
451 // convert to double to avoid integer overflows when squaring
452 nat2_sum += gmx::square(double(numAtoms));
453 nat_min = std::min(nat_min, numAtoms);
454 nat_max = std::max(nat_max, numAtoms);
456 nat_sum /= dd->nnodes;
457 nat2_sum /= dd->nnodes;
459 GMX_LOG(mdlog.info)
460 .appendTextFormatted(
461 "Atom distribution over %d domains: av %d stddev %d min %d max %d",
462 dd->nnodes, nat_sum,
463 gmx::roundToInt(std::sqrt(nat2_sum - gmx::square(static_cast<double>(nat_sum)))),
464 nat_min, nat_max);
467 return indices;
470 static void distributeAtomGroups(const gmx::MDLogger& mdlog,
471 gmx_domdec_t* dd,
472 const gmx_mtop_t& mtop,
473 const matrix box,
474 const gmx_ddbox_t* ddbox,
475 rvec pos[])
477 AtomDistribution* ma = dd->ma.get();
478 int * ibuf, buf2[2] = { 0, 0 };
479 gmx_bool bMaster = DDMASTER(dd);
481 std::vector<std::vector<int>> groupIndices;
483 if (bMaster)
485 GMX_ASSERT(box && pos, "box or pos not set on master");
487 if (dd->unitCellInfo.haveScrewPBC)
489 check_screw_box(box);
492 groupIndices = getAtomGroupDistribution(mdlog, mtop, box, *ddbox, pos, dd);
494 for (int rank = 0; rank < dd->nnodes; rank++)
496 ma->intBuffer[rank * 2] = groupIndices[rank].size();
497 ma->intBuffer[rank * 2 + 1] = ma->domainGroups[rank].numAtoms;
499 ibuf = ma->intBuffer.data();
501 else
503 ibuf = nullptr;
505 dd_scatter(dd, 2 * sizeof(int), ibuf, buf2);
507 dd->ncg_home = buf2[0];
508 dd->comm->atomRanges.setEnd(DDAtomRanges::Type::Home, buf2[1]);
509 dd->globalAtomGroupIndices.resize(dd->ncg_home);
510 dd->globalAtomIndices.resize(dd->comm->atomRanges.numHomeAtoms());
512 if (bMaster)
514 ma->atomGroups.clear();
516 int groupOffset = 0;
517 for (int rank = 0; rank < dd->nnodes; rank++)
519 ma->intBuffer[rank] = groupIndices[rank].size() * sizeof(int);
520 ma->intBuffer[dd->nnodes + rank] = groupOffset * sizeof(int);
522 ma->atomGroups.insert(ma->atomGroups.end(), groupIndices[rank].begin(),
523 groupIndices[rank].end());
525 ma->domainGroups[rank].atomGroups = gmx::constArrayRefFromArray(
526 ma->atomGroups.data() + groupOffset, groupIndices[rank].size());
528 groupOffset += groupIndices[rank].size();
532 dd_scatterv(dd, bMaster ? ma->intBuffer.data() : nullptr,
533 bMaster ? ma->intBuffer.data() + dd->nnodes : nullptr,
534 bMaster ? ma->atomGroups.data() : nullptr, dd->ncg_home * sizeof(int),
535 dd->globalAtomGroupIndices.data());
537 if (debug)
539 fprintf(debug, "Home charge groups:\n");
540 for (int i = 0; i < dd->ncg_home; i++)
542 fprintf(debug, " %d", dd->globalAtomGroupIndices[i]);
543 if (i % 10 == 9)
545 fprintf(debug, "\n");
548 fprintf(debug, "\n");
552 void distributeState(const gmx::MDLogger& mdlog,
553 gmx_domdec_t* dd,
554 const gmx_mtop_t& mtop,
555 t_state* state_global,
556 const gmx_ddbox_t& ddbox,
557 t_state* state_local)
559 rvec* xGlobal = (DDMASTER(dd) ? state_global->x.rvec_array() : nullptr);
561 distributeAtomGroups(mdlog, dd, mtop, DDMASTER(dd) ? state_global->box : nullptr, &ddbox, xGlobal);
563 dd_distribute_state(dd, state_global, state_local);