37#ifndef PECLET_AMR_LEAF_HALO_HPP
38#define PECLET_AMR_LEAF_HALO_HPP
49#include "peclet/core/common/mpi.hpp"
50#include "peclet/core/common/types.hpp"
54#ifdef KOKKOS_INLINE_FUNCTION
55#include "peclet/core/common/view.hpp"
56#include "peclet/core/halo/grid_halo.hpp"
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 std::lock_guard<std::mutex>
lk(*missMx_);
151 for (
int a = 0;
a < Dim; ++
a)
152 gc[
a] =
static_cast<Coord>(g[
a]);
162 for (
int a = 0;
a < Dim; ++
a) {
163 const long v =
static_cast<long>(gc[
a]) - fineOrigin_[
a];
170 std::array<Coord, Dim>
lc{};
171 for (
int a = 0;
a < Dim; ++
a)
172 lc[
a] =
static_cast<Coord>(
static_cast<long>(gc[
a]) - fineOrigin_[
a]);
175 auto it = probeSlot_.find(gc);
176 if (
it == probeSlot_.end())
177 throw std::runtime_error(
"amr::LeafHalo::lookup: coord not in the frozen registry");
184 for (
int a = 0;
a < Dim; ++
a)
185 gc[
a] =
static_cast<Coord>(g[
a]);
195 throw std::runtime_error(
"amr::LeafHalo::resolveMisses after finalize()");
200 std::vector<CoordArr>
coords;
201 coords.reserve(misses_.size());
202 for (
const auto&
kv : misses_)
206 for (std::size_t k = 0; k <
coords.size(); ++k) {
209 throw std::runtime_error(
210 "amr::LeafHalo: a wrapped probe has no covering leaf on its owner (octree hole?)");
212 for (
int a = 0;
a < Dim; ++
a)
216 if (
it != anchorSlot_.end()) {
219 g =
static_cast<Index>(ghostCoords_.size());
220 anchorSlot_.emplace(
anchor, g);
221 ghostCoords_.push_back(
anchor);
222 ghostLevels_.push_back(
L);
224 probeSlot_[
coords[k]] = nLocal_ + g;
225 probeSlot_[
anchor] = nLocal_ + g;
234 return static_cast<int>(d_->
local().
level(slot));
235 return ghostLevels_[
static_cast<std::size_t
>(slot - nLocal_)];
244 typename DO::FaceGatherPlan
plan;
246 plan.remoteCoords = ghostCoords_;
247 plan.remoteSlot.resize(ghostCoords_.size());
248 for (std::size_t g = 0; g < ghostCoords_.size(); ++g)
249 plan.remoteSlot[g] = nLocal_ +
static_cast<Index>(g);
253 if (!topo_.localSlot.empty())
254 throw std::runtime_error(
"amr::LeafHalo::finalize: ghost anchor resolved self-owned");
255 for (
Index l : topo_.sendLeaf)
257 throw std::runtime_error(
258 "amr::LeafHalo::finalize: an owner cannot locate a requested ghost leaf");
262 const typename DO::GatherHaloTopology&
topology()
const {
return topo_; }
267 const auto&
t = topo_;
269 for (std::size_t k = 0; k <
t.sendLeaf.size(); ++k)
270 sendBuf[k] = x[
static_cast<std::size_t
>(
t.sendLeaf[k])];
271 std::vector<MPI_Request>
reqs;
272 reqs.reserve(
t.recvRanks.size() +
t.sendRanks.size());
274 for (std::size_t k = 0; k <
t.recvRanks.size(); ++k) {
278 off +=
static_cast<std::size_t
>(
t.recvCounts[k]);
281 for (std::size_t k = 0; k <
t.sendRanks.size(); ++k) {
285 off +=
static_cast<std::size_t
>(
t.sendCounts[k]);
289 for (std::size_t k = 0; k <
t.recvSlot.size(); ++k)
290 x[
static_cast<std::size_t
>(
t.recvSlot[k])] =
recvBuf[k];
296 std::array<long, Dim> fineOrigin_{}, fineSize_{}, globalFine_{};
297 std::array<bool, Dim> periodic_{};
298 std::map<CoordArr, Index> probeSlot_;
299 std::map<CoordArr, Index> anchorSlot_;
300 std::map<CoordArr, Index> misses_;
303 std::unique_ptr<std::mutex> missMx_ = std::make_unique<std::mutex>();
304 std::vector<CoordArr> ghostCoords_;
305 std::vector<int> ghostLevels_;
306 typename DO::GatherHaloTopology topo_;
307 bool frozen_ =
false;
311#ifdef KOKKOS_INLINE_FUNCTION
319 template <
int Dim,
unsigned Bits>
321 const auto&
t =
h.topology();
323 nLocal_ =
h.numLocal();
324 nSend_ =
static_cast<Index>(
t.sendLeaf.size());
325 nRecv_ =
static_cast<Index>(
t.recvSlot.size());
326 d_sendLeaf_ =
toDevice(
t.sendLeaf,
"lh::sendLeaf");
327 d_recvSlot_ =
toDevice(
t.recvSlot,
"lh::recvSlot");
328 sendRanks_ =
t.sendRanks;
329 sendCounts_ =
t.sendCounts;
330 recvRanks_ =
t.recvRanks;
331 recvCounts_ =
t.recvCounts;
332 sendOff_.assign(sendCounts_.size() + 1, 0);
333 for (std::size_t k = 0; k < sendCounts_.size(); ++k)
334 sendOff_[k + 1] = sendOff_[k] + sendCounts_[k];
335 recvOff_.assign(recvCounts_.size() + 1, 0);
336 for (std::size_t k = 0; k < recvCounts_.size(); ++k)
337 recvOff_[k + 1] = recvOff_[k] + recvCounts_[k];
339 d_sendBuf_ =
View<double>(Kokkos::view_alloc(
"lh::sendBuf", Kokkos::WithoutInitializing),
340 static_cast<std::size_t
>(nSend_) * 3);
341 d_recvBuf_ =
View<double>(Kokkos::view_alloc(
"lh::recvBuf", Kokkos::WithoutInitializing),
342 static_cast<std::size_t
>(nRecv_) * 3);
343 h_sendBuf_ = Kokkos::create_mirror_view(d_sendBuf_);
344 h_recvBuf_ = Kokkos::create_mirror_view(d_recvBuf_);
354 Kokkos::parallel_for(
355 "lh::pack", Kokkos::RangePolicy<ExecSpace>(0, nSend_),
362 Kokkos::parallel_for(
363 "lh::scatter", Kokkos::RangePolicy<ExecSpace>(0, nRecv_),
374 Kokkos::parallel_for(
386 Kokkos::parallel_for(
387 "lh::scatter3", Kokkos::RangePolicy<ExecSpace>(0, nRecv_),
KOKKOS_LAMBDA(
const Index k) {
399 void transfer(
int width,
int tag)
const {
400 const bool aware = peclet::core::halo::detail::gpuAwareMpi();
401 if (nSend_ && !
aware)
402 Kokkos::deep_copy(h_sendBuf_, d_sendBuf_);
404 double*
sendBase =
aware ? d_sendBuf_.data() : h_sendBuf_.data();
405 double*
recvBase =
aware ? d_recvBuf_.data() : h_recvBuf_.data();
406 std::vector<MPI_Request>
reqs;
407 reqs.reserve(recvRanks_.size() + sendRanks_.size());
408 for (std::size_t k = 0; k < recvRanks_.size(); ++k) {
411 recvCounts_[k] *
width *
static_cast<int>(
sizeof(
double)),
MPI_BYTE, recvRanks_[k],
414 for (std::size_t k = 0; k < sendRanks_.size(); ++k) {
417 sendCounts_[k] *
width *
static_cast<int>(
sizeof(
double)),
MPI_BYTE, sendRanks_[k],
422 if (nRecv_ && !
aware)
423 Kokkos::deep_copy(d_recvBuf_, h_recvBuf_);
427 Index nLocal_ = 0, nSend_ = 0, nRecv_ = 0;
429 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.
typename Octree::Coord Coord
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 std::array< bool, Dim > & periodic() const
const IVec< Dim > & globalFineSize() 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 > & blockBrick() const
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 kNone
resolveGlobal(): exits a non-periodic axis
Index lookup(const CoordArr &gc) const
Const lookup against the FINALIZED registry (no miss registration): wrapped probe → local leaf or cac...
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...
Index resolveGlobal(std::array< long, Dim > g)
Wrap + resolve from unbounded long coords; kNone on a non-periodic exit.
DistributedOctree< Dim, Bits > DO
static constexpr Index kPending
resolve(): queued as a miss (call resolveMisses)
Index resolve(const CoordArr &gc)
Resolve a wrapped global fine coordinate to an extended slot: [0, nLocal) = the covering LOCAL leaf; ...
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
void exchangeHost(std::vector< double > &x, int tag=45) const
Host exchange: refresh x[nLocal, nLocal+nGhost) from the owners (x sized extendedSize()).
int level(Index slot) const
Covering-leaf level of an extended slot (local octree level convention: 0 = finest cell).
const DO::GatherHaloTopology & topology() const
std::array< Coord, Dim > CoordArr
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...
Index lookupGlobal(std::array< long, Dim > g) const
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,...