peclet-amr 0.2.0
Block-local-Morton AMR octree + collocated cut-cell Navier–Stokes solver (header-only C++20, Kokkos + MPI)
Loading...
Searching...
No Matches
refine.hpp
Go to the documentation of this file.
1// core — refinement criteria for a BlockOctree.
2//
3// Topology-mutation helpers that drive BlockOctree::refineIf from a geometric
4// criterion. The headline one is SDF-driven: refine leaves the solid surface
5// passes through (plus a band) down to a target level, leaving the interior /
6// far field coarse — the usual AMR pattern for the suite's cut-cell IBM. Reuses
7// the shared peclet::core::geom SDF (any callable returning a signed distance at a world
8// point) and the AmrGeometry world mapping.
9//
10#ifndef PECLET_AMR_REFINE_HPP
11#define PECLET_AMR_REFINE_HPP
12
13#include <algorithm>
14#include <cmath>
15#include <utility>
16#include <vector>
17
19#include "peclet/amr/common.hpp"
21#include "peclet/core/common/types.hpp"
22
23namespace peclet::amr {
24
33template <int Dim, unsigned Bits, class SdfFn>
35 unsigned targetLevel, Real band = 1.0, bool balance = true) {
36 using Code = typename BlockOctree<Dim, Bits>::Code;
37 Index total = 0;
38 for (;;) {
39 std::vector<Code> toRefine;
40 for (Index i = 0; i < t.numLeaves(); ++i) {
41 const unsigned L = t.level(i);
42 if (L <= targetLevel)
43 continue;
44 auto b = t.bounds(i);
45 Vec<Dim> c = geo.center(b);
46 // Phase 3 (`docs/amr_anisotropic.md` §6): a BOX leaf's half-diagonal is
47 // 0.5*sqrt(sum_d w_d^2) and the band margin is a length, so it takes the COARSEST axis —
48 // the conservative reading, which refines no less than the cubic rule did. Both reduce to
49 // `0.5*sqrt(Dim)*w` and `band*h0` exactly when the three spacings are equal.
50 const Vec<Dim> w = geo.leafSize(L);
51 Real halfDiag2 = 0.0;
52 for (int d = 0; d < Dim; ++d)
53 halfDiag2 += w[d] * w[d];
54 const Real halfDiag = 0.5 * std::sqrt(halfDiag2);
55 if (std::fabs(static_cast<Real>(sdf(c))) <= halfDiag + band * geo.hMax())
56 toRefine.push_back(t.code(i));
57 }
58 if (toRefine.empty())
59 break;
60 std::sort(toRefine.begin(), toRefine.end());
61 toRefine.erase(std::unique(toRefine.begin(), toRefine.end()), toRefine.end());
62 total += t.refineIf(
63 [&](Code c, unsigned) { return std::binary_search(toRefine.begin(), toRefine.end(), c); });
64 }
65 if (balance)
66 total += t.balance2to1();
67 return total;
68}
69
84template <int Dim, unsigned Bits, class SdfFn, class TargetFn>
86 TargetFn&& targetFn, Real band = 2.0, bool balance = true) {
87 using Code = typename BlockOctree<Dim, Bits>::Code;
88 const Real halfDiagFactor = 0.5 * std::sqrt(static_cast<Real>(Dim));
89 Index total = 0;
90 for (;;) {
91 std::vector<Code> toRefine;
92 for (Index i = 0; i < t.numLeaves(); ++i) {
93 const unsigned L = t.level(i);
94 if (L == 0)
95 continue;
96 auto b = t.bounds(i);
97 Vec<Dim> c = geo.center(b);
98 // Phase 3: the half-diagonal of a BOX leaf, and a band measured in cells of the level being
99 // created — the coarsest axis's width, the conservative reading (§6 of the design note).
100 const Vec<Dim> w = geo.leafSize(L);
101 Real halfDiag2 = 0.0;
102 Real wMax = w[0];
103 for (int d = 0; d < Dim; ++d) {
104 halfDiag2 += w[d] * w[d];
105 wMax = w[d] > wMax ? w[d] : wMax;
106 }
107 const Real halfDiag = 0.5 * std::sqrt(halfDiag2);
108 // Band predicate first: targetFn is the expensive one (a gap/medial-axis field, often a
109 // Python callable) and only band cells can ever be refined.
110 if (std::fabs(static_cast<Real>(sdf(c))) > halfDiag + band * 0.5 * wMax)
111 continue;
112 if (L > targetFn(c))
113 toRefine.push_back(t.code(i));
114 }
115 if (toRefine.empty())
116 break;
117 std::sort(toRefine.begin(), toRefine.end());
118 toRefine.erase(std::unique(toRefine.begin(), toRefine.end()), toRefine.end());
119 total += t.refineIf(
120 [&](Code c, unsigned) { return std::binary_search(toRefine.begin(), toRefine.end(), c); });
121 }
122 if (balance)
123 total += t.balance2to1();
124 return total;
125}
126
137template <int Dim, class GapFn>
140auto gapFloorTarget(GapFn&& gapFn, Real h0, unsigned coarsestLevel, Real n = 4.0) {
141 return [gapFn = std::forward<GapFn>(gapFn), h0, coarsestLevel, n](const Vec<Dim>& p) -> unsigned {
142 const Real g = static_cast<Real>(gapFn(p));
143 unsigned L = 0;
144 while (L < coarsestLevel && h0 * static_cast<Real>(Index(1) << (L + 1)) * n <= g)
145 ++L;
146 return L;
147 };
148}
149
150} // namespace peclet::amr
151
152#endif // PECLET_AMR_REFINE_HPP
Per-block adaptive octree over block-local Morton codes.
typename M::code_type Code
Index refineToSdf(BlockOctree< Dim, Bits > &t, const AmrGeometry< Dim > &geo, SdfFn &&sdf, unsigned targetLevel, Real band=1.0, bool balance=true)
Refine leaves near an SDF interface down to targetLevel.
Definition refine.hpp:34
Index refineToSdfGraded(BlockOctree< Dim, Bits > &t, const AmrGeometry< Dim > &geo, SdfFn &&sdf, TargetFn &&targetFn, Real band=2.0, bool balance=true)
Graded variant of refineToSdf: a per-point TARGET LEVEL function instead of one global targetLevel — ...
Definition refine.hpp:85
auto gapFloorTarget(GapFn &&gapFn, Real h0, unsigned coarsestLevel, Real n=4.0)
The plan's §7 criterion 1 as a ready-made targetFn for refineToSdfGraded: the GAP-WIDTH FLOOR.
Definition refine.hpp:140
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:51
World-space placement of a block-local octree: fine coordinate (0,..,0) sits at origin,...