core 0.5.0
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
73template <int Dim, unsigned Bits>
74std::vector<std::array<double, Dim>> transferGradients(const DistributedOctree<Dim, Bits>& d,
75 const std::vector<double>& f) {
77 using BO = typename DO::Octree;
78 using Coord = typename BO::Coord;
79 using M = typename BO::M;
80 const BO& t = d.local();
81 const Index n = t.numLeaves();
82 const int S = 2 * Dim;
83
84 struct Side {
85 double val = 0.0, sj = 0.0;
86 bool ok = false;
87 };
88 std::vector<Side> sides(static_cast<std::size_t>(n) * S);
89 std::vector<std::array<Coord, Dim>> rq;
90 std::vector<std::size_t> rslot;
91 for (Index i = 0; i < n; ++i) {
92 auto b = t.bounds(i);
93 const auto& lo = b[0];
94 const long si = 1L << t.level(i);
95 for (int axis = 0; axis < Dim; ++axis)
96 for (int dd = 0; dd < 2; ++dd) { // dd 0 = +1 side, 1 = −1 side
97 const std::size_t slot = static_cast<std::size_t>(i) * S + axis * 2 + dd;
98 const long pc = (dd == 0) ? static_cast<long>(lo[axis]) + si
99 : static_cast<long>(lo[axis]) - 1;
100 if (pc >= 0 &&
101 pc < static_cast<long>(d.blockBrick()[axis]) * static_cast<long>(d.rootSpan())) {
102 std::array<Coord, Dim> p = lo;
103 p[axis] = static_cast<Coord>(pc);
104 const Index j = t.find(M::encode(p).code());
105 if (j >= 0) {
106 sides[slot].val = f[static_cast<std::size_t>(j)];
107 sides[slot].sj = static_cast<double>(1L << t.level(j));
108 sides[slot].ok = true;
109 }
110 } else {
111 // Block-crossing: complete from the owner IF the probe stays inside the domain —
112 // a DOMAIN-crossing probe is missing (the single-rank faceNeighbor convention;
113 // transferField never wraps periodically).
114 std::array<long, Dim> g{};
115 bool inDomain = true;
116 for (int a = 0; a < Dim; ++a)
117 g[a] = static_cast<long>(lo[a]) + d.blockFineOrigin()[a];
118 g[axis] = pc + d.blockFineOrigin()[axis];
119 for (int a = 0; a < Dim; ++a)
120 if (g[a] < 0 || g[a] >= static_cast<long>(d.globalFineSize()[a])) {
121 inDomain = false;
122 break;
123 }
124 if (inDomain) {
125 std::array<Coord, Dim> gc{};
126 for (int a = 0; a < Dim; ++a)
127 gc[a] = static_cast<Coord>(g[a]);
128 rslot.push_back(slot);
129 rq.push_back(gc);
130 }
131 }
132 }
133 }
134 const std::vector<double> vals = d.coverValues(rq, f);
135 const std::vector<int> lvls = d.coverLevels(rq);
136 for (std::size_t k = 0; k < rq.size(); ++k)
137 if (lvls[k] >= 0) {
138 sides[rslot[k]].val = vals[k];
139 sides[rslot[k]].sj = static_cast<double>(1L << lvls[k]);
140 sides[rslot[k]].ok = true;
141 }
142
143 std::vector<std::array<double, Dim>> grad(static_cast<std::size_t>(n),
144 std::array<double, Dim>{});
145 for (Index i = 0; i < n; ++i) {
146 const double si = static_cast<double>(1L << t.level(i));
147 const double ui = f[static_cast<std::size_t>(i)];
148 for (int axis = 0; axis < Dim; ++axis) {
149 const Side& P = sides[static_cast<std::size_t>(i) * S + axis * 2 + 0];
150 const Side& Mi = sides[static_cast<std::size_t>(i) * S + axis * 2 + 1];
151 if (!P.ok || !Mi.ok)
152 continue; // == the jp<0 || jm<0 skip
153 const double sp = (P.val - ui) / (0.5 * (si + P.sj));
154 const double sm = (ui - Mi.val) / (0.5 * (si + Mi.sj));
155 grad[static_cast<std::size_t>(i)][axis] = detail::minmod(sp, sm);
156 }
157 }
158 return grad;
159}
160
164template <int Dim, unsigned Bits>
165std::vector<double> distributedAdapt(DistributedOctree<Dim, Bits>& d, const std::vector<double>& f,
166 double refineThresh, double coarsenThresh,
167 unsigned finestLevel = 0, double eps = 0.01,
168 bool linear = true) {
169 using BO = typename DistributedOctree<Dim, Bits>::Octree;
170 using Code = typename BO::Code;
171 using M = typename BO::M;
172
175
176 // Halo-completed prolongation gradients (BEFORE the mutation, while d still holds the old
177 // mesh): the block-local stencil zeroes the gradient at interior block boundaries, which
178 // would make the remap np-dependent there.
179 std::vector<std::array<double, Dim>> grads;
180 if (linear)
182
183 const BO oldLocal = d.local(); // snapshot for the remap + flag lookup
184 // coarsen sibling groups whose every child is flagged kCoarsen
185 d.local().coarsenIf([&](Code parent, unsigned pl) {
186 for (unsigned oct = 0; oct < (1u << Dim); ++oct) {
187 Code cc = M::from_code(parent).child(pl, oct).code();
188 Index ci = oldLocal.find(cc);
189 if (ci < 0 || flags[static_cast<std::size_t>(ci)] != kCoarsen)
190 return false;
191 }
192 return true;
193 });
194 // refine leaves flagged kRefine
195 d.local().refineIf([&](Code c, unsigned) {
196 Index ci = oldLocal.find(c);
197 return ci >= 0 && flags[static_cast<std::size_t>(ci)] == kRefine;
198 });
199 d.balance(); // cross-block 2:1 to a global fixpoint
200 return transferField(oldLocal, f, d.local(), linear, linear ? &grads : nullptr);
201}
202
203} // namespace peclet::core::amr
204
205#endif // PECLET_CORE_HAVE_MORTON
206#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...
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< 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...
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.
const IVec< Dim > & blockFineOrigin() const
const IVec< Dim > & blockBrick() const
double minmod(double a, double b)
Definition adapt.hpp:40
std::vector< int > flagByIndicator(const BlockOctree< Dim, Bits > &t, const std::vector< double > &ind, double refineThresh, double coarsenThresh, unsigned finestLevel=0)
Definition adapt.hpp:204
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< 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,...
Definition adapt.hpp:53
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.
std::vector< double > transferField(const BlockOctree< Dim, Bits > &oldT, const std::vector< double > &oldF, const BlockOctree< Dim, Bits > &newT, bool linear=true, const std::type_identity_t< std::vector< std::array< double, Dim > > > *gradIn=nullptr)
Conservative remap of a leaf field from oldT to newT (same domain).
Definition adapt.hpp:93
std::vector< std::array< double, Dim > > transferGradients(const DistributedOctree< Dim, Bits > &d, const std::vector< double > &f)
transferField's minmod prolongation gradients on a DistributedOctree: bit-identical to the block-loca...
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
Definition types.hpp:15