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, 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.
37 * \brief Implements atom redistribution functions.
39 * \author Berk Hess <hess@kth.se>
40 * \ingroup module_domdec
45 #include "redistribute.h"
49 #include "gromacs/domdec/domdec_network.h"
50 #include "gromacs/domdec/ga2la.h"
51 #include "gromacs/gmxlib/nrnb.h"
52 #include "gromacs/math/vec.h"
53 #include "gromacs/mdlib/gmx_omp_nthreads.h"
54 #include "gromacs/mdlib/nsgrid.h"
55 #include "gromacs/mdtypes/forcerec.h"
56 #include "gromacs/mdtypes/nblist.h"
57 #include "gromacs/mdtypes/state.h"
58 #include "gromacs/utility/cstringutil.h"
59 #include "gromacs/utility/fatalerror.h"
60 #include "gromacs/utility/gmxomp.h"
62 #include "domdec_internal.h"
65 /* The size per charge group of the cggl_flag buffer in gmx_domdec_comm_t */
66 static constexpr int DD_CGIBS = 2;
68 /* The flags for the cggl_flag buffer in gmx_domdec_comm_t */
70 /* The lower 16 bits are reserved for the charge group size */
71 static constexpr int DD_FLAG_NRCG = 65535;
73 /* Returns which bit tells whether to move a group forward along dimension d */
74 static inline int DD_FLAG_FW(int d)
76 return 1 << (16 + d*2);
79 /* Returns which bit tells whether to move a group backward along dimension d */
80 static inline int DD_FLAG_BW(int d)
82 return 1 << (16 + d*2 + 1);
86 copyMovedAtomsToBufferPerAtom(gmx::ArrayRef<const int> move,
87 const gmx::RangePartitioning &atomGroups,
89 rvec *src, gmx_domdec_comm_t *comm)
91 int pos_vec[DIM*2] = { 0 };
93 for (int g = 0; g < move.size(); g++)
95 const auto atomGroup = atomGroups.block(g);
96 /* Skip moved atoms */
100 /* Copy to the communication buffer */
101 int numAtoms = atomGroup.size();
102 pos_vec[m] += 1 + vec*numAtoms;
103 for (int i : atomGroup)
105 copy_rvec(src[i], comm->cgcm_state[m][pos_vec[m]++]);
107 pos_vec[m] += (nvec - vec - 1)*numAtoms;
113 copyMovedAtomsToBufferPerChargeGroup(gmx::ArrayRef<const int> move,
114 const gmx::RangePartitioning &atomGroups,
115 int nvec, rvec *src, gmx_domdec_comm_t *comm)
117 int pos_vec[DIM*2] = { 0 };
119 for (int g = 0; g < move.size(); g++)
121 const auto atomGroup = atomGroups.block(g);
122 /* Skip moved atoms */
126 /* Copy to the communication buffer */
127 copy_rvec(src[g], comm->cgcm_state[m][pos_vec[m]]);
128 pos_vec[m] += 1 + atomGroup.size()*nvec;
133 static void clear_and_mark_ind(gmx::ArrayRef<const int> move,
134 gmx::ArrayRef<const int> globalAtomGroupIndices,
135 const gmx::RangePartitioning &atomGroups,
136 gmx::ArrayRef<const int> globalAtomIndices,
141 for (int g = 0; g < move.size(); g++)
145 /* Clear the global indices */
146 for (int a : atomGroups.block(g))
148 ga2la->erase(globalAtomIndices[a]);
152 bLocalCG[globalAtomGroupIndices[g]] = FALSE;
154 /* Signal that this group has moved using the ns cell index.
155 * Here we set it to -1. fill_grid will change it
156 * from -1 to NSGRID_SIGNAL_MOVED_FAC*grid->ncells.
163 static void print_cg_move(FILE *fplog,
165 int64_t step, int cg, int dim, int dir,
166 gmx_bool bHaveCgcmOld, real limitd,
167 rvec cm_old, rvec cm_new, real pos_d)
169 gmx_domdec_comm_t *comm;
174 fprintf(fplog, "\nStep %s:\n", gmx_step_str(step, buf));
177 fprintf(fplog, "%s %d moved more than the distance allowed by the domain decomposition (%f) in direction %c\n",
178 dd->comm->bCGs ? "The charge group starting at atom" : "Atom",
179 ddglatnr(dd, dd->atomGrouping().block(cg).begin()), limitd, dim2char(dim));
183 /* We don't have a limiting distance available: don't print it */
184 fprintf(fplog, "%s %d moved more than the distance allowed by the domain decomposition in direction %c\n",
185 dd->comm->bCGs ? "The charge group starting at atom" : "Atom",
186 ddglatnr(dd, dd->atomGrouping().block(cg).begin()), dim2char(dim));
188 fprintf(fplog, "distance out of cell %f\n",
189 dir == 1 ? pos_d - comm->cell_x1[dim] : pos_d - comm->cell_x0[dim]);
192 fprintf(fplog, "Old coordinates: %8.3f %8.3f %8.3f\n",
193 cm_old[XX], cm_old[YY], cm_old[ZZ]);
195 fprintf(fplog, "New coordinates: %8.3f %8.3f %8.3f\n",
196 cm_new[XX], cm_new[YY], cm_new[ZZ]);
197 fprintf(fplog, "Old cell boundaries in direction %c: %8.3f %8.3f\n",
199 comm->old_cell_x0[dim], comm->old_cell_x1[dim]);
200 fprintf(fplog, "New cell boundaries in direction %c: %8.3f %8.3f\n",
202 comm->cell_x0[dim], comm->cell_x1[dim]);
206 static void cg_move_error(FILE *fplog,
208 int64_t step, int cg, int dim, int dir,
209 gmx_bool bHaveCgcmOld, real limitd,
210 rvec cm_old, rvec cm_new, real pos_d)
214 print_cg_move(fplog, dd, step, cg, dim, dir,
215 bHaveCgcmOld, limitd, cm_old, cm_new, pos_d);
217 print_cg_move(stderr, dd, step, cg, dim, dir,
218 bHaveCgcmOld, limitd, cm_old, cm_new, pos_d);
220 "%s moved too far between two domain decomposition steps\n"
221 "This usually means that your system is not well equilibrated",
222 dd->comm->bCGs ? "An atom group" : "An atom");
225 static void rotate_state_atom(t_state *state, int a)
227 if (state->flags & (1 << estX))
229 auto x = makeArrayRef(state->x);
230 /* Rotate the complete state; for a rectangular box only */
231 x[a][YY] = state->box[YY][YY] - x[a][YY];
232 x[a][ZZ] = state->box[ZZ][ZZ] - x[a][ZZ];
234 if (state->flags & (1 << estV))
236 auto v = makeArrayRef(state->v);
237 v[a][YY] = -v[a][YY];
238 v[a][ZZ] = -v[a][ZZ];
240 if (state->flags & (1 << estCGP))
242 auto cg_p = makeArrayRef(state->cg_p);
243 cg_p[a][YY] = -cg_p[a][YY];
244 cg_p[a][ZZ] = -cg_p[a][ZZ];
248 /* Returns a pointer to a buffer of size \p numAtomsNew with contents 0 from numAtomsOld upwards
250 * Note: numAtomsOld should either be 0 or match the current buffer size.
252 static int *getMovedBuffer(gmx_domdec_comm_t *comm,
256 std::vector<int> &movedBuffer = comm->movedBuffer;
258 GMX_RELEASE_ASSERT(numAtomsOld == 0 || movedBuffer.size() == numAtomsOld, "numAtomsOld should either be 0 or match the current size");
260 /* Contents up to numAtomsOld should be preserved, clear from numAtomsOld */
261 if (numAtomsOld == 0)
265 movedBuffer.resize(numAtomsNew);
267 return movedBuffer.data();
270 /* Bounds to determine whether an atom group moved to far between DD steps */
273 rvec distance; /* Maximum distance over which a group can move */
274 rvec lower; /* Lower bound for group location */
275 rvec upper; /* Upper bound for group location */
278 /* Compute flag that tells where to move an atom group
280 * The input dev tells whether the group moves fw,remains,bw (-1,0,1) along
282 * The return value is -1 when the group does not move.
283 * The return has move flags set when the group does move and the lower 4 bits
284 * are (mis)used to tell which is the first dimension (bit 1,2,3) the group
285 * needs to be moved along and in which direction (bit 0 not set for fw
288 static int computeMoveFlag(const gmx_domdec_t &dd,
292 int firstMoveDimValue = -1;
293 for (int d = 0; d < dd.ndim; d++)
295 const int dim = dd.dim[d];
298 flag |= DD_FLAG_FW(d);
299 if (firstMoveDimValue == -1)
301 firstMoveDimValue = d*2;
304 else if (dev[dim] == -1)
306 flag |= DD_FLAG_BW(d);
307 if (firstMoveDimValue == -1)
311 firstMoveDimValue = d*2 + 1;
315 firstMoveDimValue = d*2;
321 return firstMoveDimValue + flag;
324 /* Determine to which domains atomGroups in the range \p cg_start, \p cg_end should go.
326 * Returns in the move array where the groups should go.
327 * Also updates \p cg_cm for jumps over periodic boundaries.
329 * \TODO Rename cg to atomGroup.
331 static void calc_cg_move(FILE *fplog, int64_t step,
334 const ivec tric_dir, matrix tcm,
335 const rvec cell_x0, const rvec cell_x1,
336 const MoveLimits &moveLimits,
337 const gmx::RangePartitioning &atomGroups,
338 int cg_start, int cg_end,
340 gmx::ArrayRef<int> move)
342 const int npbcdim = dd->npbcdim;
343 auto x = makeArrayRef(state->x);
345 for (int g = cg_start; g < cg_end; g++)
347 const auto atomGroup = atomGroups.block(g);
348 const int numAtoms = atomGroup.size();
349 // TODO: Rename this center of geometry variable to cogNew
353 copy_rvec(x[atomGroup.begin()], cm_new);
357 real invNumAtoms = 1/static_cast<real>(numAtoms);
359 for (int k : atomGroup)
361 rvec_inc(cm_new, x[k]);
363 for (int d = 0; d < DIM; d++)
365 cm_new[d] = invNumAtoms*cm_new[d];
370 /* Do pbc and check DD cell boundary crossings */
371 for (int d = DIM - 1; d >= 0; d--)
375 bool bScrew = (dd->bScrewPBC && d == XX);
376 /* Determine the location of this cg in lattice coordinates */
377 real pos_d = cm_new[d];
380 for (int d2 = d + 1; d2 < DIM; d2++)
382 pos_d += cm_new[d2]*tcm[d2][d];
385 /* Put the charge group in the triclinic unit-cell */
386 if (pos_d >= cell_x1[d])
388 if (pos_d >= moveLimits.upper[d])
390 cg_move_error(fplog, dd, step, g, d, 1,
391 cg_cm != state->x.rvec_array(), moveLimits.distance[d],
392 cg_cm[g], cm_new, pos_d);
395 if (dd->ci[d] == dd->nc[d] - 1)
397 rvec_dec(cm_new, state->box[d]);
400 cm_new[YY] = state->box[YY][YY] - cm_new[YY];
401 cm_new[ZZ] = state->box[ZZ][ZZ] - cm_new[ZZ];
403 for (int k : atomGroup)
405 rvec_dec(x[k], state->box[d]);
408 rotate_state_atom(state, k);
413 else if (pos_d < cell_x0[d])
415 if (pos_d < moveLimits.lower[d])
417 cg_move_error(fplog, dd, step, g, d, -1,
418 cg_cm != state->x.rvec_array(), moveLimits.distance[d],
419 cg_cm[g], cm_new, pos_d);
424 rvec_inc(cm_new, state->box[d]);
427 cm_new[YY] = state->box[YY][YY] - cm_new[YY];
428 cm_new[ZZ] = state->box[ZZ][ZZ] - cm_new[ZZ];
430 for (int k : atomGroup)
432 rvec_inc(x[k], state->box[d]);
435 rotate_state_atom(state, k);
441 else if (d < npbcdim)
443 /* Put the charge group in the rectangular unit-cell */
444 while (cm_new[d] >= state->box[d][d])
446 rvec_dec(cm_new, state->box[d]);
447 for (int k : atomGroup)
449 rvec_dec(x[k], state->box[d]);
452 while (cm_new[d] < 0)
454 rvec_inc(cm_new, state->box[d]);
455 for (int k : atomGroup)
457 rvec_inc(x[k], state->box[d]);
463 copy_rvec(cm_new, cg_cm[g]);
465 /* Temporarily store the flag in move */
466 move[g] = computeMoveFlag(*dd, dev);
472 /* Constructor that purposely does not initialize anything */
481 /* Determine to which domains update groups in the range \p groupBegin, \p groupEnd should go.
483 * Returns in the move array where the groups should go.
484 * Also updates the COGs and coordinates for jumps over periodic boundaries.
486 static void calcGroupMove(FILE *fplog, int64_t step,
489 const ivec tric_dir, matrix tcm,
490 const rvec cell_x0, const rvec cell_x1,
491 const MoveLimits &moveLimits,
492 int groupBegin, int groupEnd,
493 gmx::ArrayRef<PbcAndFlag> pbcAndFlags)
495 GMX_RELEASE_ASSERT(!dd->bScrewPBC, "Screw PBC is not supported here");
497 const int npbcdim = dd->npbcdim;
499 gmx::UpdateGroupsCog *updateGroupsCog = dd->comm->updateGroupsCog.get();
501 for (int g = groupBegin; g < groupEnd; g++)
504 gmx::RVec &cog = updateGroupsCog->cog(g);
505 gmx::RVec cogOld = cog;
508 /* Do pbc and check DD cell boundary crossings */
509 for (int d = DIM - 1; d >= 0; d--)
513 /* Determine the location of this COG in lattice coordinates */
517 for (int d2 = d + 1; d2 < DIM; d2++)
519 pos_d += cog[d2]*tcm[d2][d];
522 /* Put the COG in the triclinic unit-cell */
523 if (pos_d >= cell_x1[d])
525 if (pos_d >= moveLimits.upper[d])
527 cg_move_error(fplog, dd, step, g, d, 1,
528 true, moveLimits.distance[d],
532 if (dd->ci[d] == dd->nc[d] - 1)
534 rvec_dec(cog, state->box[d]);
537 else if (pos_d < cell_x0[d])
539 if (pos_d < moveLimits.lower[d])
541 cg_move_error(fplog, dd, step, g, d, -1,
542 true, moveLimits.distance[d],
548 rvec_inc(cog, state->box[d]);
552 else if (d < npbcdim)
554 /* Put the COG in the rectangular unit-cell */
555 while (cog[d] >= state->box[d][d])
557 rvec_dec(cog, state->box[d]);
561 rvec_inc(cog, state->box[d]);
566 /* Store the PBC and move flag, so we can later apply them to the atoms */
567 PbcAndFlag &pbcAndFlag = pbcAndFlags[g];
569 rvec_sub(cog, cogOld, pbcAndFlag.pbcShift);
570 pbcAndFlag.moveFlag = computeMoveFlag(*dd, dev);
575 applyPbcAndSetMoveFlags(const gmx::UpdateGroupsCog &updateGroupsCog,
576 gmx::ArrayRef<const PbcAndFlag> pbcAndFlags,
579 gmx::ArrayRef<gmx::RVec> atomCoords,
580 gmx::ArrayRef<int> move)
582 for (int a = atomBegin; a < atomEnd; a++)
584 const PbcAndFlag &pbcAndFlag = pbcAndFlags[updateGroupsCog.cogIndex(a)];
585 rvec_inc(atomCoords[a], pbcAndFlag.pbcShift);
586 /* Temporarily store the flag in move */
587 move[a] = pbcAndFlag.moveFlag;
591 void dd_redistribute_cg(FILE *fplog, int64_t step,
592 gmx_domdec_t *dd, ivec tric_dir,
594 PaddedVector<gmx::RVec> *f,
599 gmx_domdec_comm_t *comm = dd->comm;
603 check_screw_box(state->box);
606 rvec *cg_cm = nullptr;
607 if (fr->cutoff_scheme == ecutsGROUP)
612 // Positions are always present, so there's nothing to flag
613 bool bV = (state->flags & (1<<estV)) != 0;
614 bool bCGP = (state->flags & (1<<estCGP)) != 0;
616 DDBufferAccess<int> moveBuffer(comm->intBuffer, dd->ncg_home);
617 gmx::ArrayRef<int> move = moveBuffer.buffer;
619 const int npbcdim = dd->npbcdim;
621 rvec cell_x0, cell_x1;
622 MoveLimits moveLimits;
623 for (int d = 0; (d < DIM); d++)
625 moveLimits.distance[d] = dd->comm->cellsize_min[d];
626 if (d >= npbcdim && dd->ci[d] == 0)
628 cell_x0[d] = -GMX_FLOAT_MAX;
632 cell_x0[d] = comm->cell_x0[d];
634 if (d >= npbcdim && dd->ci[d] == dd->nc[d] - 1)
636 cell_x1[d] = GMX_FLOAT_MAX;
640 cell_x1[d] = comm->cell_x1[d];
644 moveLimits.lower[d] = comm->old_cell_x0[d] - moveLimits.distance[d];
645 moveLimits.upper[d] = comm->old_cell_x1[d] + moveLimits.distance[d];
649 /* We check after communication if a charge group moved
650 * more than one cell. Set the pre-comm check limit to float_max.
652 moveLimits.lower[d] = -GMX_FLOAT_MAX;
653 moveLimits.upper[d] = GMX_FLOAT_MAX;
658 make_tric_corr_matrix(npbcdim, state->box, tcm);
660 const gmx::RangePartitioning &atomGrouping = dd->atomGrouping();
662 const int nthread = gmx_omp_nthreads_get(emntDomdec);
664 /* Compute the center of geometry for all home charge groups
665 * and put them in the box and determine where they should go.
667 std::vector<PbcAndFlag> pbcAndFlags(comm->useUpdateGroups ? comm->updateGroupsCog->numCogs() : 0);
669 #pragma omp parallel num_threads(nthread)
673 const int thread = gmx_omp_get_thread_num();
675 if (comm->useUpdateGroups)
677 const auto &updateGroupsCog = *comm->updateGroupsCog;
678 const int numGroups = updateGroupsCog.numCogs();
679 calcGroupMove(fplog, step, dd, state, tric_dir, tcm,
680 cell_x0, cell_x1, moveLimits,
681 ( thread *numGroups)/nthread,
682 ((thread+1)*numGroups)/nthread,
684 /* We need a barrier as atoms below can be in a COG of a different thread */
686 const int numHomeAtoms = comm->atomRanges.numHomeAtoms();
687 applyPbcAndSetMoveFlags(updateGroupsCog, pbcAndFlags,
688 ( thread *numHomeAtoms)/nthread,
689 ((thread+1)*numHomeAtoms)/nthread,
695 /* Here we handle single atoms or charge groups */
696 calc_cg_move(fplog, step, dd, state, tric_dir, tcm,
697 cell_x0, cell_x1, moveLimits,
699 ( thread *dd->ncg_home)/nthread,
700 ((thread+1)*dd->ncg_home)/nthread,
701 fr->cutoff_scheme == ecutsGROUP ? cg_cm : as_rvec_array(state->x.data()),
705 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
708 int ncg[DIM*2] = { 0 };
709 int nat[DIM*2] = { 0 };
710 for (int cg = 0; cg < dd->ncg_home; cg++)
714 const int flag = move[cg] & ~DD_FLAG_NRCG;
715 const int mc = move[cg] & DD_FLAG_NRCG;
718 std::vector<int> &cggl_flag = comm->cggl_flag[mc];
720 /* TODO: See if we can use push_back instead */
721 if ((ncg[mc] + 1)*DD_CGIBS > gmx::index(cggl_flag.size()))
723 cggl_flag.resize((ncg[mc] + 1)*DD_CGIBS);
725 cggl_flag[ncg[mc]*DD_CGIBS ] = dd->globalAtomGroupIndices[cg];
726 /* We store the cg size in the lower 16 bits
727 * and the place where the charge group should go
728 * in the next 6 bits. This saves some communication volume.
730 const int nrcg = atomGrouping.block(cg).size();
731 cggl_flag[ncg[mc]*DD_CGIBS+1] = nrcg | flag;
737 inc_nrnb(nrnb, eNR_CGCM, comm->atomRanges.numHomeAtoms());
738 inc_nrnb(nrnb, eNR_RESETX, dd->ncg_home);
741 for (int i = 0; i < dd->ndim*2; i++)
743 *ncg_moved += ncg[i];
756 /* Make sure the communication buffers are large enough */
757 for (int mc = 0; mc < dd->ndim*2; mc++)
759 size_t nvr = ncg[mc] + nat[mc]*nvec;
760 if (nvr > comm->cgcm_state[mc].size())
762 comm->cgcm_state[mc].resize(nvr);
766 switch (fr->cutoff_scheme)
769 /* Recalculating cg_cm might be cheaper than communicating,
770 * but that could give rise to rounding issues.
772 copyMovedAtomsToBufferPerChargeGroup(move, dd->atomGrouping(),
776 /* Without charge groups we send the moved atom coordinates
777 * over twice. This is so the code below can be used without
778 * many conditionals for both for with and without charge groups.
780 copyMovedAtomsToBufferPerChargeGroup(move, dd->atomGrouping(),
781 nvec, state->x.rvec_array(), comm);
784 gmx_incons("unimplemented");
788 copyMovedAtomsToBufferPerAtom(move, dd->atomGrouping(),
790 state->x.rvec_array(),
794 copyMovedAtomsToBufferPerAtom(move, dd->atomGrouping(),
796 state->v.rvec_array(),
801 copyMovedAtomsToBufferPerAtom(move, dd->atomGrouping(),
803 state->cg_p.rvec_array(),
808 if (fr->cutoff_scheme == ecutsVERLET)
810 moved = getMovedBuffer(comm, 0, dd->ncg_home);
814 moved = fr->ns->grid->cell_index;
817 clear_and_mark_ind(move,
818 dd->globalAtomGroupIndices, dd->atomGrouping(), dd->globalAtomIndices,
819 dd->ga2la, comm->bLocalCG,
822 /* Now we can remove the excess global atom-group indices from the list */
823 dd->globalAtomGroupIndices.resize(dd->ncg_home);
824 dd->atomGrouping_.reduceNumBlocks(dd->ncg_home);
826 /* We reuse the intBuffer without reacquiring since we are in the same scope */
827 DDBufferAccess<int> &flagBuffer = moveBuffer;
829 const cginfo_mb_t *cginfo_mb = fr->cginfo_mb;
831 /* Temporarily store atoms passed to our rank at the end of the range */
832 int home_pos_cg = dd->ncg_home;
833 int home_pos_at = dd->atomGrouping().subRange(0, dd->ncg_home).end();
834 for (int d = 0; d < dd->ndim; d++)
836 DDBufferAccess<gmx::RVec> rvecBuffer(comm->rvecBuffer, 0);
838 const int dim = dd->dim[d];
841 for (int dir = 0; dir < (dd->nc[dim] == 2 ? 1 : 2); dir++)
843 const int cdd = d*2 + dir;
844 /* Communicate the cg and atom counts */
845 int sbuf[2] = { ncg[cdd], nat[cdd] };
848 fprintf(debug, "Sending ddim %d dir %d: ncg %d nat %d\n",
849 d, dir, sbuf[0], sbuf[1]);
852 ddSendrecv(dd, d, dir, sbuf, 2, rbuf, 2);
854 flagBuffer.resize((ncg_recv + rbuf[0])*DD_CGIBS);
856 /* Communicate the charge group indices, sizes and flags */
857 ddSendrecv(dd, d, dir,
858 comm->cggl_flag[cdd].data(), sbuf[0]*DD_CGIBS,
859 flagBuffer.buffer.data() + ncg_recv*DD_CGIBS, rbuf[0]*DD_CGIBS);
861 const int nvs = ncg[cdd] + nat[cdd]*nvec;
862 const int i = rbuf[0] + rbuf[1] *nvec;
863 rvecBuffer.resize(nvr + i);
865 /* Communicate cgcm and state */
866 ddSendrecv(dd, d, dir,
867 as_rvec_array(comm->cgcm_state[cdd].data()), nvs,
868 as_rvec_array(rvecBuffer.buffer.data()) + nvr, i);
873 dd_check_alloc_ncg(fr, state, f, home_pos_cg + ncg_recv);
874 if (fr->cutoff_scheme == ecutsGROUP)
876 /* Here we resize to more than necessary and shrink later */
877 dd_resize_state(state, f, home_pos_at + ncg_recv*MAX_CGCGSIZE);
880 /* Process the received charge groups */
882 for (int cg = 0; cg < ncg_recv; cg++)
884 int flag = flagBuffer.buffer[cg*DD_CGIBS + 1];
885 rvec &pos = as_rvec_array(rvecBuffer.buffer.data())[buf_pos];
887 if (dim >= npbcdim && dd->nc[dim] > 2)
889 rvec &pos = as_rvec_array(rvecBuffer.buffer.data())[buf_pos];
891 /* No pbc in this dim and more than one domain boundary.
892 * We do a separate check if a charge group didn't move too far.
894 if (((flag & DD_FLAG_FW(d)) &&
895 pos[dim] > cell_x1[dim]) ||
896 ((flag & DD_FLAG_BW(d)) &&
897 pos[dim] < cell_x0[dim]))
899 cg_move_error(fplog, dd, step, cg, dim,
900 (flag & DD_FLAG_FW(d)) ? 1 : 0,
901 fr->cutoff_scheme == ecutsGROUP, 0,
909 /* Check which direction this cg should go */
910 for (int d2 = d + 1; (d2 < dd->ndim && mc == -1); d2++)
912 if (isDlbOn(dd->comm))
914 /* The cell boundaries for dimension d2 are not equal
915 * for each cell row of the lower dimension(s),
916 * therefore we might need to redetermine where
919 const int dim2 = dd->dim[d2];
920 /* If this cg crosses the box boundary in dimension d2
921 * we can use the communicated flag, so we do not
922 * have to worry about pbc.
924 if (!((dd->ci[dim2] == dd->nc[dim2]-1 &&
925 (flag & DD_FLAG_FW(d2))) ||
926 (dd->ci[dim2] == 0 &&
927 (flag & DD_FLAG_BW(d2)))))
929 /* Clear the two flags for this dimension */
930 flag &= ~(DD_FLAG_FW(d2) | DD_FLAG_BW(d2));
931 /* Determine the location of this cg
932 * in lattice coordinates
934 real pos_d = rvecBuffer.buffer[buf_pos][dim2];
937 for (int d3 = dim2+1; d3 < DIM; d3++)
939 pos_d += pos[d3]*tcm[d3][dim2];
943 GMX_ASSERT(dim2 >= 0 && dim2 < DIM, "Keep the static analyzer happy");
945 /* Check of we are not at the box edge.
946 * pbc is only handled in the first step above,
947 * but this check could move over pbc while
948 * the first step did not due to different rounding.
950 if (pos_d >= cell_x1[dim2] &&
951 dd->ci[dim2] != dd->nc[dim2]-1)
953 flag |= DD_FLAG_FW(d2);
955 else if (pos_d < cell_x0[dim2] &&
958 flag |= DD_FLAG_BW(d2);
960 flagBuffer.buffer[cg*DD_CGIBS + 1] = flag;
963 /* Set to which neighboring cell this cg should go */
964 if (flag & DD_FLAG_FW(d2))
968 else if (flag & DD_FLAG_BW(d2))
970 if (dd->nc[dd->dim[d2]] > 2)
982 const int nrcg = flag & DD_FLAG_NRCG;
985 /* Set the global charge group index and size */
986 const int globalAtomGroupIndex = flagBuffer.buffer[cg*DD_CGIBS];
987 dd->globalAtomGroupIndices.push_back(globalAtomGroupIndex);
988 dd->atomGrouping_.appendBlock(nrcg);
989 /* Copy the state from the buffer */
990 if (fr->cutoff_scheme == ecutsGROUP)
993 copy_rvec(pos, cg_cm[home_pos_cg]);
998 fr->cginfo[home_pos_cg] = ddcginfo(cginfo_mb,
999 globalAtomGroupIndex);
1002 comm->bLocalCG[globalAtomGroupIndex] = TRUE;
1005 auto x = makeArrayRef(state->x);
1006 auto v = makeArrayRef(state->v);
1007 auto cg_p = makeArrayRef(state->cg_p);
1008 rvec *rvecPtr = as_rvec_array(rvecBuffer.buffer.data());
1009 for (int i = 0; i < nrcg; i++)
1011 copy_rvec(rvecPtr[buf_pos++],
1016 for (int i = 0; i < nrcg; i++)
1018 copy_rvec(rvecPtr[buf_pos++],
1024 for (int i = 0; i < nrcg; i++)
1026 copy_rvec(rvecPtr[buf_pos++],
1027 cg_p[home_pos_at+i]);
1031 home_pos_at += nrcg;
1035 /* Reallocate the buffers if necessary */
1036 if ((ncg[mc] + 1)*DD_CGIBS > gmx::index(comm->cggl_flag[mc].size()))
1038 comm->cggl_flag[mc].resize((ncg[mc] + 1)*DD_CGIBS);
1040 size_t nvr = ncg[mc] + nat[mc]*nvec;
1041 if (nvr + 1 + nrcg*nvec > comm->cgcm_state[mc].size())
1043 comm->cgcm_state[mc].resize(nvr + 1 + nrcg*nvec);
1045 /* Copy from the receive to the send buffers */
1046 memcpy(comm->cggl_flag[mc].data() + ncg[mc]*DD_CGIBS,
1047 flagBuffer.buffer.data() + cg*DD_CGIBS,
1048 DD_CGIBS*sizeof(int));
1049 memcpy(comm->cgcm_state[mc][nvr],
1050 rvecBuffer.buffer.data() + buf_pos,
1051 (1 + nrcg*nvec)*sizeof(rvec));
1052 buf_pos += 1 + nrcg*nvec;
1059 /* Note that the indices are now only partially up to date
1060 * and ncg_home and nat_home are not the real count, since there are
1061 * "holes" in the arrays for the charge groups that moved to neighbors.
1063 if (fr->cutoff_scheme == ecutsVERLET)
1065 /* We need to clear the moved flags for the received atoms,
1066 * because the moved buffer will be passed to the nbnxn gridding call.
1068 int *moved = getMovedBuffer(comm, dd->ncg_home, home_pos_cg);
1070 for (int i = dd->ncg_home; i < home_pos_cg; i++)
1076 dd->ncg_home = home_pos_cg;
1077 comm->atomRanges.setEnd(DDAtomRanges::Type::Home, home_pos_at);
1079 if (fr->cutoff_scheme == ecutsGROUP)
1081 /* We overallocated before, we need to set the right size here */
1082 dd_resize_state(state, f, comm->atomRanges.numHomeAtoms());
1088 "Finished repartitioning: cgs moved out %d, new home %d\n",
1089 *ncg_moved, dd->ncg_home-*ncg_moved);