17#ifndef PECLET_CORE_HALO_GRID_HALO_TOPOLOGY_HPP
18#define PECLET_CORE_HALO_GRID_HALO_TOPOLOGY_HPP
44 std::memcpy(&v,
src,
sizeof(
T));
60 indexer_.init(dec.
origins()[rank], dec.
sizes()[rank], ghostWidth);
69 if (indexer_.isInner(
lmd))
73 for (
int i = 0;
i <
Dim; ++
i) {
74 g[
i] =
lmd[
i] + indexer_.originInclGhost()[
i];
93 selfSrc_.push_back(indexer_.globalToLocal(
gw));
101 recvRanks_.push_back(r);
102 recvIdx_.push_back(std::move(idx));
109 auto packNext = [&](std::vector<char>&
out) ->
int {
110 if (
k >= recvRanks_.size())
112 int dest = recvRanks_[
k];
113 const auto& g = recvGlob_[
k];
115 out.resize(g.size() *
sizeof(
Index));
116 std::memcpy(
out.data(), g.data(),
out.size());
120 std::size_t n =
msg.size() /
sizeof(
Index);
122 std::vector<Index>
sidx(n);
123 for (std::size_t
i = 0;
i < n; ++
i) {
126 sendRanks_.push_back(
src);
127 sendIdx_.push_back(std::move(
sidx));
136 long cnt[2] = {0, 0};
137 for (
const auto& g : recvGlob_)
138 cnt[0] +=
static_cast<long>(g.size());
139 for (
const auto&
sidx : sendIdx_)
140 cnt[1] +=
static_cast<long>(
sidx.size());
141 long tot[2] = {0, 0};
144 throw std::runtime_error(
"peclet.core GridHaloTopology: asymmetric halo topology (" +
145 std::to_string(
tot[0]) +
" cells requested, " +
146 std::to_string(
tot[1]) +
147 " promised) -- the NBX round lost messages");
151 persistentReady_ =
false;
158 template <
typename Field>
160 applySelfCopy(
field);
161 const std::size_t
es =
field.bytesPerElem();
165 recvBufs_.resize(recvRanks_.size());
166 for (std::size_t
k = 0;
k < recvRanks_.size(); ++
k) {
167 recvBufs_[
k].resize(recvIdx_[
k].size() *
es);
169 tag, comm_, &recvReqs_[
k]);
173 sendBufs_.resize(sendRanks_.size());
174 for (std::size_t
k = 0;
k < sendRanks_.size(); ++
k) {
175 auto&
buf = sendBufs_[
k];
176 buf.resize(sendIdx_[
k].size() *
es);
177 for (std::size_t
i = 0;
i < sendIdx_[
k].size(); ++
i) {
187 template <
typename Field>
189 const std::size_t
es =
field.bytesPerElem();
191 for (std::size_t
done = 0;
done < recvReqs_.size(); ++
done) {
196 for (std::size_t
i = 0;
i < recvIdx_[
k].size(); ++
i) {
197 field.unpack(recvIdx_[
k][
i], recvBufs_[
k].data() +
i *
es);
200 if (!sendReqs_.empty()) {
206 template <
typename Field>
217 template <
typename Field>
219 applySelfCopy(
field);
220 const std::size_t
es =
field.bytesPerElem();
224 for (std::size_t
k = 0;
k < sendRanks_.size(); ++
k) {
225 char*
dst = graphSendBuf_.data() + graphSendDispl_[
k];
226 for (std::size_t
i = 0;
i < sendIdx_[
k].size(); ++
i) {
231 MPI_BYTE, graphRecvBuf_.data(), graphRecvCnt_.data(),
232 graphRecvDispl_.data(),
MPI_BYTE, graphComm_);
234 for (std::size_t
k = 0;
k < recvRanks_.size(); ++
k) {
235 const char*
src = graphRecvBuf_.data() + graphRecvDispl_[
k];
236 for (std::size_t
i = 0;
i < recvIdx_[
k].size(); ++
i) {
248 template <
typename Field>
250 const std::size_t
es =
field.bytesPerElem();
252 std::vector<char>
tmp(
es);
253 for (std::size_t
i = 0;
i < selfSrc_.size(); ++
i) {
258 std::vector<std::vector<char>>
rbuf(sendRanks_.size());
260 for (std::size_t
k = 0;
k < sendRanks_.size(); ++
k) {
261 rbuf[
k].resize(sendIdx_[
k].size() *
es);
266 std::vector<std::vector<char>>
sbuf(recvRanks_.size());
268 for (std::size_t
k = 0;
k < recvRanks_.size(); ++
k) {
269 sbuf[
k].resize(recvIdx_[
k].size() *
es);
270 for (std::size_t
i = 0;
i < recvIdx_[
k].size(); ++
i)
276 for (std::size_t
done = 0;
done < sendRanks_.size(); ++
done) {
281 for (std::size_t
i = 0;
i < sendIdx_[
k].size(); ++
i)
300 t.sendRanks = std::vector<int>(sendRanks_.begin(), sendRanks_.end());
301 t.recvRanks = std::vector<int>(recvRanks_.begin(), recvRanks_.end());
302 for (
const auto& v : sendIdx_) {
303 t.sendCounts.push_back(
static_cast<int>(v.size()));
304 t.sendIdx.insert(
t.sendIdx.end(), v.begin(), v.end());
306 for (
const auto& v : recvIdx_) {
307 t.recvCounts.push_back(
static_cast<int>(v.size()));
308 t.recvIdx.insert(
t.recvIdx.end(), v.begin(), v.end());
310 t.selfSrc = selfSrc_;
311 t.selfDst = selfDst_;
320 for (
auto& v : recvIdx_)
333 void freeGraphComm() {
352 persistentReady_ =
false;
355 void buildRecvLookup() {
356 recvRankPos_.clear();
357 for (std::size_t
k = 0;
k < recvRanks_.size(); ++
k)
358 recvRankPos_[recvRanks_[
k]] =
k;
361 template <
typename Field>
365 if (selfSrc_.empty())
367 std::vector<char>
tmp(
field.bytesPerElem());
368 for (std::size_t
i = 0;
i < selfSrc_.size(); ++
i) {
374 void ensureGraphComm(std::size_t
es) {
375 if (persistentReady_ && graphElemSize_ ==
es)
381 std::vector<int>
sources(recvRanks_.begin(), recvRanks_.end());
382 std::vector<int>
dests(sendRanks_.begin(), sendRanks_.end());
389 graphSendCnt_.resize(
dests.size());
390 graphSendDispl_.resize(
dests.size());
392 for (std::size_t
k = 0;
k <
dests.size(); ++
k) {
393 graphSendCnt_[
k] =
static_cast<int>(sendIdx_[
k].size() *
es);
394 graphSendDispl_[
k] = off;
395 off += graphSendCnt_[
k];
397 graphSendBuf_.resize(off);
399 graphRecvCnt_.resize(
sources.size());
400 graphRecvDispl_.resize(
sources.size());
402 for (std::size_t
k = 0;
k <
sources.size(); ++
k) {
403 graphRecvCnt_[
k] =
static_cast<int>(recvIdx_[
k].size() *
es);
404 graphRecvDispl_[
k] = off;
405 off += graphRecvCnt_[
k];
407 graphRecvBuf_.resize(off);
410 persistentReady_ =
true;
415 decomp::BlockIndexer<Dim> indexer_;
418 std::vector<int> sendRanks_;
419 std::vector<std::vector<Index>> sendIdx_;
420 std::vector<int> recvRanks_;
421 std::vector<std::vector<Index>> recvIdx_;
422 std::vector<std::vector<Index>> recvGlob_;
423 std::map<int, std::size_t> recvRankPos_;
424 std::vector<Index> selfSrc_, selfDst_;
427 std::vector<MPI_Request> sendReqs_, recvReqs_;
428 std::vector<std::vector<char>> sendBufs_, recvBufs_;
433 bool persistentReady_ =
false;
434 std::size_t graphElemSize_ = 0;
435 std::vector<int> graphSendCnt_, graphSendDispl_, graphRecvCnt_, graphRecvDispl_;
436 std::vector<char> graphSendBuf_, graphRecvBuf_;
Orthogonal recursive bisection of a Dim-dimensional global cell grid into numBlocks axis-aligned bloc...
const std::vector< IVec< Dim > > & sizes() const
Extent in cells of every block, indexed by block.
const std::vector< IVec< Dim > > & origins() const
Inclusive lower corner of every block, in global cell coordinates, indexed by block.
const IVec< Dim > & globalSize() const
Global grid size in cells per axis.
Index linearGlobal(const IVec< Dim > &g) const
Global multi-index -> global linear index (x-fastest: I = x + y*nx + z*nx*ny).
int ownerOf(const IVec< Dim > &g) const
Owning block index of a global cell coordinate. Caller must wrap into [0, globalSize) first.
IVec< Dim > multiGlobal(Index lin) const
Global linear index -> global multi-index (inverse of linearGlobal).
void start(Field &field, int tag=0)
Post all sends/recvs; returns immediately so the caller can compute the block interior.
void reverseAdd(Field &field, int tag=0)
void buildTopology(const decomp::BlockDecomposer< Dim > &dec, int rank, int ghostWidth, const std::array< bool, Dim > &periodic, MPI_Comm comm=MPI_COMM_WORLD)
Build the halo topology for rank's block of dec, with the given ghost width and per-axis periodicity.
const decomp::BlockIndexer< Dim > & indexer() const
void exchangeNbx(Field &field, int tag=0)
Convenience: full blocking exchange via the NBX-style engine (start + wait).
void wait(Field &field)
Complete a started exchange: wait for receives and scatter them into the ghost cells.
GridHaloTopology(const GridHaloTopology &)=delete
std::size_t numGhostRecv() const
FlatTopology flatten() const
GridHaloTopology & operator=(const GridHaloTopology &)=delete
std::size_t numNeighbors() const
void exchangePersistent(Field &field)
Full exchange via MPI_Neighbor_alltoallv on a cached distributed-graph communicator.
GridHaloTopology()=default
int MPI_Allreduce(const void *sbuf, void *rbuf, int count, MPI_Datatype dt, MPI_Op, MPI_Comm)
int MPI_Waitall(int, MPI_Request *, MPI_Status *)
int MPI_Comm_free(MPI_Comm *)
#define MPI_STATUS_IGNORE
#define MPI_STATUSES_IGNORE
int MPI_Waitany(int, MPI_Request *, int *idx, MPI_Status *)
int MPI_Isend(const void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *)
int MPI_Finalized(int *f)
int MPI_Irecv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *)
int MPI_Neighbor_alltoallv(const void *, const int *, const int *, MPI_Datatype, void *, const int *, const int *, MPI_Datatype, MPI_Comm)
int MPI_Dist_graph_create_adjacent(MPI_Comm c, int, const int *, const int *, int, const int *, const int *, MPI_Info, int, MPI_Comm *out)
Index wrap(Index x, Index n)
Periodic wrap of a coordinate into [0, n): wrap(x, n) = (x % n + n) % n.
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).
A contiguous local array of T (one per cell of the extended block) viewed as a packable field.
void addFrom(Index localIdx, const char *src)
void pack(Index localIdx, char *dst) const
void unpack(Index localIdx, const char *src)
std::size_t bytesPerElem() const
std::vector< int > recvCounts
std::vector< int > sendRanks
std::vector< int > sendCounts
std::vector< Index > recvIdx
std::vector< int > recvRanks
std::vector< Index > selfSrc
std::vector< Index > selfDst
std::vector< Index > sendIdx