Convert gmx_domdec_master_t to C++
[gromacs.git] / src / gromacs / domdec / distribute.cpp
blob8149f01755967cffa03a304cdbac40c135ff9d09
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2018, 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/utility/fatalerror.h"
57 #include "gromacs/utility/smalloc.h"
59 #include "atomdistribution.h"
60 #include "cellsizes.h"
61 #include "domdec_internal.h"
62 #include "utility.h"
64 static void dd_distribute_vec_sendrecv(gmx_domdec_t *dd, const t_block *cgs,
65 const rvec *v, rvec *lv)
67 AtomDistribution *ma;
68 int nalloc = 0;
69 rvec *buf = nullptr;
71 if (DDMASTER(dd))
73 ma = dd->ma;
75 for (int rank = 0; rank < dd->nnodes; rank++)
77 if (rank != dd->rank)
79 const auto &domainGroups = ma->domainGroups[rank];
81 if (domainGroups.numAtoms > nalloc)
83 nalloc = over_alloc_dd(domainGroups.numAtoms);
84 srenew(buf, nalloc);
86 /* Use lv as a temporary buffer */
87 int localAtom = 0;
88 for (const int &cg : domainGroups.atomGroups)
90 for (int globalAtom = cgs->index[cg]; globalAtom < cgs->index[cg + 1]; globalAtom++)
92 copy_rvec(v[globalAtom], buf[localAtom++]);
95 GMX_RELEASE_ASSERT(localAtom == domainGroups.numAtoms, "The index count and number of indices should match");
97 #if GMX_MPI
98 MPI_Send(buf, domainGroups.numAtoms*sizeof(rvec), MPI_BYTE,
99 rank, rank, dd->mpi_comm_all);
100 #endif
103 sfree(buf);
105 const auto &domainGroups = ma->domainGroups[dd->masterrank];
106 int localAtom = 0;
107 for (const int &cg : domainGroups.atomGroups)
109 for (int globalAtom = cgs->index[cg]; globalAtom < cgs->index[cg + 1]; globalAtom++)
111 copy_rvec(v[globalAtom], lv[localAtom++]);
115 else
117 #if GMX_MPI
118 MPI_Recv(lv, dd->nat_home*sizeof(rvec), MPI_BYTE, dd->masterrank,
119 MPI_ANY_TAG, dd->mpi_comm_all, MPI_STATUS_IGNORE);
120 #endif
124 static void dd_distribute_vec_scatterv(gmx_domdec_t *dd, const t_block *cgs,
125 const rvec *v, rvec *lv)
127 int *sendCounts = nullptr;
128 int *displacements = nullptr;
130 if (DDMASTER(dd))
132 AtomDistribution *ma = dd->ma;
134 get_commbuffer_counts(ma, &sendCounts, &displacements);
136 gmx::ArrayRef<gmx::RVec> buffer = ma->rvecBuffer;
137 int localAtom = 0;
138 for (int rank = 0; rank < dd->nnodes; rank++)
140 const auto &domainGroups = ma->domainGroups[rank];
141 for (const int &cg : domainGroups.atomGroups)
143 for (int globalAtom = cgs->index[cg]; globalAtom < cgs->index[cg + 1]; globalAtom++)
145 copy_rvec(v[globalAtom], buffer[localAtom++]);
151 dd_scatterv(dd, sendCounts, displacements,
152 DDMASTER(dd) ? dd->ma->rvecBuffer.data() : nullptr,
153 dd->nat_home*sizeof(rvec), lv);
156 static void dd_distribute_vec(gmx_domdec_t *dd, const t_block *cgs,
157 const rvec *v, rvec *lv)
159 if (dd->nnodes <= c_maxNumRanksUseSendRecvForScatterAndGather)
161 dd_distribute_vec_sendrecv(dd, cgs, v, lv);
163 else
165 dd_distribute_vec_scatterv(dd, cgs, v, lv);
169 static void dd_distribute_dfhist(gmx_domdec_t *dd, df_history_t *dfhist)
171 if (dfhist == nullptr)
173 return;
176 dd_bcast(dd, sizeof(int), &dfhist->bEquil);
177 dd_bcast(dd, sizeof(int), &dfhist->nlambda);
178 dd_bcast(dd, sizeof(real), &dfhist->wl_delta);
180 if (dfhist->nlambda > 0)
182 int nlam = dfhist->nlambda;
183 dd_bcast(dd, sizeof(int)*nlam, dfhist->n_at_lam);
184 dd_bcast(dd, sizeof(real)*nlam, dfhist->wl_histo);
185 dd_bcast(dd, sizeof(real)*nlam, dfhist->sum_weights);
186 dd_bcast(dd, sizeof(real)*nlam, dfhist->sum_dg);
187 dd_bcast(dd, sizeof(real)*nlam, dfhist->sum_minvar);
188 dd_bcast(dd, sizeof(real)*nlam, dfhist->sum_variance);
190 for (int i = 0; i < nlam; i++)
192 dd_bcast(dd, sizeof(real)*nlam, dfhist->accum_p[i]);
193 dd_bcast(dd, sizeof(real)*nlam, dfhist->accum_m[i]);
194 dd_bcast(dd, sizeof(real)*nlam, dfhist->accum_p2[i]);
195 dd_bcast(dd, sizeof(real)*nlam, dfhist->accum_m2[i]);
196 dd_bcast(dd, sizeof(real)*nlam, dfhist->Tij[i]);
197 dd_bcast(dd, sizeof(real)*nlam, dfhist->Tij_empirical[i]);
202 static void dd_distribute_state(gmx_domdec_t *dd, const t_block *cgs,
203 const t_state *state, t_state *state_local,
204 PaddedRVecVector *f)
206 int nh = state_local->nhchainlength;
208 if (DDMASTER(dd))
210 GMX_RELEASE_ASSERT(state->nhchainlength == nh, "The global and local Nose-Hoover chain lengths should match");
212 for (int i = 0; i < efptNR; i++)
214 state_local->lambda[i] = state->lambda[i];
216 state_local->fep_state = state->fep_state;
217 state_local->veta = state->veta;
218 state_local->vol0 = state->vol0;
219 copy_mat(state->box, state_local->box);
220 copy_mat(state->box_rel, state_local->box_rel);
221 copy_mat(state->boxv, state_local->boxv);
222 copy_mat(state->svir_prev, state_local->svir_prev);
223 copy_mat(state->fvir_prev, state_local->fvir_prev);
224 if (state->dfhist != nullptr)
226 copy_df_history(state_local->dfhist, state->dfhist);
228 for (int i = 0; i < state_local->ngtc; i++)
230 for (int j = 0; j < nh; j++)
232 state_local->nosehoover_xi[i*nh+j] = state->nosehoover_xi[i*nh+j];
233 state_local->nosehoover_vxi[i*nh+j] = state->nosehoover_vxi[i*nh+j];
235 state_local->therm_integral[i] = state->therm_integral[i];
237 for (int i = 0; i < state_local->nnhpres; i++)
239 for (int j = 0; j < nh; j++)
241 state_local->nhpres_xi[i*nh+j] = state->nhpres_xi[i*nh+j];
242 state_local->nhpres_vxi[i*nh+j] = state->nhpres_vxi[i*nh+j];
245 state_local->baros_integral = state->baros_integral;
247 dd_bcast(dd, ((efptNR)*sizeof(real)), state_local->lambda.data());
248 dd_bcast(dd, sizeof(int), &state_local->fep_state);
249 dd_bcast(dd, sizeof(real), &state_local->veta);
250 dd_bcast(dd, sizeof(real), &state_local->vol0);
251 dd_bcast(dd, sizeof(state_local->box), state_local->box);
252 dd_bcast(dd, sizeof(state_local->box_rel), state_local->box_rel);
253 dd_bcast(dd, sizeof(state_local->boxv), state_local->boxv);
254 dd_bcast(dd, sizeof(state_local->svir_prev), state_local->svir_prev);
255 dd_bcast(dd, sizeof(state_local->fvir_prev), state_local->fvir_prev);
256 dd_bcast(dd, ((state_local->ngtc*nh)*sizeof(double)), state_local->nosehoover_xi.data());
257 dd_bcast(dd, ((state_local->ngtc*nh)*sizeof(double)), state_local->nosehoover_vxi.data());
258 dd_bcast(dd, state_local->ngtc*sizeof(double), state_local->therm_integral.data());
259 dd_bcast(dd, ((state_local->nnhpres*nh)*sizeof(double)), state_local->nhpres_xi.data());
260 dd_bcast(dd, ((state_local->nnhpres*nh)*sizeof(double)), state_local->nhpres_vxi.data());
262 /* communicate df_history -- required for restarting from checkpoint */
263 dd_distribute_dfhist(dd, state_local->dfhist);
265 dd_resize_state(state_local, f, dd->nat_home);
267 if (state_local->flags & (1 << estX))
269 const rvec *xGlobal = (DDMASTER(dd) ? as_rvec_array(state->x.data()) : nullptr);
270 dd_distribute_vec(dd, cgs, xGlobal, as_rvec_array(state_local->x.data()));
272 if (state_local->flags & (1 << estV))
274 const rvec *vGlobal = (DDMASTER(dd) ? as_rvec_array(state->v.data()) : nullptr);
275 dd_distribute_vec(dd, cgs, vGlobal, as_rvec_array(state_local->v.data()));
277 if (state_local->flags & (1 << estCGP))
279 const rvec *cgpGlobal = (DDMASTER(dd) ? as_rvec_array(state->cg_p.data()) : nullptr);
280 dd_distribute_vec(dd, cgs, cgpGlobal, as_rvec_array(state_local->cg_p.data()));
284 static std::vector < std::vector < int>>
285 getAtomGroupDistribution(FILE *fplog,
286 const matrix box, const gmx_ddbox_t &ddbox,
287 const t_block *cgs, rvec pos[],
288 gmx_domdec_t *dd)
290 AtomDistribution *ma = dd->ma;
292 /* Clear the count */
293 for (int rank = 0; rank < dd->nnodes; rank++)
295 ma->domainGroups[rank].numAtoms = 0;
298 matrix tcm;
299 make_tric_corr_matrix(dd->npbcdim, box, tcm);
301 ivec npulse;
302 const auto cellBoundaries =
303 set_dd_cell_sizes_slb(dd, &ddbox, setcellsizeslbMASTER, npulse);
305 const int *cgindex = cgs->index;
307 std::vector < std::vector < int>> indices(dd->nnodes);
309 /* Compute the center of geometry for all charge groups */
310 for (int icg = 0; icg < cgs->nr; icg++)
312 /* Set the reference location cg_cm for assigning the group */
313 rvec cg_cm;
314 int k0 = cgindex[icg];
315 int k1 = cgindex[icg + 1];
316 int nrcg = k1 - k0;
317 if (nrcg == 1)
319 copy_rvec(pos[k0], cg_cm);
321 else
323 real inv_ncg = 1/static_cast<real>(nrcg);
325 clear_rvec(cg_cm);
326 for (int k = k0; k < k1; k++)
328 rvec_inc(cg_cm, pos[k]);
330 for (int d = 0; d < DIM; d++)
332 cg_cm[d] *= inv_ncg;
335 /* Put the charge group in the box and determine the cell index ind */
336 ivec ind;
337 for (int d = DIM - 1; d >= 0; d--)
339 real pos_d = cg_cm[d];
340 if (d < dd->npbcdim)
342 bool bScrew = (dd->bScrewPBC && d == XX);
343 if (ddbox.tric_dir[d] && dd->nc[d] > 1)
345 /* Use triclinic coordinates for this dimension */
346 for (int j = d + 1; j < DIM; j++)
348 pos_d += cg_cm[j]*tcm[j][d];
351 while (pos_d >= box[d][d])
353 pos_d -= box[d][d];
354 rvec_dec(cg_cm, box[d]);
355 if (bScrew)
357 cg_cm[YY] = box[YY][YY] - cg_cm[YY];
358 cg_cm[ZZ] = box[ZZ][ZZ] - cg_cm[ZZ];
360 for (int k = k0; k < k1; k++)
362 rvec_dec(pos[k], box[d]);
363 if (bScrew)
365 pos[k][YY] = box[YY][YY] - pos[k][YY];
366 pos[k][ZZ] = box[ZZ][ZZ] - pos[k][ZZ];
370 while (pos_d < 0)
372 pos_d += box[d][d];
373 rvec_inc(cg_cm, box[d]);
374 if (bScrew)
376 cg_cm[YY] = box[YY][YY] - cg_cm[YY];
377 cg_cm[ZZ] = box[ZZ][ZZ] - cg_cm[ZZ];
379 for (int k = k0; k < k1; k++)
381 rvec_inc(pos[k], box[d]);
382 if (bScrew)
384 pos[k][YY] = box[YY][YY] - pos[k][YY];
385 pos[k][ZZ] = box[ZZ][ZZ] - pos[k][ZZ];
390 /* This could be done more efficiently */
391 ind[d] = 0;
392 while (ind[d]+1 < dd->nc[d] && pos_d >= cellBoundaries[d][ind[d] + 1])
394 ind[d]++;
397 int domainIndex = dd_index(dd->nc, ind);
398 indices[domainIndex].push_back(icg);
399 ma->domainGroups[domainIndex].numAtoms += cgindex[icg + 1] - cgindex[icg];
402 if (fplog)
404 // Use double for the sums to avoid natoms^2 overflowing
405 // (65537^2 > 2^32)
406 int nat_sum, nat_min, nat_max;
407 double nat2_sum;
409 nat_sum = 0;
410 nat2_sum = 0;
411 nat_min = ma->domainGroups[0].numAtoms;
412 nat_max = ma->domainGroups[0].numAtoms;
413 for (int rank = 0; rank < dd->nnodes; rank++)
415 int numAtoms = ma->domainGroups[rank].numAtoms;
416 nat_sum += numAtoms;
417 // cast to double to avoid integer overflows when squaring
418 nat2_sum += gmx::square(static_cast<double>(numAtoms));
419 nat_min = std::min(nat_min, numAtoms);
420 nat_max = std::max(nat_max, numAtoms);
422 nat_sum /= dd->nnodes;
423 nat2_sum /= dd->nnodes;
425 fprintf(fplog, "Atom distribution over %d domains: av %d stddev %d min %d max %d\n",
426 dd->nnodes,
427 nat_sum,
428 static_cast<int>(std::sqrt(nat2_sum - gmx::square(static_cast<double>(nat_sum)) + 0.5)),
429 nat_min, nat_max);
432 return indices;
435 static void distributeAtomGroups(FILE *fplog, gmx_domdec_t *dd,
436 const t_block *cgs,
437 const matrix box, const gmx_ddbox_t *ddbox,
438 rvec pos[])
440 AtomDistribution *ma = nullptr;
441 int *ibuf, buf2[2] = { 0, 0 };
442 gmx_bool bMaster = DDMASTER(dd);
444 std::vector < std::vector < int>> groupIndices;
446 if (bMaster)
448 ma = dd->ma;
450 if (dd->bScrewPBC)
452 check_screw_box(box);
455 groupIndices = getAtomGroupDistribution(fplog, box, *ddbox, cgs, pos, dd);
457 for (int rank = 0; rank < dd->nnodes; rank++)
459 ma->intBuffer[rank*2] = groupIndices[rank].size();
460 ma->intBuffer[rank*2 + 1] = ma->domainGroups[rank].numAtoms;
462 ibuf = ma->intBuffer.data();
464 else
466 ibuf = nullptr;
468 dd_scatter(dd, 2*sizeof(int), ibuf, buf2);
470 dd->ncg_home = buf2[0];
471 dd->nat_home = buf2[1];
472 dd->ncg_tot = dd->ncg_home;
473 dd->nat_tot = dd->nat_home;
474 if (dd->ncg_home > dd->cg_nalloc || dd->cg_nalloc == 0)
476 dd->cg_nalloc = over_alloc_dd(dd->ncg_home);
477 srenew(dd->index_gl, dd->cg_nalloc);
478 srenew(dd->cgindex, dd->cg_nalloc+1);
481 if (bMaster)
483 ma->atomGroups.clear();
485 int groupOffset = 0;
486 for (int rank = 0; rank < dd->nnodes; rank++)
488 ma->intBuffer[rank] = groupIndices[rank].size()*sizeof(int);
489 ma->intBuffer[dd->nnodes + rank] = groupOffset*sizeof(int);
491 ma->atomGroups.insert(ma->atomGroups.end(),
492 groupIndices[rank].begin(), groupIndices[rank].end());
494 ma->domainGroups[rank].atomGroups = gmx::constArrayRefFromArray(ma->atomGroups.data() + groupOffset, groupIndices[rank].size());
496 groupOffset += groupIndices[rank].size();
500 dd_scatterv(dd,
501 bMaster ? ma->intBuffer.data() : nullptr,
502 bMaster ? ma->intBuffer.data() + dd->nnodes : nullptr,
503 bMaster ? ma->atomGroups.data() : nullptr,
504 dd->ncg_home*sizeof(int), dd->index_gl);
506 /* Determine the home charge group sizes */
507 dd->cgindex[0] = 0;
508 for (int i = 0; i < dd->ncg_home; i++)
510 int cg_gl = dd->index_gl[i];
511 dd->cgindex[i+1] =
512 dd->cgindex[i] + cgs->index[cg_gl+1] - cgs->index[cg_gl];
515 if (debug)
517 fprintf(debug, "Home charge groups:\n");
518 for (int i = 0; i < dd->ncg_home; i++)
520 fprintf(debug, " %d", dd->index_gl[i]);
521 if (i % 10 == 9)
523 fprintf(debug, "\n");
526 fprintf(debug, "\n");
530 void distributeState(FILE *fplog,
531 gmx_domdec_t *dd,
532 t_state *state_global,
533 const t_block &cgs_gl,
534 const gmx_ddbox_t &ddbox,
535 t_state *state_local,
536 PaddedRVecVector *f)
538 rvec *xGlobal = (DDMASTER(dd) ? as_rvec_array(state_global->x.data()) : nullptr);
540 distributeAtomGroups(fplog, dd, &cgs_gl,
541 DDMASTER(dd) ? state_global->box : nullptr,
542 &ddbox, xGlobal);
544 dd_distribute_state(dd, &cgs_gl,
545 state_global, state_local, f);