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
mg_predict.hpp
Go to the documentation of this file.
1// amr — what pressure-multigrid ladder a given grid, tree depth and rank count will produce.
2//
3// The prediction tool of docs/amr_mg_depth.md §6.7. It is a PURE HOST FUNCTION (no MPI, no Kokkos,
4// no octree): it re-runs the §6.2 ladder rule on the ORB the distributed octree would build
5// (peclet::core::decomp::BlockDecomposer on the global ROOT grid), so a test can assert the
6// hierarchy the solver built against the hierarchy the design says it should build, instead of
7// against a literal that goes stale.
8//
9// The rule, once:
10// * levels above the root brick are the octree's own coarsenings — `lmax + 1` of them for a mesh
11// refined to level 0 (the case §6.2's table describes; see the `lmax` note on the function);
12// * below the root the ladder LIFTS while the global grid halves into a cube level AND every
13// rank's block stays even in origin and size (§6.2); the lift is lockstep, so one rank's odd
14// block stops the ladder for all;
15// * when the coarsest in-place grid is still larger than `bottomExtent` on some axis and there
16// is more than one rank, the tail gathers it and continues redundantly with the single-rank
17// ladder (§6.5);
18// * the bottom is exact damped Jacobi while the final extent is <= `bottomExtent`, and the
19// agglomerated GraphAMG-PCG solve otherwise (§6.6).
20//
21// The prediction was written before the tail (WO4) and the GraphAMG bottom (WO5) were built, as
22// the ladder of the finished design; both have since landed and match it, which is what makes it
23// the specification the built ladder is checked against (`amr_mg_predict`, `amr_mg_tail`,
24// `amr_mg_bottom{,_dist}`). It predicts the default bottom selection, `auto`, on the ORB a
25// freshly initialised DistributedOctree builds — not a weighted or rebalanced partition.
26#ifndef PECLET_AMR_MG_PREDICT_HPP
27#define PECLET_AMR_MG_PREDICT_HPP
28
29#include <algorithm>
30#include <cstddef>
31#include <string>
32#include <vector>
33
34#include "peclet/amr/common.hpp"
35#include "peclet/core/decomp/block_decomposer.hpp"
36#include "peclet/core/decomp/stage_target.hpp"
37
38namespace peclet::amr {
39
49 bool enabled = true;
52 int minExtent = 4;
58};
59
63template <int Dim>
64bool amrStageLiftable(const core::decomp::BlockDecomposer<Dim>& dec) {
65 const IVec<Dim>& G = dec.globalSize();
66 for (int a = 0; a < Dim; ++a)
67 if ((G[a] % 2) != 0 || (G[a] / 2) < 2)
68 return false;
69 for (std::size_t b = 0; b < dec.numBlocks(); ++b)
70 for (int a = 0; a < Dim; ++a)
71 if ((dec.origins()[b][a] % 2) != 0 || (dec.sizes()[b][a] % 2) != 0)
72 return false;
73 return true;
74}
75
77enum class MgLevelKind {
78 Octree,
79 Lifted,
80 Tail,
81 Sibling,
83};
84
86enum class MgBottomKind {
87 Jacobi,
88 Amg
89};
90
91inline const char* toString(MgLevelKind k) {
92 switch (k) {
94 return "octree";
96 return "lifted";
98 return "tail";
100 return "sibling";
102 return "repartition";
103 }
104 return "?";
105}
106inline const char* toString(MgBottomKind k) {
107 return k == MgBottomKind::Jacobi ? "jacobi" : "amg";
108}
109
111template <int Dim>
117
121template <int Dim>
126 std::vector<MgLevelPrediction<Dim>> levels;
128 std::vector<MgLevelKind> stages;
129 bool tail = false;
132
134 bool hasStage() const { return !stages.empty(); }
137 std::size_t numInPlace() const {
138 std::size_t k = 0;
139 while (k < levels.size() &&
140 (levels[k].kind == MgLevelKind::Octree || levels[k].kind == MgLevelKind::Lifted))
141 ++k;
142 return k;
143 }
147 std::string bottomName() const {
148 std::string s = toString(bottom);
149 for (auto it = stages.rbegin(); it != stages.rend(); ++it)
150 s += std::string("+") + toString(*it);
151 return s;
152 }
153};
154
155namespace detail {
156
157template <int Dim>
159 Index mx = 0;
160 for (int d = 0; d < Dim; ++d)
161 mx = std::max(mx, G[d]);
162 return mx;
163}
164
167template <int Dim>
168bool gridCanHalve(const IVec<Dim>& G, Index bottomExtent) {
169 if (maxExtent<Dim>(G) <= bottomExtent)
170 return false;
171 for (int d = 0; d < Dim; ++d)
172 if ((G[d] % 2) != 0 || (G[d] / 2) < 2)
173 return false;
174 return true;
175}
176
177template <int Dim>
178long long cellsOf(const IVec<Dim>& G) {
179 long long c = 1;
180 for (int d = 0; d < Dim; ++d)
181 c *= static_cast<long long>(G[d]);
182 return c;
183}
184
185template <int Dim>
188 lv.extent = G;
189 lv.cells = cellsOf<Dim>(G);
190 lv.kind = kind;
191 p.levels.push_back(lv);
192}
193
194} // namespace detail
195
213template <int Dim>
215 Index bottomExtent = 4,
216 const PressureStagePolicy& policy = {}) {
218 if (numRanks < 1)
219 numRanks = 1;
220
221 // --- the octree levels: the finest grid coarsened down to the root brick.
222 for (unsigned j = 0; j <= lmax; ++j) {
223 IVec<Dim> e{};
224 for (int d = 0; d < Dim; ++d)
225 e[d] = G[d] << (lmax - j);
226 detail::pushLevel<Dim>(p, e, MgLevelKind::Octree);
227 }
228
229 // --- the in-place lifted levels: lockstep while the grid halves and every block stays even —
230 // then, where they block above `bottomExtent`, a STAGE, whose continued ladder lifts again on
231 // its own decomposition and may stage again (the same loop, on the target decomposition).
232 namespace cd = core::decomp;
233 cd::BlockDecomposer<Dim> dec(static_cast<std::size_t>(numRanks), G);
234 Index maxBlockCells = policy.maxBlockCells;
235 if (maxBlockCells < 0) { // the leaf count of the largest finest block, uniform mesh (§9.6)
236 maxBlockCells = static_cast<Index>(cd::largestBlockCells(dec));
237 for (unsigned j = 0; j < lmax; ++j)
238 maxBlockCells <<= Dim;
239 }
240 IVec<Dim> cur = G;
242 for (;;) {
243 while (detail::gridCanHalve<Dim>(cur, bottomExtent) && amrStageLiftable<Dim>(dec)) {
244 IVec<Dim> two{};
245 for (int d = 0; d < Dim; ++d)
246 two[d] = 2;
247 dec = dec.coarsened(two);
248 cur = dec.globalSize();
249 detail::pushLevel<Dim>(p, cur, liftKind);
250 }
251 if (dec.numBlocks() <= 1 || detail::maxExtent<Dim>(cur) <= bottomExtent)
252 break; // one rank: its bottom solves the level whole; or the Jacobi bottom is exact here
253 if (!p.hasStage())
254 p.tailFrom = cur;
255 if (policy.enabled) {
256 const cd::StageTarget<Dim> t = cd::chooseStageTarget(
257 dec, cur, [](const cd::BlockDecomposer<Dim>& c) { return amrStageLiftable<Dim>(c); },
258 policy.minExtent, maxBlockCells);
259 if (t.kind == cd::StageKind::SiblingMerge || t.kind == cd::StageKind::Repartition) {
260 liftKind =
261 t.kind == cd::StageKind::SiblingMerge ? MgLevelKind::Sibling : MgLevelKind::Repartition;
262 p.stages.push_back(liftKind);
263 detail::pushLevel<Dim>(p, cur, liftKind); // the continued ladder's level 0: moved
264 dec = t.dec;
265 continue;
266 }
267 }
268 // --- the redundant tail: the gathered grid, continued on one rank.
269 p.tail = true;
270 p.stages.push_back(MgLevelKind::Tail);
271 detail::pushLevel<Dim>(p, cur,
272 MgLevelKind::Tail); // the tail's own level 0 == the gathered grid
273 while (detail::gridCanHalve<Dim>(cur, bottomExtent)) {
274 for (int d = 0; d < Dim; ++d)
275 cur[d] /= 2;
276 detail::pushLevel<Dim>(p, cur, MgLevelKind::Tail);
277 }
278 break;
279 }
280
281 p.bottom = detail::maxExtent<Dim>(cur) > bottomExtent ? MgBottomKind::Amg : MgBottomKind::Jacobi;
282 return p;
283}
284
285} // namespace peclet::amr
286
287#endif // PECLET_AMR_MG_PREDICT_HPP
void pushLevel(MgLadderPrediction< Dim > &p, const IVec< Dim > &G, MgLevelKind kind)
long long cellsOf(const IVec< Dim > &G)
bool gridCanHalve(const IVec< Dim > &G, Index bottomExtent)
The grid half of the §6.2 rule: stop when the bottom smoother is already exact, and when the grid can...
Index maxExtent(const IVec< Dim > &G)
bool amrStageLiftable(const core::decomp::BlockDecomposer< Dim > &dec)
amr's lift rule (docs/amr_mg_depth.md §6.2) as core's stage predicate: the level grid halves into a c...
const char * toString(MgLevelKind k)
MgBottomKind
What solves the coarsest level.
@ Jacobi
damped Jacobi is exact there (max extent <= bottomExtent)
@ Amg
the agglomerated GraphAMG-PCG bottom
MgLevelKind
Where a predicted level comes from.
@ Octree
a coarsening of the octree itself (at or above the root brick)
@ Repartition
the continued ladder of a repartition stage (a fresh ORB on np_L ranks)
@ Sibling
the continued ladder of a sibling-merge stage (fewer ranks, agglomerated(d))
@ Lifted
below the root brick, in place: the ORB blocks nest, transfers stay local
@ Tail
below the in-place ladder: the gathered grid, solved redundantly on every rank
MgLadderPrediction< Dim > predictPressureLadder(IVec< Dim > G, unsigned lmax, int numRanks, Index bottomExtent=4, const PressureStagePolicy &policy={})
Predict the pressure-multigrid ladder for a global ROOT grid G (in root cells, i.e.
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
The whole predicted ladder: what predictPressureLadder returns and what peclet.amr....
std::vector< MgLevelKind > stages
The stages met on the way down, outermost first (Tail, Sibling, Repartition).
bool tail
a replicated tail engages (at any depth)
MgBottomKind bottom
what solves the coarsest level under auto
std::vector< MgLevelPrediction< Dim > > levels
Finest first; each stage's levels appended after the ladder it continues.
bool hasStage() const
Whether any stage fires — compare against DistributedFlowMultigrid::hasStage().
std::size_t numInPlace() const
The levels ABOVE the first stage (kinds Octree and Lifted); compare against DistributedFlowMultigrid:...
IVec< Dim > tailFrom
the grid the (first) stage moves
std::string bottomName() const
"jacobi" / "amg", suffixed by every stage innermost first (§6.7's spelling: "+tail",...
One predicted level of the pressure ladder (docs/amr_mg_depth.md §6.7).
MgLevelKind kind
where the level comes from
long long cells
product of extent — the global cell count of a UNIFORM mesh
IVec< Dim > extent
global grid extent in THIS level's cell units
Which stage the distributed pressure ladder uses where its in-place lift blocks (docs/amr_mg_depth....
bool enabled
false: the replicated tail only (WO4)
Index maxBlockCells
Core's bound on a target block's cells.
int minExtent
Core's economic trigger / extent cap.