28#ifndef PECLET_CORE_AMR_DISTRIBUTED_OCTREE_HPP
29#define PECLET_CORE_AMR_DISTRIBUTED_OCTREE_HPP
31#ifdef PECLET_CORE_HAVE_MORTON
41#include "morton/morton.hpp"
51template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
74 rootSpan_ =
Index(1) << lmax_;
76 dec_.init(
static_cast<std::size_t
>(size_), globalRootSize_);
77 auto blk = dec_.block(
static_cast<std::size_t
>(rank_));
78 blockOriginRoot_ =
blk.origin;
79 blockBrick_ =
blk.size;
80 for (
int d = 0; d < Dim; ++d) {
81 blockFineOrigin_[d] = blockOriginRoot_[d] * rootSpan_;
82 blockFineSize_[d] = blockBrick_[d] * rootSpan_;
83 globalFineSize_[d] = globalRootSize_[d] * rootSpan_;
85 local_.
init(blockBrick_, lmax_, blockOriginRoot_);
91 int rank()
const {
return rank_; }
92 int size()
const {
return size_; }
94 unsigned lmax()
const {
return lmax_; }
100 const std::array<bool, Dim>&
periodic()
const {
return periodic_; }
102 double h0()
const {
return globalGeo_.h0; }
107 auto o = M::from_code(local_.
code(
i)).decode();
109 for (
int d = 0; d < Dim; ++d)
110 g[d] =
static_cast<Index>(
o[d]) / rootSpan_ + blockOriginRoot_[d];
117 std::array<Coord, Dim>
lc{};
118 for (
int d = 0; d < Dim; ++d) {
119 long v = g[d] - blockOriginRoot_[d];
122 lc[d] =
static_cast<Coord>(
v * rootSpan_);
124 return local_.
find(M::encode(
lc).code());
130 for (
int d = 0; d < Dim; ++d)
131 g.
origin[d] = globalGeo_.origin[d] +
static_cast<Real>(blockFineOrigin_[d]) * globalGeo_.h0;
138 std::array<Coord, Dim> g{};
139 for (
int d = 0; d < Dim; ++d)
140 g[d] =
static_cast<Coord>(b[0][d] + blockFineOrigin_[d]);
141 return M::encode(g).code();
153 std::map<int, std::vector<char>>
sendBufs;
155 auto consider = [&](
const std::array<Coord, Dim>& gc,
int Lf) {
162 const int Lf =
static_cast<int>(local_.
level(
i));
163 for (
int axis = 0; axis < Dim; ++axis)
164 for (
int dir = -1; dir <= 1; dir += 2) {
165 std::array<Coord, Dim> gc{};
168 if (neighborInfo(
i, axis, dir, gc, owner,
lnb) != Remote)
180 [&](std::vector<char>&
out) ->
int {
188 [&](
int, std::vector<char>&
msg) {
189 parseQueries(
msg, [&](
const std::array<Coord, Dim>& gc,
int Lf) {
consider(gc,
Lf); });
200 long lw =
static_cast<long>(
work),
gw = 0;
222 const int K =
static_cast<int>(
fields.size());
229 for (
int d = 0; d < Dim; ++d)
230 ncells *=
static_cast<std::size_t
>(globalRootSize_[d]);
239 auto nblk =
newDec.block(
static_cast<std::size_t
>(rank_));
243 for (
int d = 0; d < Dim; ++d)
248 std::vector<Code> codes;
249 std::vector<std::uint8_t> levels;
250 std::vector<std::vector<double>>
cols(
static_cast<std::size_t
>(
K));
251 std::map<int, std::vector<char>>
sendBufs;
255 const std::uint8_t
lv =
static_cast<std::uint8_t
>(local_.
level(
i));
259 levels.push_back(
lv);
260 for (
int c = 0;
c <
K; ++
c)
261 cols[
static_cast<std::size_t
>(
c)].push_back(
fields[
c][
static_cast<std::size_t
>(
i)]);
273 [&](std::vector<char>&
out) ->
int {
281 [&](
int, std::vector<char>&
msg) {
282 parseLeaves(
msg,
K, [&](
Code gc, std::uint8_t
lv,
const double*
comps) {
284 levels.push_back(
lv);
285 for (
int c = 0;
c <
K; ++
c)
293 const std::size_t m = codes.size();
295 for (std::size_t
j = 0;
j < m; ++
j) {
296 auto g = M::from_code(codes[
j]).decode();
297 std::array<Coord, Dim>
lc{};
298 for (
int d = 0; d < Dim; ++d)
302 std::vector<std::size_t>
ord(m);
303 for (std::size_t
j = 0;
j < m; ++
j)
305 std::sort(
ord.begin(),
ord.end(),
306 [&](std::size_t
a, std::size_t b) { return localCodes[a] < localCodes[b]; });
310 std::vector<std::vector<double>>
sortedCols(
static_cast<std::size_t
>(
K),
311 std::vector<double>(m));
312 for (std::size_t
j = 0;
j < m; ++
j) {
315 for (
int c = 0;
c <
K; ++
c)
325 for (
int d = 0; d < Dim; ++d)
326 blockFineSize_[d] =
newBrick[d] * rootSpan_;
328 for (
int c = 0;
c <
K; ++
c)
341 const int F = 2 * Dim;
342 std::vector<double>
out(
static_cast<std::size_t
>(n) *
F,
sentinel);
345 std::map<int, std::vector<char>>
reqBufs;
347 for (
int axis = 0; axis < Dim; ++axis)
348 for (
int dir = -1; dir <= 1; dir += 2) {
349 const int slot = faceSlot(
i, axis, dir,
F);
350 std::array<Coord, Dim> gc{};
353 NbState
st = neighborInfo(
i, axis, dir, gc, owner,
lnb);
356 out[
static_cast<std::size_t
>(
slot)] = field[
static_cast<std::size_t
>(
lnb)];
357 }
else if (
st == Remote) {
358 if (owner == rank_) {
361 out[
static_cast<std::size_t
>(
slot)] = field[
static_cast<std::size_t
>(
leaf)];
363 appendRequest(
reqBufs[owner], gc,
static_cast<std::int64_t
>(
slot));
369 std::map<int, std::vector<char>>
replyBufs;
374 [&](std::vector<char>&
outb) ->
int {
382 [&](
int src, std::vector<char>&
msg) {
383 parseRequests(
msg, [&](
const std::array<Coord, Dim>& gc, std::int64_t
reqId) {
395 [&](std::vector<char>&
outb) ->
int {
403 [&](
int, std::vector<char>&
msg) {
404 parseReplies(
msg, [&](std::int64_t
reqId,
double v) {
405 out[
static_cast<std::size_t
>(
reqId)] =
v;
422 std::vector<std::array<Coord, Dim>>
430 const int F = 2 * Dim;
434 for (
int axis = 0; axis < Dim; ++axis)
435 for (
int dir = -1; dir <= 1; dir += 2) {
436 const int slot = faceSlot(
i, axis, dir,
F);
437 std::array<Coord, Dim> gc{};
440 NbState
st = neighborInfo(
i, axis, dir, gc, owner,
lnb);
446 }
else if (
st == Remote) {
459 const std::vector<double>& field,
461 std::vector<double>
out(
static_cast<std::size_t
>(
plan.nFaces),
sentinel);
462 for (std::size_t k = 0; k <
plan.directSlot.size(); ++k)
463 out[
static_cast<std::size_t
>(
plan.directSlot[k])] =
464 field[
static_cast<std::size_t
>(
plan.directLeaf[k])];
466 for (std::size_t k = 0; k <
plan.remoteSlot.size(); ++k)
467 out[
static_cast<std::size_t
>(
plan.remoteSlot[k])] =
vals[k];
498 t.localSlot =
plan.directSlot;
499 t.localLeaf =
plan.directLeaf;
502 std::map<int, std::vector<char>>
req;
504 for (std::size_t k = 0; k <
plan.remoteCoords.size(); ++k) {
505 const std::array<Coord, Dim>& gc =
plan.remoteCoords[k];
507 const int owner = ownerOfFine(gc);
508 if (owner == rank_) {
511 t.localSlot.push_back(
slot);
512 t.localLeaf.push_back(
leaf);
516 appendRequest(
req[owner], gc,
reqId);
521 t.recvRanks.push_back(
kv.first);
522 t.recvCounts.push_back(
static_cast<int>(
kv.second.size()));
524 t.recvSlot.push_back(
s);
531 auto it =
req.begin();
533 [&](std::vector<char>&
o) ->
int {
541 [&](
int src, std::vector<char>&
msg) {
543 parseRequests(
msg, [&](
const std::array<Coord, Dim>& gc, std::int64_t
reqId) {
545 if (
static_cast<std::int64_t
>(
leaves.size()) <=
reqId)
546 leaves.resize(
static_cast<std::size_t
>(
reqId) + 1, -1);
553 t.sendRanks.push_back(
kv.first);
554 t.sendCounts.push_back(
static_cast<int>(
kv.second.size()));
556 t.sendLeaf.push_back(
l);
567 std::array<Coord, Dim>
gc{};
573 f.
state =
static_cast<int>(neighborInfo(
i, axis, dir,
f.gc,
f.owner,
f.localNb));
580 std::vector<double>
out(
coords.size(), -1.0);
581 std::map<int, std::vector<char>>
req;
582 for (std::size_t idx = 0; idx <
coords.size(); ++idx) {
583 int owner = ownerOfFine(
coords[idx]);
584 if (owner == rank_) {
588 appendRequest(
req[owner],
coords[idx],
static_cast<std::int64_t
>(idx));
593 [&](
const std::array<Coord, Dim>& gc) ->
double {
595 return (
leaf >= 0) ?
static_cast<double>(local_.
level(
leaf)) : -1.0;
599 for (std::size_t
i = 0;
i <
out.size(); ++
i)
600 lv[
i] =
static_cast<int>(
out[
i]);
607 const std::vector<double>& field,
610 std::map<int, std::vector<char>>
req;
611 for (std::size_t idx = 0; idx <
coords.size(); ++idx) {
612 int owner = ownerOfFine(
coords[idx]);
613 if (owner == rank_) {
616 out[idx] = field[
static_cast<std::size_t
>(
leaf)];
618 appendRequest(
req[owner],
coords[idx],
static_cast<std::int64_t
>(idx));
623 [&](
const std::array<Coord, Dim>& gc) ->
double {
632 enum NbState { InBlock, Remote, DomainNone };
634 int ownerOfFine(
const std::array<Coord, Dim>& gc)
const {
636 for (
int d = 0; d < Dim; ++d)
644 template <
class Responder>
646 std::vector<double>&
out,
int tagA,
int tagB)
const {
647 std::map<int, std::vector<char>>
rep;
649 halo::NbxEngine
eng(comm_);
650 auto it =
req.begin();
652 [&](std::vector<char>&
o) ->
int {
660 [&](
int src, std::vector<char>&
msg) {
661 parseRequests(
msg, [&](
const std::array<Coord, Dim>& gc, std::int64_t
reqId) {
668 halo::NbxEngine
eng(comm_);
669 auto it =
rep.begin();
671 [&](std::vector<char>&
o) ->
int {
679 [&](
int, std::vector<char>&
msg) {
680 parseReplies(
msg, [&](std::int64_t
reqId,
double v) {
681 out[
static_cast<std::size_t
>(
reqId)] =
v;
688 static int faceSlot(
Index i,
int axis,
int dir,
int F) {
689 return static_cast<int>(
i) *
F + 2 * axis + (dir > 0 ? 0 : 1);
695 NbState neighborInfo(
Index i,
int axis,
int dir, std::array<Coord, Dim>& gc,
int& owner,
696 Index& localNb)
const {
698 const auto&
lo = b[0];
700 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
size)
703 if (
pc >= 0 &&
pc <
static_cast<long>(blockFineSize_[axis])) {
704 std::array<Coord, Dim> p =
lo;
705 p[axis] =
static_cast<Coord>(
pc);
706 localNb = local_.
find(M::encode(p).code());
710 std::array<long, Dim> g{};
711 for (
int d = 0; d < Dim; ++d)
712 g[d] =
static_cast<long>(blockFineOrigin_[d]) +
static_cast<long>(
lo[d]);
713 g[axis] =
static_cast<long>(blockFineOrigin_[axis]) +
pc;
714 for (
int d = 0; d < Dim; ++d) {
715 const long gf =
static_cast<long>(globalFineSize_[d]);
716 if (g[d] < 0 || g[d] >=
gf) {
719 g[d] = ((g[d] %
gf) +
gf) %
gf;
723 for (
int d = 0; d < Dim; ++d) {
724 gc[d] =
static_cast<Coord>(g[d]);
725 rootCell[d] =
static_cast<Index>(g[d] /
static_cast<long>(rootSpan_));
733 Index locateGlobal(
const std::array<Coord, Dim>& gc)
const {
734 std::array<Coord, Dim>
lc{};
735 for (
int d = 0; d < Dim; ++d)
736 lc[d] =
static_cast<Coord>(gc[d] - blockFineOrigin_[d]);
737 return local_.
find(M::encode(
lc).code());
741 static void appendBytes(std::vector<char>&
buf,
const void* p, std::size_t n) {
742 const char*
c =
static_cast<const char*
>(p);
745 static void appendQuery(std::vector<char>&
buf,
const std::array<Coord, Dim>& gc,
int Lf) {
746 for (
int d = 0; d < Dim; ++d) {
747 std::int64_t
v =
static_cast<std::int64_t
>(gc[d]);
748 appendBytes(
buf, &
v,
sizeof(
v));
751 appendBytes(
buf, &
l,
sizeof(
l));
754 static void parseQueries(
const std::vector<char>&
buf,
Fn&&
fn) {
756 const std::size_t
item = Dim *
sizeof(std::int64_t) +
sizeof(std::int32_t);
757 while (off +
item <=
buf.size()) {
758 std::array<Coord, Dim> gc{};
759 for (
int d = 0; d < Dim; ++d) {
761 std::memcpy(&
v,
buf.data() + off,
sizeof(
v));
763 gc[d] =
static_cast<Coord>(
v);
766 std::memcpy(&
l,
buf.data() + off,
sizeof(
l));
768 fn(gc,
static_cast<int>(
l));
771 static void appendRequest(std::vector<char>&
buf,
const std::array<Coord, Dim>& gc,
772 std::int64_t
reqId) {
773 for (
int d = 0; d < Dim; ++d) {
774 std::int64_t
v =
static_cast<std::int64_t
>(gc[d]);
775 appendBytes(
buf, &
v,
sizeof(
v));
780 static void parseRequests(
const std::vector<char>&
buf,
Fn&&
fn) {
782 const std::size_t
item = (Dim + 1) *
sizeof(std::int64_t);
783 while (off +
item <=
buf.size()) {
784 std::array<Coord, Dim> gc{};
785 for (
int d = 0; d < Dim; ++d) {
787 std::memcpy(&
v,
buf.data() + off,
sizeof(
v));
789 gc[d] =
static_cast<Coord>(
v);
793 off +=
sizeof(
reqId);
799 static void appendLeaf(std::vector<char>&
buf,
Code gc, std::uint8_t level,
800 const std::vector<std::vector<double>>&
fields,
Index i,
int K) {
801 std::int64_t
c =
static_cast<std::int64_t
>(gc);
802 appendBytes(
buf, &
c,
sizeof(
c));
803 std::int32_t
l =
static_cast<std::int32_t
>(level);
804 appendBytes(
buf, &
l,
sizeof(
l));
805 for (
int k = 0; k <
K; ++k) {
806 double v =
fields[
static_cast<std::size_t
>(k)][
static_cast<std::size_t
>(
i)];
807 appendBytes(
buf, &
v,
sizeof(
v));
811 static void parseLeaves(
const std::vector<char>&
buf,
int K,
Fn&&
fn) {
813 const std::size_t
item =
814 sizeof(std::int64_t) +
sizeof(std::int32_t) +
static_cast<std::size_t
>(
K) *
sizeof(
double);
815 std::vector<double>
comps(
static_cast<std::size_t
>(
K));
816 while (off +
item <=
buf.size()) {
818 std::memcpy(&
c,
buf.data() + off,
sizeof(
c));
821 std::memcpy(&
l,
buf.data() + off,
sizeof(
l));
823 for (
int k = 0; k <
K; ++k) {
824 std::memcpy(&
comps[
static_cast<std::size_t
>(k)],
buf.data() + off,
sizeof(
double));
827 fn(
static_cast<Code>(
c),
static_cast<std::uint8_t
>(
l),
comps.data());
830 static void appendReply(std::vector<char>&
buf, std::int64_t
reqId,
double v) {
832 appendBytes(
buf, &
v,
sizeof(
v));
835 static void parseReplies(
const std::vector<char>&
buf,
Fn&&
fn) {
837 const std::size_t
item =
sizeof(std::int64_t) +
sizeof(
double);
838 while (off +
item <=
buf.size()) {
841 off +=
sizeof(
reqId);
843 std::memcpy(&
v,
buf.data() + off,
sizeof(
v));
852 decomp::BlockDecomposer<Dim> dec_;
861 std::array<bool, Dim> periodic_{};
morton::Morton< Dim, Bits > M
void init(IVec< Dim > brick, unsigned lmax, IVec< Dim > globalOrigin=IVec< Dim >{})
Index balance2to1()
Enforce 2:1 (graded) balance within this block: no two face-adjacent leaves differ by more than one l...
void assign(IVec< Dim > brick, unsigned lmax, IVec< Dim > globalOrigin, std::vector< Code > codes, std::vector< std::uint8_t > levels)
Replace the entire leaf set directly (used by load-balancing migration, which rebuilds a block from l...
typename M::coord_type Coord
Index refineIf(Pred &&pred)
Split every leaf for which pred(code, level) is true (and level > 0) into its 2^Dim children one leve...
unsigned level(Index i) const
Index find(Code p) const
Leaf containing Morton code p, or -1. Host wrapper over amrLocate.
typename M::code_type Code
std::array< std::array< Coord, Dim >, 2 > bounds(Index i) const
Inclusive integer bounds [lo, hi] of leaf i in fine units.
const Octree & local() const
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 AmrGeometry< Dim > & globalGeometry() const
BlockOctree< Dim, Bits > Octree
typename Octree::Code Code
const IVec< Dim > & blockOriginRoot() const
static constexpr double kNoNeighbor
gather sentinel for "no neighbour"
IVec< Dim > globalRootOf(Index i) const
Global root-cell coordinate of local leaf i (lmax==0: the leaf is one root cell).
FaceInfo faceAcross(Index i, int axis, int dir) const
AmrGeometry< Dim > localGeometry() const
World geometry of this rank's block (the global geometry shifted to its origin).
FaceGatherPlan buildFaceGatherPlan() const
Index rebalance(std::vector< std::vector< double > > &fields)
Re-decompose by per-root-cell octree-leaf count and migrate leaves (with their field columns) to the ...
Index findGlobalRoot(const IVec< Dim > &g) const
Local leaf covering global root-cell g, or -1 if not owned by this rank (lmax==0).
const IVec< Dim > & globalFineSize() const
std::vector< double > coverValues(const std::vector< std::array< Coord, Dim > > &coords, const std::vector< double > &field, double sentinel=kNoNeighbor) const
For each global fine coord, the covering leaf's field value on its owner (sentinel if none).
std::vector< double > faceNeighborGather(const FaceGatherPlan &plan, const std::vector< double > &field, double sentinel=kNoNeighbor) const
Face-neighbour gather using a precomputed FaceGatherPlan: only the field values move,...
const IVec< Dim > & globalRootSize() const
DistributedOctree()=default
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...
void init(IVec< Dim > globalRootSize, unsigned lmax, AmrGeometry< Dim > globalGeo, std::array< bool, Dim > periodic, MPI_Comm comm=MPI_COMM_WORLD)
Decompose a globalRootSize grid of root cells over the communicator and build this rank's local block...
std::vector< double > faceNeighborGather(const std::vector< double > &field, double sentinel=kNoNeighbor) const
For each local leaf and each of the 2*Dim faces, the neighbouring leaf's field value.
Code globalCode(Index i) const
Global Morton code of local leaf i (origin in global fine coordinates).
Index balance()
Bring the whole distributed octree to a 2:1-balanced state.
const IVec< Dim > & blockFineOrigin() const
const IVec< Dim > & blockBrick() const
typename Octree::Coord Coord
int MPI_Comm_size(MPI_Comm, int *s)
int MPI_Allreduce(const void *sbuf, void *rbuf, int count, MPI_Datatype dt, MPI_Op, MPI_Comm)
int MPI_Comm_rank(MPI_Comm, int *r)
Kokkos::View< T *, MemSpace > View
1D device array.
double Real
Default host floating type. Device kernels may use float; conversions happen at the boundary.
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
Static topology of the face-neighbour gather: which out-slots are filled from a local leaf (directSlo...
std::vector< std::array< Coord, Dim > > remoteCoords
owner-gathered coords (incl. owner==rank)
std::vector< Index > directLeaf
out[directSlot[k]] = field[directLeaf[k]]
Index nFaces
local leaves * 2*Dim
std::vector< Index > directSlot
std::vector< Index > remoteSlot
out[remoteSlot[k]] = covered value
Classification of leaf i's face on (axis,dir): {state, global fine probe gc, owner,...
std::array< Coord, Dim > gc
Flattened, value-only topology for the face-neighbour gather, established ONCE: the per-matvec exchan...
std::vector< int > recvCounts
std::vector< Index > localSlot
std::vector< int > sendRanks
std::vector< Index > localLeaf
std::vector< Index > recvSlot
std::vector< int > sendCounts
std::vector< int > recvRanks
std::vector< Index > sendLeaf