core
Shared MPI block decomposition + asynchronous ghost-layer exchange (header-only C++20)
Loading...
Searching...
No Matches
distributed_adapt.hpp
Go to the documentation of this file.
1// core — distributed dynamic (solution-adaptive) AMR on a DistributedOctree.
2//
3// The distributed counterpart of adapt.hpp, keeping the existing ORB block ownership.
4// (Adaptation can leave the per-rank leaf counts uneven; DistributedOctree::rebalance()
5// re-decomposes on leaf-count weights and migrates leaves+fields to restore balance.)
6// It composes pieces that are already validated:
7// * the Löhner indicator is evaluated from the owner-based face-neighbour halo
8// (DistributedOctree::faceNeighborGather), so each rank sees the same neighbour
9// values a whole-domain solve would — the flags are identical to the serial ones;
10// * refine / coarsen run on each rank's *local* octree (sibling groups never cross
11// root cells, so coarsening is purely local), then DistributedOctree::balance()
12// restores global 2:1 across blocks;
13// * the field is remapped locally with transferField (refine/coarsen/balance only
14// change a block's internal structure), so it is conservative per block and needs
15// no field communication.
16// Because flags come from the (deterministic) halo gather, balance() is deterministic,
17// and the remap is per-cell, the adapted mesh + field are bit-identical across rank
18// counts (COMM_WORLD == COMM_SELF).
19//
20// Header-only, guarded by PECLET_CORE_HAVE_MORTON.
21#ifndef PECLET_CORE_AMR_DISTRIBUTED_ADAPT_HPP
22#define PECLET_CORE_AMR_DISTRIBUTED_ADAPT_HPP
23
24#ifdef PECLET_CORE_HAVE_MORTON
25
26#include <cmath>
27#include <vector>
28
32
33namespace peclet::core::amr {
34
37template <int Dim, unsigned Bits>
39 const std::vector<double>& u, double eps = 0.01) {
40 const auto g = d.faceNeighborGather(u); // out[i*F + 2*axis + (dir>0?0:1)]
42 const Index n = d.local().numLeaves();
43 const int F = 2 * Dim;
44 std::vector<double> e(static_cast<std::size_t>(n), 0.0);
45 for (Index i = 0; i < n; ++i) {
46 const double ui = u[static_cast<std::size_t>(i)];
47 double num2 = 0.0, den2 = 0.0;
48 for (int axis = 0; axis < Dim; ++axis) {
49 const double up = g[static_cast<std::size_t>(i) * F + 2 * axis + 0];
50 const double um = g[static_cast<std::size_t>(i) * F + 2 * axis + 1];
51 if (up == sentinel || um == sentinel)
52 continue;
53 const double d2 = up - 2.0 * ui + um;
54 const double nrm = std::fabs(up - ui) + std::fabs(ui - um) +
55 eps * (std::fabs(up) + 2.0 * std::fabs(ui) + std::fabs(um));
56 num2 += d2 * d2;
57 den2 += nrm * nrm;
58 }
59 e[static_cast<std::size_t>(i)] = (den2 > 0.0) ? std::sqrt(num2 / den2) : 0.0;
60 }
61 return e;
62}
63
67template <int Dim, unsigned Bits>
68std::vector<double> distributedAdapt(DistributedOctree<Dim, Bits>& d, const std::vector<double>& f,
69 double refineThresh, double coarsenThresh,
70 unsigned finestLevel = 0, double eps = 0.01,
71 bool linear = true) {
72 using BO = typename DistributedOctree<Dim, Bits>::Octree;
73 using Code = typename BO::Code;
74 using M = typename BO::M;
75
78
79 const BO oldLocal = d.local(); // snapshot for the remap + flag lookup
80 // coarsen sibling groups whose every child is flagged kCoarsen
81 d.local().coarsenIf([&](Code parent, unsigned pl) {
82 for (unsigned oct = 0; oct < (1u << Dim); ++oct) {
83 Code cc = M::from_code(parent).child(pl, oct).code();
84 Index ci = oldLocal.find(cc);
85 if (ci < 0 || flags[static_cast<std::size_t>(ci)] != kCoarsen)
86 return false;
87 }
88 return true;
89 });
90 // refine leaves flagged kRefine
91 d.local().refineIf([&](Code c, unsigned) {
92 Index ci = oldLocal.find(c);
93 return ci >= 0 && flags[static_cast<std::size_t>(ci)] == kRefine;
94 });
95 d.balance(); // cross-block 2:1 to a global fixpoint
96 return transferField(oldLocal, f, d.local(), linear);
97}
98
99} // namespace peclet::core::amr
100
101#endif // PECLET_CORE_HAVE_MORTON
102#endif // PECLET_CORE_AMR_DISTRIBUTED_ADAPT_HPP
Index coarsenIf(Pred &&pred)
Merge complete sibling groups (all 2^Dim children present, all at the same level) whose parent satisf...
Index refineIf(Pred &&pred)
Split every leaf for which pred(code, level) is true (and level > 0) into its 2^Dim children one leve...
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.
Index balance()
Bring the whole distributed octree to a 2:1-balanced state.
std::vector< int > flagByIndicator(const BlockOctree< Dim, Bits > &t, const std::vector< double > &ind, double refineThresh, double coarsenThresh, unsigned finestLevel=0)
Definition adapt.hpp:172
std::vector< double > lohnerIndicatorDistributed(const DistributedOctree< Dim, Bits > &d, const std::vector< double > &u, double eps=0.01)
Löhner indicator per local leaf, using the owner-based face-neighbour halo so cross-block neighbours ...
std::vector< double > transferField(const BlockOctree< Dim, Bits > &oldT, const std::vector< double > &oldF, const BlockOctree< Dim, Bits > &newT, bool linear=true)
Conservative remap of a leaf field from oldT to newT (same domain).
Definition adapt.hpp:50
std::vector< double > distributedAdapt(DistributedOctree< Dim, Bits > &d, const std::vector< double > &f, double refineThresh, double coarsenThresh, unsigned finestLevel=0, double eps=0.01, bool linear=true)
One distributed solution-adaptive step.
Kokkos::View< T *, MemSpace > View
1D device array.
Definition view.hpp:26
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
Definition types.hpp:15