38#ifndef PECLET_CORE_AMR_LEAF_HALO_HPP
39#define PECLET_CORE_AMR_LEAF_HALO_HPP
41#ifdef PECLET_CORE_HAVE_MORTON
54#ifdef KOKKOS_INLINE_FUNCTION
61template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
75 for (
int a = 0;
a < Dim; ++
a) {
96 bool wrap(std::array<long, Dim>& g)
const {
97 for (
int a = 0;
a < Dim; ++
a) {
98 const long gf = globalFine_[
a];
99 if (g[
a] < 0 || g[
a] >=
gf) {
113 for (
int a = 0;
a < Dim; ++
a) {
114 const long v =
static_cast<long>(gc[
a]) - fineOrigin_[
a];
121 std::array<Coord, Dim>
lc{};
122 for (
int a = 0;
a < Dim; ++
a)
123 lc[
a] =
static_cast<Coord>(
static_cast<long>(gc[
a]) - fineOrigin_[
a]);
126 auto it = probeSlot_.find(gc);
127 if (
it != probeSlot_.end())
130 throw std::runtime_error(
"amr::LeafHalo::resolve: unknown coord after finalize()");
140 for (
int a = 0;
a < Dim; ++
a)
141 gc[
a] =
static_cast<Coord>(g[
a]);
151 for (
int a = 0;
a < Dim; ++
a) {
152 const long v =
static_cast<long>(gc[
a]) - fineOrigin_[
a];
159 std::array<Coord, Dim>
lc{};
160 for (
int a = 0;
a < Dim; ++
a)
161 lc[
a] =
static_cast<Coord>(
static_cast<long>(gc[
a]) - fineOrigin_[
a]);
164 auto it = probeSlot_.find(gc);
165 if (
it == probeSlot_.end())
166 throw std::runtime_error(
"amr::LeafHalo::lookup: coord not in the frozen registry");
173 for (
int a = 0;
a < Dim; ++
a)
174 gc[
a] =
static_cast<Coord>(g[
a]);
184 throw std::runtime_error(
"amr::LeafHalo::resolveMisses after finalize()");
189 std::vector<CoordArr>
coords;
190 coords.reserve(misses_.size());
191 for (
const auto&
kv : misses_)
195 for (std::size_t k = 0; k <
coords.size(); ++k) {
198 throw std::runtime_error(
199 "amr::LeafHalo: a wrapped probe has no covering leaf on its owner (octree hole?)");
201 for (
int a = 0;
a < Dim; ++
a)
205 if (
it != anchorSlot_.end()) {
208 g =
static_cast<Index>(ghostCoords_.size());
209 anchorSlot_.emplace(
anchor, g);
210 ghostCoords_.push_back(
anchor);
211 ghostLevels_.push_back(
L);
213 probeSlot_[
coords[k]] = nLocal_ + g;
214 probeSlot_[
anchor] = nLocal_ + g;
223 return static_cast<int>(d_->
local().
level(slot));
224 return ghostLevels_[
static_cast<std::size_t
>(slot - nLocal_)];
229 return ghostCoords_[
static_cast<std::size_t
>(g)];
235 typename DO::FaceGatherPlan
plan;
237 plan.remoteCoords = ghostCoords_;
238 plan.remoteSlot.resize(ghostCoords_.size());
239 for (std::size_t g = 0; g < ghostCoords_.size(); ++g)
240 plan.remoteSlot[g] = nLocal_ +
static_cast<Index>(g);
244 if (!topo_.localSlot.empty())
245 throw std::runtime_error(
"amr::LeafHalo::finalize: ghost anchor resolved self-owned");
246 for (
Index l : topo_.sendLeaf)
248 throw std::runtime_error(
249 "amr::LeafHalo::finalize: an owner cannot locate a requested ghost leaf");
253 const typename DO::GatherHaloTopology&
topology()
const {
return topo_; }
258 const auto&
t = topo_;
260 for (std::size_t k = 0; k <
t.sendLeaf.size(); ++k)
261 sendBuf[k] = x[
static_cast<std::size_t
>(
t.sendLeaf[k])];
262 std::vector<MPI_Request>
reqs;
263 reqs.reserve(
t.recvRanks.size() +
t.sendRanks.size());
265 for (std::size_t k = 0; k <
t.recvRanks.size(); ++k) {
268 t.recvCounts[k] *
static_cast<int>(
sizeof(
double)),
MPI_BYTE,
t.recvRanks[k],
tag,
270 off +=
static_cast<std::size_t
>(
t.recvCounts[k]);
273 for (std::size_t k = 0; k <
t.sendRanks.size(); ++k) {
276 t.sendCounts[k] *
static_cast<int>(
sizeof(
double)),
MPI_BYTE,
t.sendRanks[k],
tag,
278 off +=
static_cast<std::size_t
>(
t.sendCounts[k]);
282 for (std::size_t k = 0; k <
t.recvSlot.size(); ++k)
283 x[
static_cast<std::size_t
>(
t.recvSlot[k])] =
recvBuf[k];
289 std::array<long, Dim> fineOrigin_{}, fineSize_{}, globalFine_{};
290 std::array<bool, Dim> periodic_{};
291 std::map<CoordArr, Index> probeSlot_;
292 std::map<CoordArr, Index> anchorSlot_;
293 std::map<CoordArr, Index> misses_;
294 std::vector<CoordArr> ghostCoords_;
295 std::vector<int> ghostLevels_;
296 typename DO::GatherHaloTopology topo_;
297 bool frozen_ =
false;
301#ifdef KOKKOS_INLINE_FUNCTION
309 template <
int Dim,
unsigned Bits>
311 const auto&
t =
h.topology();
313 nLocal_ =
h.numLocal();
314 nSend_ =
static_cast<Index>(
t.sendLeaf.size());
315 nRecv_ =
static_cast<Index>(
t.recvSlot.size());
316 d_sendLeaf_ =
toDevice(
t.sendLeaf,
"lh::sendLeaf");
317 d_recvSlot_ =
toDevice(
t.recvSlot,
"lh::recvSlot");
318 sendRanks_ =
t.sendRanks;
319 sendCounts_ =
t.sendCounts;
320 recvRanks_ =
t.recvRanks;
321 recvCounts_ =
t.recvCounts;
322 sendOff_.assign(sendCounts_.size() + 1, 0);
323 for (std::size_t k = 0; k < sendCounts_.size(); ++k)
324 sendOff_[k + 1] = sendOff_[k] + sendCounts_[k];
325 recvOff_.assign(recvCounts_.size() + 1, 0);
326 for (std::size_t k = 0; k < recvCounts_.size(); ++k)
327 recvOff_[k + 1] = recvOff_[k] + recvCounts_[k];
329 d_sendBuf_ =
View<double>(Kokkos::view_alloc(
"lh::sendBuf", Kokkos::WithoutInitializing),
330 static_cast<std::size_t
>(nSend_) * 3);
331 d_recvBuf_ =
View<double>(Kokkos::view_alloc(
"lh::recvBuf", Kokkos::WithoutInitializing),
332 static_cast<std::size_t
>(nRecv_) * 3);
333 h_sendBuf_ = Kokkos::create_mirror_view(d_sendBuf_);
334 h_recvBuf_ = Kokkos::create_mirror_view(d_recvBuf_);
344 Kokkos::parallel_for(
345 "lh::pack", Kokkos::RangePolicy<ExecSpace>(0, nSend_),
352 Kokkos::parallel_for(
353 "lh::scatter", Kokkos::RangePolicy<ExecSpace>(0, nRecv_),
364 Kokkos::parallel_for(
365 "lh::pack3", Kokkos::RangePolicy<ExecSpace>(0, nSend_),
KOKKOS_LAMBDA(
const Index p) {
376 Kokkos::parallel_for(
377 "lh::scatter3", Kokkos::RangePolicy<ExecSpace>(0, nRecv_),
KOKKOS_LAMBDA(
const Index k) {
389 void transfer(
int width,
int tag)
const {
391 if (nSend_ && !
aware)
392 Kokkos::deep_copy(h_sendBuf_, d_sendBuf_);
394 double*
sendBase =
aware ? d_sendBuf_.data() : h_sendBuf_.data();
395 double*
recvBase =
aware ? d_recvBuf_.data() : h_recvBuf_.data();
396 std::vector<MPI_Request>
reqs;
397 reqs.reserve(recvRanks_.size() + sendRanks_.size());
398 for (std::size_t k = 0; k < recvRanks_.size(); ++k) {
401 recvCounts_[k] *
width *
static_cast<int>(
sizeof(
double)),
MPI_BYTE, recvRanks_[k],
404 for (std::size_t k = 0; k < sendRanks_.size(); ++k) {
407 sendCounts_[k] *
width *
static_cast<int>(
sizeof(
double)),
MPI_BYTE, sendRanks_[k],
412 if (nRecv_ && !
aware)
413 Kokkos::deep_copy(d_recvBuf_, h_recvBuf_);
417 Index nLocal_ = 0, nSend_ = 0, nRecv_ = 0;
419 std::vector<int> sendRanks_, sendCounts_, sendOff_, recvRanks_, recvCounts_, recvOff_;
unsigned level(Index i) const
Index find(Code p) const
Leaf containing Morton code p, or -1. Host wrapper over amrLocate.
const std::array< bool, Dim > & periodic() const
GatherHaloTopology buildGatherHaloTopology(const FaceGatherPlan &plan) const
Build the value-only gather topology from a FaceGatherPlan: classify each remote coord by owner (owne...
const IVec< Dim > & globalFineSize() const
std::vector< int > coverLevels(const std::vector< std::array< Coord, Dim > > &coords) const
For each global fine coord (already wrapped into the domain), the level of the covering leaf on its o...
const IVec< Dim > & blockFineOrigin() const
const IVec< Dim > & blockBrick() const
typename Octree::Coord Coord
Device-resident value refresh over a finalized LeafHalo: pack the owner's local values as a Kokkos ke...
void init(const LeafHalo< Dim, Bits > &h)
void exchange3(View< double > x0, View< double > x1, View< double > x2, int tag=46) const
Batched 3-component refresh (one message round for u0,u1,u2 — stride-3 packing).
void exchange(View< double > x, int tag=45) const
Refresh x[nLocal, nLocal+nGhost) (x is the extended device field, size >= extendedSize()).
static constexpr Index kPending
resolve(): queued as a miss (call resolveMisses)
void exchangeHost(std::vector< double > &x, int tag=45) const
Host exchange: refresh x[nLocal, nLocal+nGhost) from the owners (x sized extendedSize()).
Index lookup(const CoordArr &gc) const
Const lookup against the FINALIZED registry (no miss registration): wrapped probe → local leaf or cac...
Index resolve(const CoordArr &gc)
Resolve a wrapped global fine coordinate to an extended slot: [0, nLocal) = the covering LOCAL leaf; ...
DistributedOctree< Dim, Bits > DO
Index lookupGlobal(std::array< long, Dim > g) const
static constexpr Index kNone
resolveGlobal(): exits a non-periodic axis
int level(Index slot) const
Covering-leaf level of an extended slot (local octree level convention: 0 = finest cell).
const DO::GatherHaloTopology & topology() const
bool wrap(std::array< long, Dim > &g) const
Wrap an unbounded global fine probe into the domain (in place); false = the probe exits a non-periodi...
const CoordArr & ghostCoord(Index g) const
Global fine anchor (lo corner) of ghost g in [0, nGhosts) — for SDF sampling / multi-hop probe constr...
Index extendedSize() const
Index resolveGlobal(std::array< long, Dim > g)
Wrap + resolve from unbounded long coords; kNone on a non-periodic exit.
long resolveMisses()
COLLECTIVE (all ranks together, misses or not): one owner coverLevels round resolving every queued mi...
void finalize()
Freeze the ghost set and establish the owner↔ghost value topology (one NBX round; the owner-side loca...
std::array< Coord, Dim > CoordArr
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 *)
#define MPI_STATUSES_IGNORE
int MPI_Isend(const void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *)
int MPI_Irecv(void *, int, MPI_Datatype, int, int, MPI_Comm, MPI_Request *)
std::vector< std::array< double, Dim > > transferFieldGradients(const BlockOctree< Dim, Bits > &oldT, const std::vector< double > &oldF)
Per-old-leaf minmod prolongation gradients (per fine-coordinate unit) — transferField's stencil,...
bool gpuAwareMpi()
Whether to hand DEVICE pointers straight to MPI (GPU-aware MPI) instead of host-staging.
View< T > toDevice(const std::vector< T > &h, const std::string &label)
Upload a host std::vector into a freshly-sized device View (empty vector => empty view).
Kokkos::View< T *, MemSpace > View
1D device array.
Kokkos::View< Index *, MemSpace > IndexView
Device array of grid/particle indices (the matched send/recv/self-copy lists).
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).