30#ifndef PECLET_CORE_AMR_BLOCK_OCTREE_HPP
31#define PECLET_CORE_AMR_BLOCK_OCTREE_HPP
33#ifdef PECLET_CORE_HAVE_MORTON
40#include "morton/morton.hpp"
53 Index n,
typename M::code_type p) {
67 if (M::from_code(p).
ancestor(levels[idx]).code() == codes[idx])
77template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
80 using M = morton::Morton<Dim, Bits>;
81 using Code =
typename M::code_type;
82 using Coord =
typename M::coord_type;
83 static constexpr unsigned octants = M::octants;
105 std::array<Coord, Dim>
o{};
106 for (
int i = 0;
i < Dim; ++
i)
107 o[
i] =
static_cast<Coord>(
r[
i]) * rootSpan;
108 codes_.push_back(M::encode(
o).
code());
109 levels_.push_back(
static_cast<std::uint8_t
>(lmax_));
120 std::vector<std::uint8_t>
levels) {
124 codes_ = std::move(
codes);
125 levels_ = std::move(
levels);
131 unsigned lmax()
const {
return lmax_; }
136 unsigned level(
Index i)
const {
return levels_[
static_cast<std::size_t
>(
i)]; }
137 const std::vector<Code>&
codes()
const {
return codes_; }
138 const std::vector<std::uint8_t>&
levels()
const {
return levels_; }
149 std::array<std::array<Coord, Dim>, 2> b{};
150 auto o = M::from_code(codes_[
static_cast<std::size_t
>(
i)]).decode();
152 for (
int d = 0; d < Dim; ++d) {
163 M probe = M::from_code(codes_[
static_cast<std::size_t
>(
i)]);
166 if (!
probe.try_add(
static_cast<unsigned>(axis), step))
169 if (!
probe.try_sub(
static_cast<unsigned>(axis), 1))
182 template <
class Pred>
184 std::vector<Code> nc;
185 std::vector<std::uint8_t>
nl;
186 nc.reserve(codes_.size());
187 nl.reserve(levels_.size());
189 for (std::size_t k = 0; k < codes_.size(); ++k) {
190 const unsigned L = levels_[k];
191 const Code c = codes_[k];
196 nl.push_back(
static_cast<std::uint8_t
>(
L - 1));
201 nl.push_back(
static_cast<std::uint8_t
>(
L));
211 const Code target = codes_[
static_cast<std::size_t
>(
i)];
218 template <
class Pred>
220 std::vector<Code> nc;
221 std::vector<std::uint8_t>
nl;
222 nc.reserve(codes_.size());
223 nl.reserve(levels_.size());
226 while (k < codes_.size()) {
227 const unsigned L = levels_[k];
229 if (
L < lmax_ && k +
octants <= codes_.size()) {
230 const Code parent = M::from_code(codes_[k]).ancestor(
L + 1).code();
234 M::from_code(codes_[k +
oct]).ancestor(
L + 1).code() ==
parent &&
235 M::from_code(
parent).child(
L + 1,
oct).code() == codes_[k +
oct];
239 nl.push_back(
static_cast<std::uint8_t
>(
L + 1));
246 nc.push_back(codes_[k]);
247 nl.push_back(
static_cast<std::uint8_t
>(
L));
265 for (std::size_t k = 0; k < codes_.size(); ++k) {
266 const unsigned Lf = levels_[k];
267 for (
int axis = 0; axis < Dim; ++axis) {
268 for (
int dir = -1; dir <= 1; dir += 2) {
270 if (
nb >= 0 && levels_[
static_cast<std::size_t
>(
nb)] >=
Lf + 2)
271 toRefine.push_back(codes_[
static_cast<std::size_t
>(
nb)]);
289 for (std::size_t k = 0; k < codes_.size(); ++k) {
290 const unsigned Lf = levels_[k];
291 for (
int axis = 0; axis < Dim; ++axis)
292 for (
int dir = -1; dir <= 1; dir += 2) {
295 unsigned Ln = levels_[
static_cast<std::size_t
>(
nb)];
307 std::vector<Index>
ord(codes_.size());
308 for (std::size_t
i = 0;
i <
ord.size(); ++
i)
310 std::sort(
ord.begin(),
ord.end(), [&](
Index a,
Index b) { return codes_[a] < codes_[b]; });
311 std::vector<Code> nc(codes_.size());
312 std::vector<std::uint8_t>
nl(levels_.size());
313 for (std::size_t
i = 0;
i <
ord.size(); ++
i) {
314 nc[
i] = codes_[
static_cast<std::size_t
>(
ord[
i])];
315 nl[
i] = levels_[
static_cast<std::size_t
>(
ord[
i])];
324 std::vector<Code> codes_;
325 std::vector<std::uint8_t> levels_;
Per-block adaptive octree over block-local Morton codes.
const std::vector< Code > & codes() const
const IVec< Dim > & brick() const
Index coarsenIf(Pred &&pred)
Merge complete sibling groups (all 2^Dim children present, all at the same level) whose parent satisf...
morton::Morton< Dim, Bits > M
void init(IVec< Dim > brick, unsigned lmax, IVec< Dim > globalOrigin=IVec< Dim >{})
bool refineLeaf(Index i)
Refine a single leaf by index. Returns true if it was split.
Index balance2to1()
Enforce 2:1 (graded) balance within this block: no two face-adjacent leaves differ by more than one l...
Index find(const std::array< Coord, Dim > &fine) const
Leaf containing fine-unit coordinates, or -1.
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
BlockOctree(IVec< Dim > brick, unsigned lmax, IVec< Dim > globalOrigin=IVec< Dim >{})
Build a uniform block: a brick (in root cells) of leaves at level lmax.
bool isBalanced() const
True iff the leaves form a valid 2:1-balanced partition: every face neighbour differs by at most one ...
const std::vector< std::uint8_t > & levels() const
Index find(Code p) const
Leaf containing Morton code p, or -1. Host wrapper over amrLocate.
static constexpr unsigned bits()
static constexpr unsigned octants
const IVec< Dim > & globalOrigin() const
Index faceNeighbor(Index i, int axis, int dir) const
The leaf across leaf i's face on axis in direction dir (±1), or -1 if it lies outside the block.
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.
MORTON_HD Index amrLocate(const typename M::code_type *codes, const std::uint8_t *levels, Index n, typename M::code_type p)
Locate the leaf containing Morton code p in sorted leaf arrays.
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).