36#ifndef PECLET_CORE_DECOMP_REDISTRIBUTE_TOPOLOGY_HPP
37#define PECLET_CORE_DECOMP_REDISTRIBUTE_TOPOLOGY_HPP
63template <
int Dim,
class T>
65 static_assert(std::is_trivially_copyable_v<T>,
"RedistributeTopology moves T as raw bytes");
94 template <
class SrcIndex,
class DstIndex>
102 throw std::invalid_argument(
"RedistributeTopology: an InPlace target has no movement");
104 throw std::invalid_argument(
"RedistributeTopology: StageComm was made for another target");
105 if (
src.globalSize() !=
dst.dec.globalSize())
106 throw std::invalid_argument(
107 "RedistributeTopology: source and target decompose different grids");
111 if (
src.numBlocks() !=
static_cast<std::size_t
>(size))
112 throw std::invalid_argument(
113 "RedistributeTopology: the current decomposition needs one block per parent rank");
125 requireDistinct(srcSlots_,
"srcIndex");
130 displs_.push_back(
static_cast<Index>(dstSlots_.size()));
131 counts_.push_back(cellCount(b));
139 if (!contains(
tb,
mb))
140 throw std::logic_error(
141 "RedistributeTopology: a member's block lies outside its target block");
144 if (
static_cast<Index>(dstSlots_.size()) != cellCount(
tb))
145 throw std::logic_error(
"RedistributeTopology: the members do not tile the target block");
148 for (
int r = 0; r < size; ++r)
150 if (
static_cast<Index>(dstSlots_.size()) != cellCount(
dst.dec.block(0)))
151 throw std::logic_error(
"RedistributeTopology: the blocks do not tile the level grid");
153 requireDistinct(dstSlots_,
"dstIndex");
173 void forward(
const std::vector<const T*>&
src,
const std::vector<T*>&
dst) {
174 const std::size_t
nF =
src.size();
175 if (active_ &&
dst.size() !=
nF)
176 throw std::invalid_argument(
"RedistributeTopology::forward: src/dst field counts differ");
179 pointToPoint(
src, srcSlots_, sendSeg_,
dst, dstSlots_, recvSeg_);
182 const std::size_t n = srcSlots_.size();
183 sbuf_.resize(n *
nF);
184 for (std::size_t
f = 0;
f <
nF; ++
f)
185 for (std::size_t
i = 0;
i < n; ++
i)
186 sbuf_[
f * n +
i] =
src[
f][srcSlots_[
i]];
187 const bool unpack = active_;
189 rbuf_.resize(dstSlots_.size() *
nF);
193 unpack ? bc_.data() :
nullptr, unpack ? bd_.data() :
nullptr,
MPI_BYTE, 0, comm_);
200 for (std::size_t
m = 0;
m < counts_.size(); ++
m) {
201 const std::size_t
cnt =
static_cast<std::size_t
>(counts_[
m]);
202 const std::size_t
d0 =
static_cast<std::size_t
>(displs_[
m]);
203 const T*
q = rbuf_.data() +
d0 *
nF;
204 for (std::size_t
f = 0;
f <
nF; ++
f)
205 for (std::size_t
i = 0;
i <
cnt; ++
i)
216 const std::size_t
nF =
src.size();
217 if (active_ &&
dst.size() !=
nF)
218 throw std::invalid_argument(
"RedistributeTopology::backward: src/dst field counts differ");
221 pointToPoint(
dst, dstSlots_, recvSeg_,
src, srcSlots_, sendSeg_);
224 const std::size_t n = srcSlots_.size();
226 const std::size_t
d0 =
static_cast<std::size_t
>(displs_[
static_cast<std::size_t
>(rank_)]);
227 for (std::size_t
f = 0;
f <
nF; ++
f)
228 for (std::size_t
i = 0;
i < n; ++
i)
234 sbuf_.resize(dstSlots_.size() *
nF);
235 for (std::size_t
m = 0;
m < counts_.size(); ++
m) {
236 const std::size_t
cnt =
static_cast<std::size_t
>(counts_[
m]);
237 const std::size_t
d0 =
static_cast<std::size_t
>(displs_[
m]);
238 T*
q = sbuf_.data() +
d0 *
nF;
239 for (std::size_t
f = 0;
f <
nF; ++
f)
240 for (std::size_t
i = 0;
i <
cnt; ++
i)
244 rbuf_.resize(n *
nF);
245 MPI_Scatterv(active_ ? sbuf_.data() :
nullptr, active_ ? bc_.data() :
nullptr,
248 for (std::size_t
f = 0;
f <
nF; ++
f)
249 for (std::size_t
i = 0;
i < n; ++
i)
250 src[
f][srcSlots_[
i]] = rbuf_[
f * n +
i];
261 template <
class SrcIndex,
class DstIndex>
265 throw std::invalid_argument(
"RedistributeTopology: StageComm was made for another target");
266 if (
src.globalSize() !=
dst.dec.globalSize())
267 throw std::invalid_argument(
268 "RedistributeTopology: source and target decompose different grids");
270 throw std::invalid_argument(
"RedistributeTopology: the topology id must lie in [0, " +
275 if (
src.numBlocks() !=
static_cast<std::size_t
>(size))
276 throw std::invalid_argument(
277 "RedistributeTopology: the current decomposition needs one block per parent rank");
278 const std::size_t
npL =
dst.dec.numBlocks();
280 throw std::invalid_argument(
281 "RedistributeTopology: a Repartition target needs 1..size blocks");
282 for (std::size_t b = 0; b <
npL; ++b)
283 if (
dst.ownerOf[b] !=
static_cast<int>(b))
284 throw std::invalid_argument(
285 "RedistributeTopology: a Repartition target's owners must be the identity");
286 if (c.active != (
static_cast<std::size_t
>(rank_) <
npL))
287 throw std::invalid_argument(
"RedistributeTopology: StageComm was made for another target");
302 std::vector<Index>
mySlot;
303 mySlot.reserve(
static_cast<std::size_t
>(cellCount(
mine)));
305 for (std::size_t
t = 0;
t <
npL; ++
t) {
307 if (!intersect(
mine,
dst.dec.block(
t),
I))
309 sendSeg_.push_back({
static_cast<int>(
t),
static_cast<Index>(srcSlots_.size()), cellCount(
I)});
311 srcSlots_.push_back(
mySlot[
static_cast<std::size_t
>(localLinear(g,
mine))]);
314 if (
static_cast<Index>(srcSlots_.size()) != cellCount(
mine))
315 throw std::logic_error(
316 "RedistributeTopology: the send segments do not tile the source block");
317 requireDistinct(srcSlots_,
"srcIndex");
321 for (
int s = 0; s < size; ++s) {
323 if (!intersect(
tb,
src.block(
static_cast<std::size_t
>(s)),
J))
325 recvSeg_.push_back({s,
static_cast<Index>(dstSlots_.size()), cellCount(
J)});
328 if (
static_cast<Index>(dstSlots_.size()) != cellCount(
tb))
329 throw std::logic_error(
330 "RedistributeTopology: the receive segments do not tile the target block");
331 requireDistinct(dstSlots_,
"dstIndex");
339 void pointToPoint(
const std::vector<const T*>& from,
const std::vector<Index>&
outSlots,
340 const std::vector<Segment>&
out,
const std::vector<T*>&
to,
341 const std::vector<Index>&
inSlots,
const std::vector<Segment>&
in) {
342 const std::size_t
nF = std::max(from.size(),
to.size());
343 if (!
out.empty() && from.size() !=
nF)
344 throw std::invalid_argument(
"RedistributeTopology: a sending rank needs every field");
345 if (!
in.empty() &&
to.size() !=
nF)
346 throw std::invalid_argument(
"RedistributeTopology: a receiving rank needs every field");
350 for (
const Segment& s :
out) {
353 const std::size_t
cnt =
static_cast<std::size_t
>(s.count),
354 d0 =
static_cast<std::size_t
>(s.displ);
355 T*
q = sbuf_.data() +
d0 *
nF;
356 for (std::size_t
f = 0;
f <
nF; ++
f)
357 for (std::size_t
i = 0;
i <
cnt; ++
i)
361 reqs_.reserve(
out.size() +
in.size());
362 for (
const Segment& s :
in) {
365 reqs_.emplace_back();
366 MPI_Irecv(rbuf_.data() +
static_cast<std::size_t
>(s.displ) *
nF,
367 bytes(
static_cast<std::size_t
>(s.count) *
nF),
MPI_BYTE, s.peer, tag_, comm_,
370 for (
const Segment& s :
out) {
373 reqs_.emplace_back();
374 MPI_Isend(sbuf_.data() +
static_cast<std::size_t
>(s.displ) *
nF,
375 bytes(
static_cast<std::size_t
>(s.count) *
nF),
MPI_BYTE, s.peer, tag_, comm_,
379 for (
const Segment&
so :
out) {
380 if (
so.peer != rank_)
382 for (
const Segment&
si :
in) {
383 if (
si.peer != rank_)
385 const std::size_t
cnt =
static_cast<std::size_t
>(
so.count);
386 for (std::size_t
f = 0;
f <
nF; ++
f)
387 for (std::size_t
i = 0;
i <
cnt; ++
i)
389 from[
f][
outSlots[
static_cast<std::size_t
>(
so.displ) +
i]];
394 for (
const Segment& s :
in) {
397 const std::size_t
cnt =
static_cast<std::size_t
>(s.count),
398 d0 =
static_cast<std::size_t
>(s.displ);
399 const T*
q = rbuf_.data() +
d0 *
nF;
400 for (std::size_t
f = 0;
f <
nF; ++
f)
401 for (std::size_t
i = 0;
i <
cnt; ++
i)
407 for (
int k = 0;
k <
Dim; ++
k) {
408 const Index lo = std::max(a.origin[
k], b.origin[
k]);
409 const Index hi = std::min(a.origin[
k] + a.size[
k], b.origin[
k] + b.size[
k]);
420 for (
int k =
Dim - 1;
k >= 0; --
k)
421 i =
i * b.size[
k] + (g[
k] - b.origin[
k]);
428 for (
int k = 0;
k <
Dim; ++
k)
429 end[
k] = b.origin[
k] + b.size[
k];
434 for (
int k = 0;
k <
Dim; ++
k)
439 for (
int k = 0;
k <
Dim; ++
k)
445 static void requireDistinct(std::vector<Index> v,
const char*
what) {
446 std::sort(v.begin(), v.end());
447 if (std::adjacent_find(v.begin(), v.end()) != v.end())
448 throw std::invalid_argument(std::string(
"RedistributeTopology: ") +
what +
449 " maps two cells to one slot");
451 static int bytes(std::size_t
elems) {
452 const std::size_t b =
elems *
sizeof(
T);
453 if (b >
static_cast<std::size_t
>(
INT_MAX))
454 throw std::overflow_error(
"RedistributeTopology: a message exceeds INT_MAX bytes");
455 return static_cast<int>(b);
458 void byteLayout(std::size_t
nF) {
459 bc_.resize(counts_.size());
460 bd_.resize(counts_.size());
461 for (std::size_t
m = 0;
m < counts_.size(); ++
m) {
462 bc_[
m] = bytes(
static_cast<std::size_t
>(counts_[
m]) *
nF);
463 bd_[
m] = bytes(
static_cast<std::size_t
>(displs_[
m]) *
nF);
468 bool active_ =
false;
472 std::vector<Index> srcSlots_;
473 std::vector<Index> dstSlots_;
475 std::vector<Index> counts_, displs_;
476 std::vector<int> bc_, bd_;
477 std::vector<Segment> sendSeg_, recvSeg_;
478 std::vector<MPI_Request> reqs_;
479 std::vector<T> sbuf_, rbuf_;
The planned, repeatable movement of a multigrid level's fields onto a stage target and back (the file...
StageKind kind() const
The kind of the target this topology was built for (InPlace before build()).
bool active() const
Whether this rank holds target fields (dst in forward, the input of backward).
std::size_t numSourceCells() const
Cells of this rank's current block (the extent srcIndex was called on).
static constexpr int kTagSpan
Number of distinct ids, hence the tags kTagBase .. kTagBase + kTagSpan - 1.
void backward(const std::vector< const T * > &dst, const std::vector< T * > &src)
Target -> level, overwriting src[f] on this rank's current block.
void forward(const std::vector< const T * > &src, const std::vector< T * > &dst)
Level -> target.
void build(const BlockDecomposer< Dim > &src, const StageTarget< Dim > &dst, const StageComm &c, SrcIndex &&srcIndex, DstIndex &&dstIndex, int id=0)
Establish the movement from src (the level's current decomposition; parent rank r owns block r) to ds...
std::size_t numTargetCells() const
Cells of this rank's target block (0 on an inactive rank).
RedistributeTopology()=default
static constexpr int kTagBase
Repartition's point-to-point tags: kTagBase + id for the per-topology id passed to build(),...
int MPI_Comm_size(MPI_Comm, int *s)
int MPI_Waitall(int, MPI_Request *, MPI_Status *)
#define MPI_STATUSES_IGNORE
int MPI_Isend(const void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *)
int MPI_Comm_rank(MPI_Comm, int *r)
int MPI_Irecv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *)
StageKind
How a multigrid level moves when it can no longer coarsen in place (see chooseStageTarget and the fil...
@ Repartition
a fresh proportional ORB of the level grid on the first np_L parent ranks
@ InPlace
no stage: the level coarsens on its current decomposition
@ SiblingMerge
ORB tree truncated: contiguous runs of blocks merge onto their lowest rank.
@ Replicated
the whole level as one block, held identically on every rank
Kokkos::View< T *, MemSpace > View
1D device array.
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
The communicators of one coarse-level multigrid stage, made by makeStageComm (the file comment spells...
int myTargetBlock
the target block this rank owns, or -1
std::vector< int > members
Parent ranks of this rank's group, in group-comm order (owner first); empty for Repartition.
bool active
this rank owns a target block
StageKind kind
the kind of the StageTarget this was made for
MPI_Comm group
this rank's target block's members; owner = group rank 0