include/pops/coupling/amr/amr_coupler_mp.hpp Source FileΒΆ

adc_cpp: include/pops/coupling/amr/amr_coupler_mp.hpp Source File
adc_cpp 0.3.0
Model-free C++23 core for coupled hyperbolic-elliptic systems on adaptive (AMR) meshes, with MPI and GPU (Kokkos) backends
amr_coupler_mp.hpp
Go to the documentation of this file.
1#pragma once
2
5#include <pops/coupling/amr/amr_diagnostics.hpp> // amr_mass, amr_max_drift_speed
6#include <pops/coupling/amr/amr_level_storage.hpp> // AmrLevelStack
7#include <pops/coupling/amr/amr_regrid_coupler.hpp> // amr_regrid_finest (Berger-Rigoutsos)
8#include <pops/coupling/single/coupler.hpp> // detail::coupler_eval_rhs (f = model.elliptic_rhs(U))
9#include <pops/numerics/elliptic/mg/composite_fac_poisson.hpp> // COMPOSITE FAC 2-level Poisson solver (opt-in)
12#include <pops/numerics/time/amr/reflux/amr_reflux_mf.hpp> // AmrLevelMP, amr_step_multilevel_multipatch, mf_*_mb
21#include <pops/mesh/layout/refinement.hpp> // coarsen_index
22#include <pops/parallel/comm.hpp> // all_reduce_sum / all_reduce_max (distributed mass/drift)
23
24#include <algorithm> // std::max
25#include <cmath> // std::hypot
26#include <cstddef> // std::size_t
27#include <functional> // std::function (conducting-wall predicate passed to the MG)
28#include <map> // named_aux_: model-named aux fields (comp -> coarse field), re-applied by compute_aux
29#include <stdexcept> // std::runtime_error (density size guard)
30#include <utility> // std::pair, std::move
31#include <vector>
32
45
46namespace pops {
47
48namespace detail {
49// Coupling primitive (not policy): piecewise-constant injection of aux from
50// multi-box parent -> multi-box child (valid + ghosts). DISTRIBUTED, same scheme as
51// mf_fill_fine_ghosts_mb. Two parent cases:
52// - REPLICATED (level 0, replicated_parent=true): parent fully local on each rank,
53// direct read via mf_find_box. This is the replicated coarse (per-rank dmap): parallel_copy
54// would violate the replicated-metadata assumption. Bit-identical path to the historical one.
55// - DISTRIBUTED (intermediate, replicated_parent=false): the parent may be on another rank,
56// we bring its valid regions onto a LOCAL child-coarsen grid via parallel_copy, then
57// inject. A child cell outside the parent coverage (GLOBAL box_array) is left
58// intact, like the replicated path (which skipped it via mf_find_box < 0).
59// In serial both paths are identical (parent local everywhere, parallel_copy = memory copy).
60inline void coupler_inject_aux_mb(const MultiFab& parent, MultiFab& child,
61 bool replicated_parent = true) {
62 const int nc = child.ncomp();
63 if (replicated_parent) {
65 for (int lc = 0; lc < child.local_size(); ++lc) {
66 Array4 c = child.fab(lc).array();
67 const Box2D g = child.fab(lc).grown_box();
68 for (int j = g.lo[1]; j <= g.hi[1]; ++j)
69 for (int i = g.lo[0]; i <= g.hi[0]; ++i) {
70 const int ci = coarsen_index(i, kAmrRefRatio), cj = coarsen_index(j, kAmrRefRatio);
71 const int pb = mf_find_box(parent, ci, cj);
72 if (pb < 0)
73 continue;
74 const ConstArray4 pp = parent.fab(pb).const_array();
75 for (int k = 0; k < nc; ++k)
76 c(i, j, k) = pp(ci, cj, k);
77 }
78 }
79 return;
80 }
81 const BoxArray& pba = parent.box_array(); // GLOBAL: rank-independent coverage
82 auto covered = [&](int ci, int cj) {
83 for (int b = 0; b < pba.size(); ++b)
84 if (pba[b].contains(ci, cj))
85 return true;
86 return false;
87 };
88 const BoxArray ccoarse = coarsen_grown(child.box_array(), child.n_grow(), kAmrRefRatio);
89 MultiFab Pc(ccoarse, child.dmap(), parent.ncomp(), 0);
90 parallel_copy(Pc, parent); // parent regions (from any rank) -> local grid
92 for (int lc = 0; lc < child.local_size(); ++lc) {
93 Array4 c = child.fab(lc).array();
94 const ConstArray4 pp = Pc.fab(lc).const_array();
95 const Box2D g = child.fab(lc).grown_box();
96 for (int j = g.lo[1]; j <= g.hi[1]; ++j)
97 for (int i = g.lo[0]; i <= g.hi[0]; ++i) {
98 const int ci = coarsen_index(i, kAmrRefRatio), cj = coarsen_index(j, kAmrRefRatio);
99 if (!covered(ci, cj))
100 continue; // outside coverage -> keep the child value
101 for (int k = 0; k < nc; ++k)
102 c(i, j, k) = pp(ci, cj, k);
103 }
104 }
105}
106// Writes an initial density (component 0, n*n row-major in GLOBAL indices) on the coarse
107// level, MULTI-BOX and DISTRIBUTION-AWARE: each rank touches only its LOCAL fabs and reads
108// rho at the cell GLOBAL index (i,j). For Euler (ncomp 4) it also sets zero momentum
109// + thermal energy r/(gamma-1); ncomp 1 touches only density. Replicated mono-box:
110// a single fab covering the domain, global == local indices -> bit-identical to the historical
111// direct write. Distributed multi-box: each local box reads its window of rho.
112inline void coupler_write_coarse(MultiFab& U, const std::vector<double>& rho, int n, int ncomp,
113 double gamma) {
114 if (static_cast<int>(rho.size()) != n * n)
115 throw std::runtime_error("AMR coupler: initial density of size != n*n");
116 const Real gm1 = Real(gamma) - Real(1);
117 device_fence();
118 for (int li = 0; li < U.local_size(); ++li) {
119 Array4 u = U.fab(li).array();
120 const Box2D v = U.box(li);
121 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
122 for (int i = v.lo[0]; i <= v.hi[0]; ++i) {
123 const Real r = rho[static_cast<std::size_t>(j) * n + i];
124 u(i, j, 0) = r;
125 if (ncomp >= 3) {
126 u(i, j, 1) = 0;
127 u(i, j, 2) = 0;
128 }
129 if (ncomp == 4)
130 u(i, j, 3) = r / gm1;
131 }
132 }
133}
134
135// Writes the FULL INITIAL CONSERVATIVE STATE (all components) on the coarse level from a
136// flat component-major field @p state (c*n*n + j*n + i), of size ncomp*n*n. Counterpart of
137// coupler_write_coarse for the multi-component seed: same box traversal (replicated mono-box
138// AND distributed multi-box, GLOBAL indices (i,j)), only the per-cell write differs -- here we copy
139// the ncomp components positionally (no density/momentum/energy wiring; the caller already provides
140// the conservative, e.g. [rho, rho*u, rho*v]). gamma omitted (no energy derived). Index computed
141// in std::size_t (no int overflow at large n, unlike the int validation of
142// coupler_write_coarse). Used for the drift seed (set_conservative_state).
143inline void coupler_write_coarse_state(MultiFab& U, const std::vector<double>& state, int n,
144 int ncomp) {
145 const std::size_t nn = static_cast<std::size_t>(n) * static_cast<std::size_t>(n);
146 if (state.size() != nn * static_cast<std::size_t>(ncomp))
147 throw std::runtime_error(
148 "AMR coupler: initial state of size != ncomp*n*n (full conservative "
149 "state; ncomp == model n_vars)");
150 device_fence();
151 for (int li = 0; li < U.local_size(); ++li) {
152 Array4 u = U.fab(li).array();
153 const Box2D v = U.box(li);
154 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
155 for (int i = v.lo[0]; i <= v.hi[0]; ++i)
156 for (int c = 0; c < ncomp; ++c)
157 u(i, j, c) = state[static_cast<std::size_t>(c) * nn +
158 static_cast<std::size_t>(j) * static_cast<std::size_t>(n) +
159 static_cast<std::size_t>(i)];
160 }
161}
162
163// Reads the coarse density (component 0) into a GLOBAL n*n row-major field, MULTI-BOX and
164// DISTRIBUTION-AWARE. Each rank writes its local cells into an n*n buffer initialized to 0
165// then, if distributed, all_reduce_sum_inplace recomposes the full field on ALL ranks (the
166// boxes are disjoint -> the cross-rank sum reconstructs the field exactly). Replicated mono-box:
167// a single fab covers everything, the buffer is already complete, all_reduce would be the identity
168// -> we avoid it (bit-identical to the historical direct read fab(0)).
169inline std::vector<double> coupler_read_coarse(const MultiFab& U, int n, bool replicated) {
170 device_fence();
171 std::vector<double> out(static_cast<std::size_t>(n) * n, 0.0);
172 for (int li = 0; li < U.local_size(); ++li) {
173 const ConstArray4 u = U.fab(li).const_array();
174 const Box2D v = U.box(li);
175 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
176 for (int i = v.lo[0]; i <= v.hi[0]; ++i)
177 out[static_cast<std::size_t>(j) * n + i] = u(i, j, 0);
178 }
179 if (!replicated)
180 all_reduce_sum_inplace(out.data(), static_cast<int>(out.size()));
181 return out;
182}
183
184// Reads the coarse-level potential phi (component 0 of aux(0), written by compute_aux after the
185// Poisson solve) into a GLOBAL n*n row-major field, MULTI-BOX and DISTRIBUTION-AWARE. aux(0) shares
186// EXACTLY the layout of the coarse U (same BoxArray + DistributionMapping, cf. amr_level_storage:
187// aux_[0] is built on U.box_array()/U.dmap()), so the recomposition is identical to
188// coupler_read_coarse: local n*n buffer, all_reduce_sum if distributed (disjoint boxes -> exact
189// sum), avoided in replicated mono-box (field already complete). PRECONDITION: update()/compute_aux
190// has run at least once (otherwise aux(0) is 0). Strict counterpart of coupler_read_coarse for phi.
191inline std::vector<double> coupler_read_coarse_phi(const MultiFab& aux0, int n, bool replicated) {
192 device_fence();
193 std::vector<double> out(static_cast<std::size_t>(n) * n, 0.0);
194 for (int li = 0; li < aux0.local_size(); ++li) {
195 const ConstArray4 a = aux0.fab(li).const_array();
196 const Box2D v = aux0.box(li);
197 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
198 for (int i = v.lo[0]; i <= v.hi[0]; ++i)
199 out[static_cast<std::size_t>(j) * n + i] = a(i, j, 0);
200 }
201 if (!replicated)
202 all_reduce_sum_inplace(out.data(), static_cast<int>(out.size()));
203 return out;
204}
205
206// Injects the coarse into the valid cells of a fine patch (piecewise constant, ratio 2),
207// MULTI-BOX and DISTRIBUTION-AWARE. Makes the hierarchy consistent before the first sync_down (the
208// seed patch is at 0). Replicated mono-box: coarse fully local, direct read via
209// mf_find_box (always found); no collective -> bit-identical to the historical fab(0).
210// Distributed multi-box: we bring the needed coarse regions onto a LOCAL child-coarsen grid
211// via parallel_copy (same scheme as coupler_inject_aux_mb), then inject.
212inline void coupler_inject_coarse_to_fine_mb(const MultiFab& Uc, MultiFab& Uf, bool replicated) {
213 const int nc = Uf.ncomp();
214 if (replicated) {
215 device_fence();
216 for (int li = 0; li < Uf.local_size(); ++li) {
217 Array4 f = Uf.fab(li).array();
218 const Box2D v = Uf.box(li);
219 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
220 for (int i = v.lo[0]; i <= v.hi[0]; ++i) {
221 const int ci = coarsen_index(i, kAmrRefRatio), cj = coarsen_index(j, kAmrRefRatio);
222 const int pb = mf_find_box(Uc, ci, cj);
223 if (pb < 0)
224 continue;
225 const ConstArray4 c = Uc.fab(pb).const_array();
226 for (int k = 0; k < nc; ++k)
227 f(i, j, k) = c(ci, cj, k);
228 }
229 }
230 return;
231 }
232 const BoxArray ccoarse = coarsen(Uf.box_array(), kAmrRefRatio); // coarse footprint (valid cells)
233 MultiFab Pc(ccoarse, Uf.dmap(), Uc.ncomp(), 0);
234 parallel_copy(Pc, Uc); // coarse regions (from any rank) -> local grid
235 device_fence();
236 for (int li = 0; li < Uf.local_size(); ++li) {
237 Array4 f = Uf.fab(li).array();
238 const ConstArray4 c = Pc.fab(li).const_array();
239 const Box2D v = Uf.box(li);
240 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
241 for (int i = v.lo[0]; i <= v.hi[0]; ++i) {
242 const int ci = coarsen_index(i, kAmrRefRatio), cj = coarsen_index(j, kAmrRefRatio);
243 for (int k = 0; k < nc; ++k)
244 f(i, j, k) = c(ci, cj, k);
245 }
246 }
247}
248
249// Builds the coarse level (BoxArray + DistributionMapping) of the AmrSystem path according to the
250// ownership policy, in a SINGLE point for both build paths (native + compiled):
251// - replicated (distribute=false, DEFAULT): mono-box covering the domain, dmap = my_rank() everywhere
252// (the box lives on each rank). In serial my_rank()=0 -> identical to round-robin, bit for bit.
253// This is the layout GeometricMG(replicated=true) and the historical one expect.
254// - distributed (distribute=true): multi-box BoxArray::from_domain(dom, max_grid) spread round-robin
255// DistributionMapping(ba.size(), n_ranks()). Each rank carries only its tiles -> the coarse
256// Poisson and coarse transport distribute (strong-scaling). max_grid<=0 => n/2 (2x2).
257inline std::pair<BoxArray, DistributionMapping> coupler_make_coarse_layout(int n, bool distribute,
258 int max_grid) {
259 const Box2D dom = Box2D::from_extents(n, n);
260 if (!distribute) {
261 BoxArray ba(std::vector<Box2D>{dom});
262 return {ba, DistributionMapping(std::vector<int>{my_rank()})};
263 }
264 const int mg = (max_grid > 0) ? max_grid : (n / 2 > 0 ? n / 2 : n);
265 BoxArray ba = BoxArray::from_domain(dom, mg);
266 return {ba, DistributionMapping(ba.size(), n_ranks())};
267}
268
269} // namespace detail
270
275template <class Model, class Elliptic = GeometricMG>
277 static_assert(EllipticSolver<Elliptic>, "Elliptic must model EllipticSolver");
278
279 public:
280 // active: optional "active cell" predicate (interior of the conductor), for the circular
281 // conducting wall of the column instability (passed as-is to the multigrid). Empty
282 // by default -> no wall (historical behavior unchanged). Only the coarse carries the
283 // wall: the fine patches refine the ring edge, strictly inside the wall.
284 // replicated_coarse: level-0 (coarse) OWNERSHIP POLICY. BOTH modes are
285 // stable and their equivalence is proven bit for bit (test_mpi_decoarse, maxdiff=0):
286 // true (performant DEFAULT): coarse mono-box REPLICATED on all ranks. Best coarse
287 // MG solve (no multigrid degeneration), zero communication for the
288 // coarse Poisson, robust reference -> the right default for small/medium cases.
289 // false (EXPLICIT scalable mode): coarse multi-box DISTRIBUTED round-robin. Lifts the
290 // O(NX*NY*nranks) memory lock of level 0, required at very large scale. But the
291 // geometric MG degenerates for a finely-split coarse (>2x2 boxes do not tile the
292 // coarsest grid): reserve for cases where the level-0 memory is the lock.
293 // Criterion: set false ONLY when memory scalability requires it; otherwise keep true.
294 // Removing the replicated path is DEFERRED as long as the distributed one is not strictly
295 // superior. mg_ receives the same flag (otherwise, under replicated MPI, the coarse would fall on
296 // the single rank 0 and compute_aux would read a phi absent elsewhere). In serial, both coincide.
297 AmrCouplerMP(const Model& model, const Geometry& geom, const BoxArray& ba_coarse, const BCRec& bc,
298 std::vector<AmrLevelMP> levels, std::function<bool(Real, Real)> active = {},
299 bool replicated_coarse = true)
300 : model_(model),
301 geom_(geom),
302 mg_(geom, ba_coarse, bc, std::move(active), replicated_coarse),
303 stack_(geom.domain, std::move(levels), aux_comps<Model>()),
304 replicated_coarse_(replicated_coarse) {}
305
306 std::vector<AmrLevelMP>& levels() { return stack_.levels(); }
307 MultiFab& coarse() { return stack_.coarse(); }
308 const MultiFab& coarse() const { return stack_.coarse(); }
309 // coarse-level aux: (phi, dphi/dx, dphi/dy), component 0 = phi (cf. compute_aux). Same
310 // layout as coarse(). Read by the AmrSystem potential hook (coupler_read_coarse_phi).
311 MultiFab& aux0() { return stack_.aux(0); }
312 const MultiFab& aux0() const { return stack_.aux(0); }
313
319 void set_named_aux(int comp, std::vector<Real> field) {
320 named_aux_[comp] = std::move(field);
321 apply_named_aux(); // stack_ exists at ctor: reflect onto the coarse aux right away
322 }
327 void set_named_aux_bc(int comp, AuxHaloPolicy policy) { named_aux_bc_[comp] = policy; }
328 const Box2D& domain() const { return stack_.domain(); }
329 int nlev() const { return stack_.nlev(); }
330
331 // ----------------------------------------------------------------------------------------------
332 // SINGLE-RANK AMR CHECKPOINT / RESTART (ADC-65). The mono-block coupler carries the FULL
333 // CONSERVATIVE STATE per level (all components) + the phi (multigrid warm-start), and can IMPOSE
334 // a SAVED fine hierarchy (instead of Berger-Rigoutsos clustering on tags). SINGLE-RANK: the
335 // accessors loop over local_size() + device_fence(), WITHOUT MPI gather (the facade rejects np>1
336 // AND multi-block upstream; multi-rank/multi-block restart is a documented follow-up).
337 // ----------------------------------------------------------------------------------------------
338
339 // Reads the FULL conservative state (all components) of level @p k into a flat
340 // component-major field c*nf*nf + j*nf + i, nf = n << k (n = coarse side). The cells OUTSIDE
341 // patches (uncovered fine level) stay at 0: a fine level is only defined within its patches
342 // (at restart we rewrite ONLY the patch cells, cf. set_level_state).
343 std::vector<double> level_state(int k) {
344 std::vector<AmrLevelMP>& L = stack_.L();
345 if (k < 0 || k >= static_cast<int>(L.size()))
346 throw std::runtime_error("AmrCouplerMP::level_state: level out of bounds");
347 MultiFab& U = L[k].U;
348 const int nc = U.ncomp();
349 const std::size_t nf = static_cast<std::size_t>(stack_.domain().nx()) << k;
350 std::vector<double> out(static_cast<std::size_t>(nc) * nf * nf, 0.0);
351 device_fence();
352 for (int li = 0; li < U.local_size(); ++li) {
353 const ConstArray4 u = U.fab(li).const_array();
354 const Box2D v = U.box(li);
355 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
356 for (int i = v.lo[0]; i <= v.hi[0]; ++i)
357 for (int c = 0; c < nc; ++c)
358 out[static_cast<std::size_t>(c) * nf * nf + static_cast<std::size_t>(j) * nf +
359 static_cast<std::size_t>(i)] = u(i, j, c);
360 }
361 return out;
362 }
363
364 // Restores the full conservative state of level @p k from @p s (same layout as level_state).
365 // Writes ONLY the VALID cells of the local fabs (the patches): the ghosts are redone at the
366 // next update()/advance (exactly like after a regrid), and a fine cell outside a patch
367 // does not exist. NO RE-PROLONGATION: the state is restored AS-IS (no coarse->fine injection).
368 void set_level_state(int k, const std::vector<double>& s) {
369 std::vector<AmrLevelMP>& L = stack_.L();
370 if (k < 0 || k >= static_cast<int>(L.size()))
371 throw std::runtime_error("AmrCouplerMP::set_level_state: level out of bounds");
372 MultiFab& U = L[k].U;
373 const int nc = U.ncomp();
374 const std::size_t nf = static_cast<std::size_t>(stack_.domain().nx()) << k;
375 if (s.size() != static_cast<std::size_t>(nc) * nf * nf)
376 throw std::runtime_error("AmrCouplerMP::set_level_state: state size != ncomp*nf*nf");
377 device_fence();
378 for (int li = 0; li < U.local_size(); ++li) {
379 Array4 u = U.fab(li).array();
380 const Box2D v = U.box(li);
381 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
382 for (int i = v.lo[0]; i <= v.hi[0]; ++i)
383 for (int c = 0; c < nc; ++c)
384 u(i, j, c) = s[static_cast<std::size_t>(c) * nf * nf +
385 static_cast<std::size_t>(j) * nf + static_cast<std::size_t>(i)];
386 }
387 }
388
389 // Reads the potential phi of level @p k, flat nf*nf row-major field (nf = n << k), zeros outside patches.
390 // Level 0: the multigrid WARM-START -- mg_.phi() (VALID cells), the state actually
391 // reused by the NEXT solve (GeometricMG::solve keeps phi between calls). Level >= 1:
392 // aux(k) component 0 (informational; recomputed at update). It is mg_.phi() level 0 that makes the
393 // restart BIT-IDENTICAL (the 1st post-restart solve starts from the same guess as the continuous run).
394 std::vector<double> level_potential(int k) {
395 if (k < 0 || k >= stack_.nlev())
396 throw std::runtime_error("AmrCouplerMP::level_potential: level out of bounds");
397 const std::size_t nf = static_cast<std::size_t>(stack_.domain().nx()) << k;
398 std::vector<double> out(nf * nf, 0.0);
399 device_fence();
400 const MultiFab& P = (k == 0) ? mg_.phi() : stack_.aux(k);
401 for (int li = 0; li < P.local_size(); ++li) {
402 const ConstArray4 p = P.fab(li).const_array();
403 const Box2D v = P.box(li);
404 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
405 for (int i = v.lo[0]; i <= v.hi[0]; ++i)
406 out[static_cast<std::size_t>(j) * nf + static_cast<std::size_t>(i)] = p(i, j, 0);
407 }
408 return out;
409 }
410
411 // Restores the potential of level @p k. Level 0: warm-start mg_.phi() (valid cells) -> the
412 // multigrid restart is BIT-IDENTICAL (the 1st post-restart solve starts from the same guess). Level
413 // >= 1: aux(k) comp 0 (recomputed at update; idempotent restore, no effect on the dynamics).
414 void set_level_potential(int k, const std::vector<double>& p) {
415 if (k < 0 || k >= stack_.nlev())
416 throw std::runtime_error("AmrCouplerMP::set_level_potential: level out of bounds");
417 const std::size_t nf = static_cast<std::size_t>(stack_.domain().nx()) << k;
418 if (p.size() != nf * nf)
419 throw std::runtime_error("AmrCouplerMP::set_level_potential: phi size != nf*nf");
420 device_fence();
421 MultiFab& P = (k == 0) ? mg_.phi() : stack_.aux(k);
422 for (int li = 0; li < P.local_size(); ++li) {
423 Array4 q = P.fab(li).array();
424 const Box2D v = P.box(li);
425 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
426 for (int i = v.lo[0]; i <= v.hi[0]; ++i)
427 q(i, j, 0) = p[static_cast<std::size_t>(j) * nf + static_cast<std::size_t>(i)];
428 }
429 }
430
431 // GLOBAL (np>1 GATHER) variants of level_state / level_potential (ADC-509). The base accessors
432 // above fill the GLOBAL buffer from the LOCAL fabs only (zeros where this rank owns no box); these
433 // add an all_reduce_sum_inplace so EACH rank holds the complete field (AMR reflux pattern, comm.hpp;
434 // MIRROR of System::state_global / gather_global). COLLECTIVE: all ranks MUST call them. Mono-rank
435 // the reduce is the identity -> bit-identical to level_state / level_potential. The base distributed
436 // coarse + round-robin fine patches are disjoint, so the sum double-counts nothing.
437 std::vector<double> level_state_global(int k) {
438 std::vector<double> out = level_state(k);
439 all_reduce_sum_inplace(out.data(), static_cast<int>(out.size()));
440 return out;
441 }
442 std::vector<double> level_potential_global(int k) {
443 std::vector<double> out = level_potential(k);
444 all_reduce_sum_inplace(out.data(), static_cast<int>(out.size()));
445 return out;
446 }
447
448 // Imposes the fine-level hierarchy (restart): rebuilds level 1 on the SAVED @p
449 // fine_boxes BoxArray (instead of Berger-Rigoutsos clustering on tags), via the SAME mechanism as
450 // regrid (regrid_field_on_layout: parent interp + fine carry-over), then reattaches the level-1 aux.
451 // The rebuilt valid content is OVERWRITTEN afterwards by set_level_state (restore as-is): here we
452 // rely only on the IMPOSED LAYOUT. SINGLE-RANK, 2-level mono-block hierarchy (so we impose
453 // ONLY level 1). Clear rejection if the hierarchy has no fine level or if no box was saved.
454 void set_hierarchy(const std::vector<Box2D>& fine_boxes) {
455 std::vector<AmrLevelMP>& L = stack_.L();
456 if (L.size() < 2)
457 throw std::runtime_error(
458 "AmrCouplerMP::set_hierarchy: mono-level hierarchy (no fine patch "
459 "to impose)");
460 if (fine_boxes.empty())
461 throw std::runtime_error(
462 "AmrCouplerMP::set_hierarchy: no saved fine box (restart of a "
463 "fine-patch hierarchy required)");
464 const int ngf = L[1].U.n_grow(); // inherit the ghost width of the current fine (scheme parity)
465 BoxArray fb(fine_boxes);
466 DistributionMapping dmap(static_cast<int>(fb.size()),
467 n_ranks()); // single-rank -> all on rank 0
468 L[1].U = regrid_field_on_layout(fb, dmap, L[0].U, L[1].U, /*pk=*/0, ngf, replicated_coarse_);
469 stack_.reattach_aux(1); // realloc aux[1] on the new layout + rewire L[1].aux
470 }
471
472 void sync_down() { // average fine -> coarse over the whole hierarchy (multi-box)
473 auto& L = stack_.L();
474 for (int k = stack_.nlev() - 1; k >= 1; --k)
475 mf_average_down_mb(L[k].U, L[k - 1].U);
476 }
477
482 void set_composite_poisson(bool v) { composite_poisson_ = v; }
483 bool composite_poisson() const { return composite_poisson_; }
484
485 void compute_aux() { // coarse Poisson + grad phi + injection to the fine levels
486 auto& L = stack_.L();
487 const Box2D& dom = stack_.domain();
488 const Real dx = geom_.dx(), dy = geom_.dy();
489 // COMPOSITE path (opt-in): the fine patch TRULY refines the elliptic. Supported scope = 2 levels,
490 // ONE mono-box fine patch, replicated coarse (Phase 2). Otherwise Option A below (bit-identical).
491 if (composite_poisson_ && replicated_coarse_ && stack_.nlev() == 2 &&
492 L[1].U.box_array().size() == 1) {
493 compute_aux_composite();
494 return;
495 }
496 // right-hand side via the model (no copied formula): f = elliptic_rhs(U)
497 detail::coupler_eval_rhs(L[0].U, mg_.rhs(), model_);
498 mg_.solve(); // leaves phi with its ghosts filled (last gs_rb_sweep -> fill_ghosts)
499 device_fence();
500 // aux = (phi, grad phi) per LOCAL coarse fab: covers the replicated mono-box (1 fab) as well as
501 // the distributed multi-box (de-replication). The box-edge derivatives read the ghosts of
502 // phi filled by the solve (distributed inter-box exchange via fill_boundary). mg_.phi() and
503 // aux(0) share the same layout (same BoxArray + DistributionMapping) -> fab(li) <-> box(li).
504 for (int li = 0; li < mg_.phi().local_size(); ++li) {
505 const ConstArray4 p = mg_.phi().fab(li).const_array();
506 Array4 a = stack_.aux(0).fab(li).array();
507 const Box2D b = stack_.aux(0).box(li);
508 for (int j = b.lo[1]; j <= b.hi[1]; ++j)
509 for (int i = b.lo[0]; i <= b.hi[0]; ++i) {
510 a(i, j, 0) = p(i, j);
511 a(i, j, 1) = (p(i + 1, j) - p(i - 1, j)) / (2 * dx);
512 a(i, j, 2) = (p(i, j + 1) - p(i, j - 1)) / (2 * dy);
513 }
514 }
515 // model-NAMED aux (ADC-291): re-apply the static named fields onto the coarse valid cells BEFORE
516 // fill_boundary (ghosts) and the injection (lines below), so they reach every level and survive a
517 // regrid. The loop above wrote only comps 0..2 (phi/grad), so this never clobbers them. No-op
518 // without a named field (bit-identical).
519 apply_named_aux();
520 fill_boundary(stack_.aux(0), dom, Periodicity{true, true});
521 apply_named_aux_bc(); // ADC-369: per-field halo override on the coarse physical ghosts (after the
522 // shared fill); no-op on a periodic domain / without a policy.
523 // parent aux(k-1) replicated only if level 0 is: otherwise it is DISTRIBUTED (multi-box)
524 // and the injection goes through parallel_copy. Beyond level 1, the parent is always distributed.
525 for (int k = 1; k < stack_.nlev(); ++k)
526 detail::coupler_inject_aux_mb(stack_.aux(k - 1), stack_.aux(k),
527 /*replicated_parent=*/(k == 1) && replicated_coarse_);
528 }
529
532 void update() {
533 sync_down();
534 compute_aux();
535 }
536
537 // Selectable spatial discretization (default FirstOrder = NoSlope + Rusanov,
538 // strictly identical to the old step()). recon_prim selects the primitive
539 // reconstruction (same parameter as assemble_rhs / System); false (default) -> conservative.
540 // imex: treats the stiff source IMPLICITLY (backward_euler) rather than forward Euler;
541 // false (default) -> historical explicit treatment, bit-identical. The source being
542 // cell-local (outside reflux registers), the implicit split preserves conservation.
555 template <class Disc = FirstOrder>
556 void step(Real dt, bool recon_prim = false, bool imex = false, const NewtonOptions& nopts = {},
557 AmrTimeMethod tmethod = AmrTimeMethod::kEuler, Real pos_floor = Real(0)) {
558 update();
559 advance_amr<typename Disc::Limiter, typename Disc::NumericalFlux>(
560 model_, stack_.L(), stack_.domain(), dt, Periodicity{true, true}, replicated_coarse_,
561 recon_prim, imex, nopts, tmethod, pos_floor);
562 }
563
572 template <class Disc = FirstOrder>
573 void advance_transport(Real dt, bool recon_prim = false, Real pos_floor = Real(0)) {
574 advance_amr<typename Disc::Limiter, typename Disc::NumericalFlux>(
575 model_, stack_.L(), stack_.domain(), dt, Periodicity{true, true}, replicated_coarse_,
576 recon_prim, /*imex=*/false, NewtonOptions{}, AmrTimeMethod::kEuler, pos_floor);
577 }
578
579 // Regrid of the FINE level by Berger-Rigoutsos (delegated to amr_regrid_finest):
580 // rebuilds the patches (carry over fine data, otherwise parent interp) + the aux.
581 // margin = nesting. The coupler only orders the call.
582 template <class Crit>
583 void regrid(Crit crit, int grow = 2, int margin = 2) {
584 amr_regrid_finest(stack_.L(), stack_.aux(), stack_.domain(), crit, grow, margin,
585 aux_comps<Model>(), replicated_coarse_);
586 }
587
588 // coarse mass via the shared diagnostic amr_mass_mb (replicated mono-box as well as
589 // distributed multi-box). Replicated coarse: the local sum IS already the total mass
590 // (each rank holds everything) -> no all_reduce. Distributed: local part -> all_reduce_sum.
591 Real mass() const {
592 const Real M = amr_mass_mb(stack_.coarse(), geom_.dx(), geom_.dy());
593 return replicated_coarse_ ? M : all_reduce_sum(M);
594 }
595
596 // max drift speed via amr_max_drift_speed_mb + floor. all_reduce_max correct
597 // in BOTH cases: under replication the local max is already global (idempotent);
598 // distributed, we take the max of the parts.
600 const Real v = amr_max_drift_speed_mb(stack_.aux(0), model_.B0);
601 return all_reduce_max(std::max(v, Real(1e-12)));
602 }
603
613 Real w = Real(1e-12);
614 MultiFab& U = stack_.coarse();
615 MultiFab& A = stack_.aux(0);
616 for (int li = 0; li < U.local_size(); ++li) {
617 const ConstArray4 u = U.fab(li).const_array();
618 const ConstArray4 a = A.fab(li).const_array();
619 const Box2D b = U.box(li);
620 for (int j = b.lo[1]; j <= b.hi[1]; ++j)
621 for (int i = b.lo[0]; i <= b.hi[0]; ++i) {
622 const auto us = load_state<Model>(u, i, j);
623 const Aux ax = load_aux<aux_comps<Model>()>(a, i, j);
624 w = std::max(
625 w, std::max(model_.max_wave_speed(us, ax, 0), model_.max_wave_speed(us, ax, 1)));
626 }
627 }
628 return all_reduce_max(w);
629 }
630
631 private:
635 void compute_aux_composite() {
636 // ADC-291 NOTE: unlike compute_aux (Option A), this opt-in composite-FAC path does NOT re-apply
637 // named aux onto the fine level (it derives each level's aux from the FAC phi, with no coarse->fine
638 // aux injection). The coarse named comp survives (the grad writes touch only comps 0..2), but a
639 // fine-level model reading extra_field(k) would read 0. set_composite_poisson is C++-only and not
640 // facade-reachable, so named aux cannot hit this path today; carrying named aux to the composite
641 // fine level is a documented follow-up (cf. pops.capabilities()['aux']['followups']).
642 auto& L = stack_.L();
643 const Box2D& dom = stack_.domain();
644 const Box2D fine_box = L[1].U.box_array()[0];
645 if (!fac_built_ || !same_box(fac_fine_box_, fine_box)) {
646 fac_ = std::make_shared<CompositeFacPoisson>(geom_, mg_.box_array(), mg_.bc(), fine_box, 2);
647 fac_fine_box_ = fine_box;
648 fac_built_ = true;
649 }
650 // f = elliptic_rhs(U) PER LEVEL: the fine has its OWN refined right-hand side (not an injection).
651 detail::coupler_eval_rhs(L[0].U, fac_->rhs_coarse(), model_);
652 detail::coupler_eval_rhs(L[1].U, fac_->rhs_fine(), model_);
653 fac_->solve();
654 device_fence();
655 // level-0 aux (coarse): phi + grad from phi_coarse (same centered stencils as the Option A path).
656 fill_ghosts(fac_->phi_coarse(), dom, mg_.bc());
657 detail::coupler_grad_phi(fac_->phi_coarse(), stack_.aux(0), Real(1) / (Real(2) * geom_.dx()),
658 Real(1) / (Real(2) * geom_.dy()));
659 fill_boundary(stack_.aux(0), dom, Periodicity{true, true});
660 // level-1 aux (fine): phi + grad from phi_fine -> FINE grad (fine centered diff, reads the C-F
661 // bilinear ghosts) = the fidelity gain vs the constant coarse grad injected by Option A.
662 detail::coupler_grad_phi(fac_->phi_fine(), stack_.aux(1), Real(1) / (Real(2) * L[1].dx),
663 Real(1) / (Real(2) * L[1].dy));
664 }
665
666 static bool same_box(const Box2D& a, const Box2D& b) {
667 return a.lo[0] == b.lo[0] && a.lo[1] == b.lo[1] && a.hi[0] == b.hi[0] && a.hi[1] == b.hi[1];
668 }
669
670 Model model_;
671 Geometry geom_;
672 Elliptic mg_;
673 AmrLevelStack<AmrLevelMP> stack_;
674 bool
675 replicated_coarse_; // level 0 replicated (true) or distributed multi-box (false, de-replication)
676 // COMPOSITE FAC Poisson path (opt-in, set_composite_poisson). fac_ built lazily on the
677 // current fine patch (rebuilt if the patch changes after regrid). Default OFF -> Option A bit-identical.
678 bool composite_poisson_ = false;
679 bool fac_built_ = false;
680 std::shared_ptr<CompositeFacPoisson> fac_;
681 Box2D fac_fine_box_{};
682 // Model-NAMED aux fields (ADC-291): component (>= kAuxNamedBase) -> coarse base-level field
683 // (n*n row-major). STATIC user fields re-applied by compute_aux each update (so they persist across
684 // regrid). Empty by default -> bit-identical. cf. set_named_aux / apply_named_aux.
685 std::map<int, std::vector<Real>> named_aux_;
686 // Per-field aux HALO policy (ADC-369): component -> uniform boundary policy, applied to the coarse aux
687 // after the shared fill (apply_named_aux_bc). Empty by default -> bit-identical.
688 std::map<int, AuxHaloPolicy> named_aux_bc_;
689
690 // Re-applies the model-NAMED aux fields onto the COARSE shared aux valid cells. Mirror of
691 // SystemFieldSolver::apply_named_aux_one and AmrRuntime::apply_named_aux: per local fab (MPI-safe),
692 // valid cells only, global flat index j*nx+i. compute_aux runs the coarse->fine injection right
693 // after, carrying the named comps to the fine levels. No-op without a named field.
694 void apply_named_aux() {
695 if (named_aux_.empty())
696 return;
697 const int row = stack_.domain().nx();
698 for (const auto& [comp, field] : named_aux_) {
699 if (field.empty() || comp >= stack_.aux(0).ncomp())
700 continue;
701 for (int li = 0; li < stack_.aux(0).local_size(); ++li) {
702 Array4 a = stack_.aux(0).fab(li).array();
703 const Box2D v = stack_.aux(0).box(li);
704 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
705 for (int i = v.lo[0]; i <= v.hi[0]; ++i)
706 a(i, j, comp) = field[static_cast<std::size_t>(j) * row + i];
707 }
708 }
709 }
710
711 // Per-field aux HALO override (ADC-369) on the COARSE aux, AFTER the shared fill. Overrides only each
712 // declared component's physical-face ghosts; aux_halo_override(mg_.bc(), policy) keeps periodic faces
713 // periodic (so on a periodic domain this is a no-op). Mirror of SystemFieldSolver::apply_named_aux_bc.
714 void apply_named_aux_bc() {
715 if (named_aux_bc_.empty())
716 return;
717 for (const auto& [comp, policy] : named_aux_bc_) {
718 if (comp >= stack_.aux(0).ncomp())
719 continue;
720 fill_physical_bc(stack_.aux(0), stack_.domain(), aux_halo_override(mg_.bc(), policy), comp);
721 }
722 }
723};
724
725} // namespace pops
Diagnostics extracted from the AMR couplers: mass and max drift speed (responsibility c).
AmrLevelStack: AMR hierarchy storage (levels + aux) extracted from the couplers.
Umbrella for the AMR MultiFab stack: includes the numerics/time sub-headers in dependency order (flux...
amr_regrid_finest: Berger-Rigoutsos regrid of the finest level (responsibility b).
Box2D: the integer index space of a 2D cell-centered Cartesian grid.
BoxArray: the set of boxes tiling a level (disjoint, covering).
Multi-patch E x B AMR coupler.
Definition amr_coupler_mp.hpp:276
std::vector< double > level_potential(int k)
Definition amr_coupler_mp.hpp:394
const Box2D & domain() const
Definition amr_coupler_mp.hpp:328
MultiFab & coarse()
Definition amr_coupler_mp.hpp:307
void set_level_state(int k, const std::vector< double > &s)
Definition amr_coupler_mp.hpp:368
void set_composite_poisson(bool v)
OPT-IN: replaces the Option A AMR Poisson (coarse solve + piecewise-constant gradient injection) with...
Definition amr_coupler_mp.hpp:482
void set_named_aux_bc(int comp, AuxHaloPolicy policy)
Registers a per-field aux HALO policy (ADC-369) for the named component comp.
Definition amr_coupler_mp.hpp:327
std::vector< AmrLevelMP > & levels()
Definition amr_coupler_mp.hpp:306
bool composite_poisson() const
Definition amr_coupler_mp.hpp:483
void set_hierarchy(const std::vector< Box2D > &fine_boxes)
Definition amr_coupler_mp.hpp:454
std::vector< double > level_potential_global(int k)
Definition amr_coupler_mp.hpp:442
AmrCouplerMP(const Model &model, const Geometry &geom, const BoxArray &ba_coarse, const BCRec &bc, std::vector< AmrLevelMP > levels, std::function< bool(Real, Real)> active={}, bool replicated_coarse=true)
Definition amr_coupler_mp.hpp:297
void step(Real dt, bool recon_prim=false, bool imex=false, const NewtonOptions &nopts={}, AmrTimeMethod tmethod=AmrTimeMethod::kEuler, Real pos_floor=Real(0))
Advances the hierarchy by one step dt: update() then advance_amr (Berger-Oliger subcycling + reflux +...
Definition amr_coupler_mp.hpp:556
void set_level_potential(int k, const std::vector< double > &p)
Definition amr_coupler_mp.hpp:414
int nlev() const
Definition amr_coupler_mp.hpp:329
void advance_transport(Real dt, bool recon_prim=false, Real pos_floor=Real(0))
TRANSPORT-ONLY ADVANCE (hyperbolic), WITHOUT update() or source.
Definition amr_coupler_mp.hpp:573
const MultiFab & aux0() const
Definition amr_coupler_mp.hpp:312
void compute_aux()
Definition amr_coupler_mp.hpp:485
void sync_down()
Definition amr_coupler_mp.hpp:472
Real max_drift_speed() const
Definition amr_coupler_mp.hpp:599
Real mass() const
Definition amr_coupler_mp.hpp:591
MultiFab & aux0()
Definition amr_coupler_mp.hpp:311
void set_named_aux(int comp, std::vector< Real > field)
Registers a model-NAMED aux field (ADC-291) at shared-channel component comp (>= kAuxNamedBase),...
Definition amr_coupler_mp.hpp:319
std::vector< double > level_state(int k)
Definition amr_coupler_mp.hpp:343
const MultiFab & coarse() const
Definition amr_coupler_mp.hpp:308
Real max_wave_speed()
Max wave speed on the coarse level via model.max_wave_speed.
Definition amr_coupler_mp.hpp:612
void regrid(Crit crit, int grow=2, int margin=2)
Definition amr_coupler_mp.hpp:583
void update()
Updates the hierarchy before a step: sync_down (fine -> coarse) then compute_aux (coarse Poisson + gr...
Definition amr_coupler_mp.hpp:532
std::vector< double > level_state_global(int k)
Definition amr_coupler_mp.hpp:437
Ordered list of boxes tiling a level.
Definition box_array.hpp:22
int size() const
Number of boxes in the tiling.
Definition box_array.hpp:42
static BoxArray from_domain(const Box2D &domain, int max_grid_size)
Tile the domain into tiles of at most max_grid_size per direction, distributed evenly.
Definition box_array.hpp:30
Owning MPI rank of each box, indexed by GLOBAL box index (parallel to a BoxArray).
Definition distribution_mapping.hpp:19
const Box2D & grown_box() const
Grown box (valid + ng ghosts) = actual memory footprint.
Definition fab2d.hpp:76
ConstArray4 const_array() const
READ handle (POD device-copyable) over this Fab. Valid as long as the Fab lives.
Definition fab2d.hpp:96
Array4 array()
WRITE handle (POD device-copyable) over this Fab. Valid as long as the Fab lives.
Definition fab2d.hpp:91
Field distributed over a level: decomposition (BoxArray) + distribution (DistributionMapping) + ncomp...
Definition multifab.hpp:33
Fab2D & fab(int li)
Local fab at index li (0 <= li < local_size()), for writing.
Definition multifab.hpp:67
int ncomp() const
Number of components.
Definition multifab.hpp:60
const DistributionMapping & dmap() const
GLOBAL distribution (owner rank per box).
Definition multifab.hpp:58
int n_grow() const
Number of ghost layers.
Definition multifab.hpp:62
const BoxArray & box_array() const
GLOBAL decomposition of the level (all boxes, all ranks).
Definition multifab.hpp:56
const Box2D & box(int li) const
VALID box of local fab li.
Definition multifab.hpp:71
int local_size() const
Number of fabs OWNED by this rank (bound on local indices).
Definition multifab.hpp:65
Parallel seam: minimal MPI abstraction (rank/size + collectives) with serial fallback.
CompositeFacPoisson: 2-level AMR COMPOSITE elliptic solver (Fast Adaptive Composite,...
Definition elliptic_solver.hpp:30
Coupler: single-block hyperbolic-elliptic coupler (Poisson -> aux -> advance loop).
DistributionMapping: maps each box (by global index) to its owning MPI rank.
EllipticSolver concept: common contract for elliptic solvers at the MultiFab level (solve D phi = f),...
fill_boundary: INTRA-level halo exchange (fills ghosts from neighbors).
for_each_cell and reductions: the parallelism SEAM over the cells of a Box2D; sync_host / sync_device...
GeometricMG: in-house geometric multigrid (V-cycle) for the elliptic operator, Gauss-Seidel smoother ...
Geometry: index-space (Box2D) <-> Cartesian physical-space mapping; PolarGeometry: SIBLING for a glob...
MultiFab: a field DISTRIBUTED over a level (equivalent of AMReX's MultiFab).
void coupler_inject_aux_mb(const MultiFab &parent, MultiFab &child, bool replicated_parent=true)
Definition amr_coupler_mp.hpp:60
std::vector< double > coupler_read_coarse(const MultiFab &U, int n, bool replicated)
Definition amr_coupler_mp.hpp:169
std::pair< BoxArray, DistributionMapping > coupler_make_coarse_layout(int n, bool distribute, int max_grid)
Definition amr_coupler_mp.hpp:257
void coupler_eval_rhs(const MultiFab &state, MultiFab &rhs, const Model &model)
Assemble the single-model elliptic RHS: rhs = model.elliptic_rhs(U) on valid cells (delegated to Sing...
Definition coupler.hpp:53
std::vector< double > coupler_read_coarse_phi(const MultiFab &aux0, int n, bool replicated)
Definition amr_coupler_mp.hpp:191
void coupler_write_coarse(MultiFab &U, const std::vector< double > &rho, int n, int ncomp, double gamma)
Definition amr_coupler_mp.hpp:112
void coupler_grad_phi(const MultiFab &phi, MultiFab &aux, Real cx, Real cy)
Set aux = (phi, d phi/dx, d phi/dy) by centered differences (factors cx, cy = 1/(2 dx),...
Definition coupler.hpp:64
void coupler_inject_coarse_to_fine_mb(const MultiFab &Uc, MultiFab &Uf, bool replicated)
Definition amr_coupler_mp.hpp:212
void coupler_write_coarse_state(MultiFab &U, const std::vector< double > &state, int n, int ncomp)
Definition amr_coupler_mp.hpp:143
Definition amr_hierarchy.hpp:29
int mf_find_box(const MultiFab &mf, int I, int J)
Definition amr_subcycling.hpp:231
double Real
Definition types.hpp:30
void mf_average_down_mb(const MultiFab &Uf, MultiFab &Uc)
Definition amr_subcycling.hpp:305
int n_ranks()
Definition comm.hpp:139
BCRec aux_halo_override(const BCRec &shared, const AuxHaloPolicy &p)
Builds the effective override BCRec for a per-field aux halo: starts from the SHARED aux BC shared (s...
Definition physical_bc.hpp:204
double all_reduce_sum(double x)
Definition comm.hpp:143
BoxArray coarsen(const BoxArray &ba, int r)
Coarsens each box of the BoxArray by a ratio r (coarsen box by box, order preserved).
Definition refinement.hpp:39
BoxArray coarsen_grown(const BoxArray &ba, int ngrow, int r)
Definition amr_subcycling.hpp:241
void all_reduce_sum_inplace(double *, int)
Definition comm.hpp:155
POPS_HD constexpr int aux_comps()
Width of the aux channel a model CONSUMES.
Definition physical_model.hpp:67
void device_fence()
Device barrier: waits for in-flight kernels to finish before a HOST access to unified memory.
Definition kokkos_env.hpp:43
void fill_boundary(MultiFab &mf, const Box2D &domain, Periodicity per={})
BLOCKING halo exchange: begin then end immediately (no overlap).
Definition fill_boundary.hpp:333
Real amr_max_drift_speed_mb(const MultiFab &aux0, Real B0)
LOCAL max drift speed: max of |grad phi| / B0 (aux comp 1, 2 = grad phi) over the valid cells,...
Definition amr_diagnostics.hpp:48
Real amr_mass_mb(const MultiFab &coarse, Real dx, Real dy)
LOCAL mass: sum of u(.,.,0) * dx * dy over the valid cells of ALL local fabs, WITHOUT MPI reduction (...
Definition amr_diagnostics.hpp:33
int my_rank()
Definition comm.hpp:136
void fill_physical_bc(MultiFab &mf, const Box2D &domain, const BCRec &bc)
Fills the physical-face ghosts of ALL components per bc (historical entry point, bit-identical).
Definition physical_bc.hpp:176
AmrTimeMethod
Definition amr_flux_helpers.hpp:46
double all_reduce_max(double x)
Definition comm.hpp:146
constexpr int kAmrRefRatio
The native AMR refinement ratio between two consecutive levels.
Definition refinement_ratio.hpp:30
void amr_regrid_finest(std::vector< AmrLevelMP > &L, std::vector< MultiFab > &aux, const Box2D &dom, Crit crit, int grow, int margin, int aux_ncomp=kAuxBaseComps, bool coarse_replicated=true)
Regrid the finest level (L.back()) by Berger-Rigoutsos on the criterion crit applied to the parent: r...
Definition amr_regrid_coupler.hpp:151
void parallel_copy(MultiFab &dst, const MultiFab &src)
Copies the valid regions that OVERLAP from src to dst (same indices, no shift).
Definition refinement.hpp:50
POPS_HD int coarsen_index(int a, int r)
Index of the coarse cell containing the fine cell a (FLOOR division by r, handles a < 0).
Definition refinement.hpp:34
MultiFab regrid_field_on_layout(const BoxArray &fb, const DistributionMapping &dmap, const MultiFab &par, const MultiFab &old, int pk, int ngf, bool coarse_replicated=true)
Rebuild ONE fine MultiFab on the IMPOSED layout fb / dmap (the same one for all blocks in multi-block...
Definition amr_regrid_coupler.hpp:86
void fill_ghosts(MultiFab &mf, const Box2D &domain, const BCRec &bc)
COMPLETE ghost filling: fill_boundary (interior + periodic, periodicity deduced from bc) THEN fill_ph...
Definition physical_bc.hpp:227
PHYSICAL boundary conditions at the domain edge (BCType, BCRec, fill_physical_bc, fill_ghosts).
AMR inter-level transfer operators (integer ratio r) + parallel_copy.
Single source of truth for the native AMR refinement ratio.
WRITE POD handle (raw pointer + strides) over a Fab2D buffer, indexed by (i, j, c) IN GLOBAL INDICES ...
Definition fab2d.hpp:29
Per-field aux halo policy (ADC-369): a UNIFORM boundary policy for ONE model-named aux component,...
Definition physical_bc.hpp:195
POINTWISE auxiliary fields shared with the physics: single coupling channel.
Definition state.hpp:123
Boundary conditions for the FOUR faces of the domain (type + associated Dirichlet value).
Definition physical_bc.hpp:29
2D integer index space, cell-centered.
Definition box2d.hpp:37
static Box2D from_extents(int nx, int ny)
Box [0, nx-1] x [0, ny-1] covering nx*ny cells from the index origin.
Definition box2d.hpp:42
int hi[2]
Definition box2d.hpp:39
int lo[2]
Definition box2d.hpp:38
READ-only handle (const counterpart of Array4): same layout and same contract (POD device-copyable,...
Definition fab2d.hpp:44
Cartesian geometry of a level: index domain + physical bounds [xlo, xhi] x [ylo, yhi].
Definition geometry.hpp:20
POPS_HD Real dy() const
Grid spacing in y (= (yhi - ylo) / domain.ny()). POPS_HD.
Definition geometry.hpp:33
POPS_HD Real dx() const
Grid spacing in x (= (xhi - xlo) / domain.nx()). POPS_HD.
Definition geometry.hpp:31
Options of the local Newton of the implicit source (backward-Euler).
Definition implicit_stepper.hpp:114
Per-direction periodicity: halo wrapping in x and/or y during the exchange (false = open edge,...
Definition fill_boundary.hpp:37
Base scalar types and the POPS_HD macro (host+device portability).