core 1.5.0
Shared MPI block decomposition, asynchronous ghost-layer exchange, SDF geometry, load balancing (header-only C++20)
Loading...
Searching...
No Matches
stage_comm.hpp
Go to the documentation of this file.
1// core — the communicators of a coarse-level multigrid stage (amr/docs/amr_mg_core_boundary.md §4).
2//
3// Given a `StageTarget` (stage_target.hpp), every rank of the parent communicator learns which
4// target block it belongs to, whether it OWNS one (and so continues the hierarchy below the stage),
5// and gets two communicators:
6//
7// group — the ranks whose current blocks make up this rank's target block, in ascending parent
8// rank; the owner is group rank 0. The movement's gather / scatter runs on it.
9// sub — the owners only, ranked by target block index (MPI_COMM_NULL on a non-owner). The
10// continued hierarchy runs on it.
11//
12// SiblingMerge reproduces flow's `Telescope` communicators exactly (flow/src/mac_cutcell_mg.hpp,
13// CutcellMG::initMpi): MPI_Comm_split(parent, group, rank) and MPI_Comm_split(parent, owner ? 0 :
14// MPI_UNDEFINED, group). Replicated: one group of every rank; every rank is an owner.
15// Repartition (design §11.2): there are no groups — every parent rank takes part in the
16// point-to-point movement, so `group` IS `parent` (the same handle, not a duplicate) and `members`
17// is empty; the owners are parent ranks [0, np_L), `sub` = MPI_Comm_split(parent, rank < np_L ? 0
18// : MPI_UNDEFINED, rank), and `myGroup` is -1 (a current block feeds several target blocks).
19//
20// The parent-rank convention is core's: parent rank r owns block r of the current decomposition.
21// The struct OWNS `group` and `sub` (not `parent`, and not `group` when it is `parent`):
22// non-copyable, movable, freed on destruction with the same MPI_Finalized guard as GridHalo.
23#ifndef PECLET_CORE_DECOMP_STAGE_COMM_HPP
24#define PECLET_CORE_DECOMP_STAGE_COMM_HPP
25
26#include <stdexcept>
27#include <utility>
28#include <vector>
29
32
33namespace peclet::core::decomp {
34
43struct StageComm {
48 bool active = false;
49 int myTargetBlock = -1;
50 int myGroup = -1;
52 std::vector<int> members;
53
54 StageComm() = default;
55 StageComm(const StageComm&) = delete;
56 StageComm& operator=(const StageComm&) = delete;
58 StageComm(StageComm&& o) noexcept { swap(o); }
61 if (this != &o) {
62 release();
63 swap(o);
64 }
65 return *this;
66 }
67 ~StageComm() { release(); }
68
69 private:
70 void swap(StageComm& o) noexcept {
71 std::swap(parent, o.parent);
72 std::swap(group, o.group);
73 std::swap(sub, o.sub);
74 std::swap(kind, o.kind);
75 std::swap(active, o.active);
76 std::swap(myTargetBlock, o.myTargetBlock);
77 std::swap(myGroup, o.myGroup);
78 members.swap(o.members);
79 }
80 void release() noexcept {
81 int fin = 0;
83 if (!fin) {
84 if (group != MPI_COMM_NULL && group != parent) // Repartition: group IS parent
86 if (sub != MPI_COMM_NULL)
88 }
90 }
91};
92
104template <int Dim>
106 if (t.kind == StageKind::InPlace)
107 throw std::invalid_argument("makeStageComm: an InPlace target has no stage");
108 int rank = 0, size = 1;
109 MPI_Comm_rank(parent, &rank);
110 MPI_Comm_size(parent, &size);
111
112 StageComm c;
113 c.parent = parent;
114 c.kind = t.kind;
115 if (t.kind == StageKind::SiblingMerge) {
116 if (t.groupOf.size() != static_cast<std::size_t>(size))
117 throw std::invalid_argument(
118 "makeStageComm: groupOf must map every block of the current decomposition, one per rank");
119 const int myGroup = t.groupOf[static_cast<std::size_t>(rank)];
120 const bool owner = (rank == t.ownerOf[static_cast<std::size_t>(myGroup)]);
121 MPI_Comm_split(parent, myGroup, rank, &c.group);
122 MPI_Comm_split(parent, owner ? 0 : MPI_UNDEFINED, myGroup, &c.sub);
123 c.active = owner;
124 c.myGroup = myGroup;
125 c.myTargetBlock = owner ? myGroup : -1;
126 for (int b = 0; b < size; ++b)
127 if (t.groupOf[static_cast<std::size_t>(b)] == myGroup)
128 c.members.push_back(b);
129 if (c.members.front() != t.ownerOf[static_cast<std::size_t>(myGroup)])
130 throw std::logic_error("makeStageComm: a group's owner must be its lowest rank");
131 } else if (t.kind == StageKind::Repartition) {
132 const std::size_t npL = t.dec.numBlocks();
133 if (!t.groupOf.empty() || t.ownerOf.size() != npL || npL < 1 ||
134 npL > static_cast<std::size_t>(size))
135 throw std::invalid_argument(
136 "makeStageComm: a Repartition target needs 1..size blocks, ownerOf per block, no "
137 "groupOf");
138 for (std::size_t b = 0; b < npL; ++b)
139 if (t.ownerOf[b] != static_cast<int>(b))
140 throw std::invalid_argument(
141 "makeStageComm: a Repartition target's owners must be the identity on [0, np_L)");
142 const bool owner = static_cast<std::size_t>(rank) < npL;
143 c.group = parent;
144 MPI_Comm_split(parent, owner ? 0 : MPI_UNDEFINED, rank, &c.sub);
145 c.active = owner;
146 c.myGroup = -1;
147 c.myTargetBlock = owner ? rank : -1;
148 } else { // Replicated
149 MPI_Comm_split(parent, 0, rank, &c.group);
150 MPI_Comm_split(parent, 0, rank, &c.sub);
151 c.active = true;
152 c.myGroup = 0;
153 c.myTargetBlock = 0;
154 c.members.resize(static_cast<std::size_t>(size));
155 for (int r = 0; r < size; ++r)
156 c.members[static_cast<std::size_t>(r)] = r;
157 }
158 return c;
159}
160
161} // namespace peclet::core::decomp
162
163#endif // PECLET_CORE_DECOMP_STAGE_COMM_HPP
int MPI_Comm_size(MPI_Comm, int *s)
Definition mpi_stub.hpp:70
#define MPI_COMM_NULL
Definition mpi_stub.hpp:28
int MPI_Comm
Definition mpi_stub.hpp:17
int MPI_Comm_free(MPI_Comm *)
Definition mpi_stub.hpp:107
int MPI_Finalized(int *f)
Definition mpi_stub.hpp:58
int MPI_Comm_rank(MPI_Comm, int *r)
Definition mpi_stub.hpp:66
#define MPI_UNDEFINED
Definition mpi_stub.hpp:34
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.
StageComm makeStageComm(MPI_Comm parent, const StageTarget< Dim > &t)
Build the stage communicators for t on parent.
Kokkos::View< T *, MemSpace > View
1D device array.
Definition view.hpp:26
The communicators of one coarse-level multigrid stage, made by makeStageComm (the file comment spells...
StageComm(const StageComm &)=delete
MPI_Comm sub
owners only, key = target block; MPI_COMM_NULL elsewhere
StageComm & operator=(const StageComm &)=delete
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
StageComm & operator=(StageComm &&o) noexcept
Frees this object's own communicators first, then takes over o's.
StageComm(StageComm &&o) noexcept
Takes over o's communicators; o is left empty (all MPI_COMM_NULL, nothing to free).
MPI_Comm group
this rank's target block's members; owner = group rank 0
int myGroup
the target block this rank's current block moves into (-1: Repartition)