include/pops/coupling/system/amr_system_coupler.hpp Source FileΒΆ

adc_cpp: include/pops/coupling/system/amr_system_coupler.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_system_coupler.hpp
Go to the documentation of this file.
1#pragma once
2
6#include <pops/coupling/amr/amr_coupler_mp.hpp> // detail::coupler_inject_aux_mb
7#include <pops/coupling/base/aux_fill.hpp> // detail::derive_aux_bc + detail::fill_bz_box (shared)
8#include <pops/coupling/source/coupled_source.hpp> // CoupledSourceFor
10#include <pops/numerics/elliptic/interface/elliptic_problem.hpp> // field_postprocess, FieldPostProcess
13#include <pops/numerics/time/amr/reflux/amr_reflux_mf.hpp> // AmrLevelMP, advance_amr, mf_average_down_mb
14#include <pops/numerics/time/integrators/implicit_stepper.hpp> // backward_euler_source
15#include <pops/numerics/time/schemes/scheduler.hpp> // block_substeps_v, block_time_treatment_v
24#include <pops/parallel/comm.hpp> // all_reduce_sum
25
26#include <cstddef>
27#include <functional>
28#include <stdexcept>
29#include <type_traits>
30#include <utility>
31#include <vector>
32
44
45namespace pops {
46
47static_assert(kAmrRefRatio == 2, "refine(1 << k) assumes a power-of-two (ratio-2) cascade");
48
53
54// EXPLICIT layout of a shared AMR hierarchy (point 2 of the multi-block capstone, first MINIMAL
55// step). Single source of truth on the GRID that all blocks share: per level the BoxArray (the boxes
56// AND their order), the DistributionMapping (rank per box), dx/dy, and the number of levels
57// (= ba.size()). Today this information is implicit, scattered across each AmrLevelMP (U.box_array() /
58// U.dmap() / dx,dy). This type only EXTRACTS it for the same_layout_or_throw guard: it does NOT
59// replace EquationBlock / AmrLevelMP and introduces NO block abstraction (the wide AmrBlock of the
60// design is a LATER step, and only if needed). The layout of a stack of levels is read via from_levels.
65 std::vector<BoxArray> ba; // [level]: boxes of the level (set AND order)
66 std::vector<DistributionMapping> dm; // [level], parallel to ba: MPI rank per box
67 std::vector<Real> dx, dy; // [level]: grid spacing (= dx_coarse / 2^k)
68
70 int nlev() const { return static_cast<int>(ba.size()); }
71
72 // Reads the layout carried by the level stack of ONE block (each AmrLevelMP carries
73 // U.box_array() / U.dmap() / dx,dy). No copy of field data: only the grid.
76 static AmrHierarchyLayout from_levels(const std::vector<AmrLevelMP>& levels) {
78 const int n = static_cast<int>(levels.size());
79 L.ba.reserve(n);
80 L.dm.reserve(n);
81 L.dx.reserve(n);
82 L.dy.reserve(n);
83 for (const auto& lv : levels) {
84 L.ba.push_back(lv.U.box_array());
85 L.dm.push_back(lv.U.dmap());
86 L.dx.push_back(lv.dx);
87 L.dy.push_back(lv.dy);
88 }
89 return L;
90 }
91};
92
93namespace detail {
94template <class>
95inline constexpr bool amr_always_false_v = false;
96
97// EXACT comparison of the grids of two levels (point 1): same BoxArray (boxes AND order), same
98// DistributionMapping (rank per box), same dx/dy (bit-for-bit). Returns true if everything matches.
99// dx/dy are the level spacings, identical by construction if the boxes are; we compare them anyway to
100// catch a mis-wired geometry.
101inline bool same_level_layout(const BoxArray& a_ba, const DistributionMapping& a_dm, Real a_dx,
102 Real a_dy, const BoxArray& b_ba, const DistributionMapping& b_dm,
103 Real b_dx, Real b_dy) {
104 return a_ba.boxes() == b_ba.boxes() && a_dm.ranks() == b_dm.ranks() && a_dx == b_dx &&
105 a_dy == b_dy;
106}
107
108// LAYOUT CONSISTENCY guard between blocks (point 1 of the capstone). The aux is SHARED per level: all
109// blocks MUST live on EXACTLY the same grid at each level, otherwise the rewiring
110// levels[k].aux = &aux_[k] and the advance read an inconsistent grid (silent out-of-bound access).
111// The old check only compared the NUMBER of boxes (.size()); here we compare EXACTLY: number of
112// levels, then per level BoxArray (boxes AND order), DistributionMapping and dx/dy. Throws a clear
113// error at the FIRST discrepancy (block and level located). A single block matches itself trivially ->
114// single-block path strictly bit-identical (the loop over the other blocks is empty).
115inline void same_layout_or_throw(const std::vector<std::vector<AmrLevelMP>>& block_levels) {
116 if (block_levels.empty())
117 return;
118 const auto& ref = block_levels[0];
119 const int nlev = static_cast<int>(ref.size());
120 for (std::size_t b = 1; b < block_levels.size(); ++b) {
121 const auto& cur = block_levels[b];
122 if (static_cast<int>(cur.size()) != nlev)
123 throw std::runtime_error(
124 "AmrSystemCoupler: all blocks must have the same number of levels "
125 "(shared AMR layout)");
126 for (int k = 0; k < nlev; ++k) {
127 if (!same_level_layout(cur[k].U.box_array(), cur[k].U.dmap(), cur[k].dx, cur[k].dy,
128 ref[k].U.box_array(), ref[k].U.dmap(), ref[k].dx, ref[k].dy))
129 throw std::runtime_error(
130 "AmrSystemCoupler: inconsistent AMR layout between blocks (the shared aux requires the "
131 "SAME BoxArray [boxes and order], the SAME DistributionMapping and the SAME dx/dy per "
132 "level)");
133 }
134 }
135}
136} // namespace detail
137
142template <CoupledSystemLike System, class RhsAssembler, class Elliptic = GeometricMG>
144 static_assert(EllipticSolver<Elliptic>, "the elliptic backend must model EllipticSolver");
145
146 public:
147 // block_levels[b] = hierarchy of block b (level 0 = coarse on ba_coarse, levels
148 // > 0 = fine patches). The AmrLevelMP carry U + dx/dy per level; their aux pointer
149 // is (re)wired here to the SHARED aux. The ctor also re-points block.state to the
150 // coarse level of its hierarchy, so that the system RHS (ChargeDensityRhs) reads
151 // the coarse densities correctly.
152 // bz: out-of-plane magnetic field B_z(x, y) provided by the user (constant or field),
153 // shared by ALL blocks. Set on the B_z component (index kAuxBaseComps) of the SHARED aux
154 // channel of EACH level, from the cell centers OF THAT LEVEL (each level has its own
155 // geometry / dx). AMR analog of the bz_ of SystemAssembler (non-AMR path). A block that
156 // reads B_z (n_aux=4) sees it at all levels, a base block (3) ignores the component. Without
157 // a block with an extra field (width 3) or if bz is empty: no-op -> bit-identical to history.
158 AmrSystemCoupler(System system, const Geometry& geom, const BoxArray& ba_coarse,
159 const BCRec& bcPhi, RhsAssembler rhs_assembler,
160 std::vector<std::vector<AmrLevelMP>> block_levels,
161 Periodicity base_per = Periodicity{true, true}, bool replicated_coarse = true,
163 std::function<bool(Real, Real)> active = {},
164 std::function<Real(Real, Real)> bz = {})
165 : system_(std::move(system)),
166 rhs_assembler_(std::move(rhs_assembler)),
167 geom_(geom),
168 dom_(geom.domain),
169 base_per_(base_per),
170 bcPhi_(bcPhi),
171 aux_bc_(detail::derive_aux_bc(bcPhi)),
172 replicated_coarse_(replicated_coarse),
173 cadence_(cadence),
174 mg_(geom, ba_coarse, bcPhi, std::move(active), replicated_coarse),
175 block_levels_(std::move(block_levels)),
176 bz_(std::move(bz)) {
177 // Construction checks (Codex review): without them, a malformed hierarchy
178 // causes a silent out-of-bound access in the wiring / the advance.
179 if (block_levels_.size() != System::n_blocks)
180 throw std::runtime_error(
181 "AmrSystemCoupler: block_levels must have one level vector per block "
182 "(size != n_blocks)");
183 nlev_ = block_levels_.empty() ? 0 : static_cast<int>(block_levels_[0].size());
184 if (nlev_ == 0)
185 throw std::runtime_error("AmrSystemCoupler: at least one level (coarse) required");
186 // EXACT layout consistency between blocks (the aux is shared per level): same number of
187 // levels, and per level same BoxArray (boxes AND order), same DistributionMapping, same
188 // dx/dy. Replaces the old check that only compared the NUMBER of boxes (.size()).
189 // Single-block: the check is trivial (a single block) -> bit-identical to history.
190 detail::same_layout_or_throw(block_levels_);
191 // SHARED aux: one MultiFab (phi, grad phi [, B_z, ...]) per level, on the common grid.
192 // Sized once -> stable addresses for the blocks' aux pointers. Width =
193 // max of aux_comps<Model> over the blocks (at least 3): a block reading B_z (n_aux > 3) has
194 // the room at EACH level, a base block ignores the extra components. Without a block with an
195 // extra field -> width 3 -> allocation strictly bit-identical to history.
196 aux_ncomp_ = system_aux_comps(system_);
197 aux_.resize(nlev_);
198 for (int k = 0; k < nlev_; ++k)
199 aux_[k] =
200 MultiFab(block_levels_[0][k].U.box_array(), block_levels_[0][k].U.dmap(), aux_ncomp_, 1);
201 for (auto& levels : block_levels_)
202 for (int k = 0; k < nlev_; ++k)
203 levels[k].aux = &aux_[k];
204
205 // re-point each block to ITS coarse level (block.U() = coarse of the block).
206 std::size_t b = 0;
207 system_.for_each_block([&](auto& block) {
208 block.state = &block_levels_[b][0].U;
209 ++b;
210 });
211
212 fill_bz(); // populates B_z per level (no-op if no block requests it or if bz is empty)
213 }
214
215 // Setter (parity with the ctor: alternative to set B_z after construction). Immediately
216 // re-populates the aux channel of each level. Effective no-op if the aux width <= base.
217 void set_bz(std::function<Real(Real, Real)> bz) {
218 bz_ = std::move(bz);
219 fill_bz();
220 }
221
222 System& system() { return system_; }
223 const System& system() const { return system_; }
224 MultiFab& phi() { return mg_.phi(); }
225 int nlev() const { return nlev_; }
226 const MultiFab& aux(int k) const { return aux_[k]; }
227 // WRITE access to the shared aux channel of level k (parity with SystemAssembler::aux()):
228 // allows populating an extra component (B_z, ...) that field_postprocess does not touch
229 // (it only writes phi/grad, comp 0..2). The width is aux_ncomp_ (max aux_comps of the blocks).
230 MultiFab& aux(int k) { return aux_[k]; }
231 int aux_ncomp() const { return aux_ncomp_; }
232 std::vector<AmrLevelMP>& levels(std::size_t b) { return block_levels_[b]; }
233 MultiFab& coarse(std::size_t b) { return block_levels_[b][0].U; }
234 const MultiFab& coarse(std::size_t b) const { return block_levels_[b][0].U; }
235 // number of Poisson solves of the last step(): diagnostic of the cadence.
236 int solve_count() const { return solve_count_; }
237
238 // sync_down (per block) + coarse system Poisson + coarse aux + fine injection.
243 ++solve_count_;
244 for (auto& levels : block_levels_)
245 for (int k = nlev_ - 1; k >= 1; --k)
246 mf_average_down_mb(levels[k].U, levels[k - 1].U);
247
248 rhs_assembler_(system_, mg_.rhs()); // f = Sum_s q_s n_s on the coarse level
249 mg_.solve();
250
251 // coarse aux = (phi, grad phi) via the SAME clean path as the single-level
252 // SystemCoupler (Codex review 9.4): fill the ghosts of phi according to bcPhi_, then
253 // field_postprocess, then fill the ghosts of aux according to aux_bc_ (derived from bcPhi_).
254 // Handles the non-periodic case (Foextrap) instead of a hard-coded periodic fill_boundary.
255 fill_ghosts(mg_.phi(), dom_, bcPhi_);
256 const Real cx = Real(1) / (2 * geom_.dx()), cy = Real(1) / (2 * geom_.dy());
257 field_postprocess(mg_.phi(), aux_[0], cx, cy,
258 FieldPostProcess{FieldPostProcess::GradSign::Plus, true});
259 fill_ghosts(aux_[0], dom_, aux_bc_);
260 for (int k = 1; k < nlev_; ++k)
261 detail::coupler_inject_aux_mb(aux_[k - 1], aux_[k],
262 /*replicated_parent=*/(k == 1) && replicated_coarse_);
263
264 // B_z PER LEVEL (not just propagated): coupler_inject_aux_mb copies ALL the components
265 // of the parent (including B_z) to the fine levels, which would overwrite the fine B_z with a
266 // coarse B_z injected (constant per coarse cell). So we re-set B_z from bz_ at the FINE centers
267 // after the injection, so that a spatially varying B_z is sampled at the level resolution.
268 // Static and cheap; no-op if the aux width <= base or bz empty (constant B_z: this re-fill is
269 // idempotent, the injection would have sufficed).
270 fill_bz();
271 }
272
273 // Advances the system by one step. Explicit blocks: advance_amr with their Disc and their
274 // species substeps. Implicit / IMEX blocks: delegated to the callback (coupler, block,
275 // levels, dt), Newton / IMEX branch point (default AmrImplicitSourceStepper).
279 template <class ImplicitAdvance>
280 void step(Real dt, ImplicitAdvance&& implicit_advance) {
281 solve_count_ = 0;
282 solve_fields();
283 std::size_t b = 0;
284 system_.for_each_block([&](auto& block) {
285 using Block = std::decay_t<decltype(block)>;
286 using Disc = typename Block::Spatial;
287 constexpr TimeTreatment treatment = block_time_treatment_v<Block>;
288 constexpr int n = block_substeps_v<Block>;
289 constexpr int stride = block_stride_v<Block>;
290 const std::size_t bi = b++;
291 // HOLD-THEN-CATCH-UP cadence (add_block doc, sec.8.2 C): the block is HELD at the
292 // macro-steps 0..stride-2 and catches up at macro-step stride-1 (when
293 // (macro_step_+1) % stride == 0). Avoids a slow block advancing ahead
294 // at the first macro-step (macro_step_=0, old condition 0%stride==0 true),
295 // which put the slow block IN THE FUTURE relative to the fast blocks.
296 // stride=1: (macro_step_+1)%1==0 always true -> every step, bit-identical.
297 if ((macro_step_ + 1) % stride != 0)
298 return;
299 const Real bdt = dt * static_cast<Real>(stride);
300 auto& levels = block_levels_[bi];
301 if constexpr (treatment == TimeTreatment::Explicit) {
302 const Real h = bdt / static_cast<Real>(n);
303 for (int s = 0; s < n; ++s) {
304 // PerSubstep: re-solves phi before each subsequent substep (the charge has
305 // moved); the first reuses the head solve. OncePerStep: phi frozen.
306 if (cadence_ == PoissonCadence::PerSubstep && s > 0)
307 solve_fields();
308 advance_amr<typename Disc::Limiter, typename Disc::NumericalFlux>(
309 block.model, levels, dom_, h, base_per_, replicated_coarse_);
310 }
311 } else if constexpr (treatment == TimeTreatment::Implicit ||
312 treatment == TimeTreatment::IMEX) {
313 // IMEX = true forward-backward (Codex review 9.1): explicit transport by the
314 // AMR engine on a SOURCE-FREE model (-div F only), then implicit source by
315 // the callback. Pure implicit: everything to the callback (no transport).
316 if constexpr (treatment == TimeTreatment::IMEX)
317 advance_amr<typename Disc::Limiter, typename Disc::NumericalFlux>(
318 SourceFreeModel<typename Block::Model>{block.model}, levels, dom_, bdt, base_per_,
319 replicated_coarse_);
320 implicit_advance(*this, block, levels, bdt);
321 }
322 });
323 ++macro_step_;
324 }
325
326 // Overload for a fully explicit system.
328 void step(Real dt) {
329 step(dt, [](auto&, auto& block, auto&, Real) {
330 using Block = std::decay_t<decltype(block)>;
331 static_assert(detail::amr_always_false_v<Block>,
332 "AmrSystemCoupler::step(dt) cannot advance an "
333 "implicit/IMEX block without a callback");
334 });
335 }
336
337 // Inter-species coupling source on AMR (parity with SystemCoupler, Codex review
338 // 9.5): forward-Euler splitting applied PER LEVEL. We refresh phi (aux per
339 // level) then, at each level k, we temporarily re-point each block to its
340 // level k and let the source read all the blocks + aux[k]. NoCoupledSource => no-op.
341 template <class CoupledSource>
342 void coupled_source_step(CoupledSource&& src, Real dt) {
344 "coupled_source_step expects a CoupledSource: apply(system, aux, dt)");
345 solve_fields();
346 for (int k = 0; k < nlev_; ++k) {
347 std::size_t b = 0;
348 MultiFab* saved[System::n_blocks == 0 ? 1 : System::n_blocks];
349 system_.for_each_block([&](auto& block) {
350 saved[b] = block.state;
351 block.state = &block_levels_[b][k].U;
352 ++b;
353 });
354 src.apply(system_, aux_[k], dt);
355 b = 0;
356 system_.for_each_block([&](auto& block) { block.state = saved[b++]; });
357 }
358 // COVERAGE INVARIANT: the source was applied independently on EACH
359 // level, so a coarse cell COVERED by a fine patch now carries its
360 // own coarse source, unrelated to the source seen by its fine children. A
361 // covered coarse cell must, by definition, be the 2x2 average of its children
362 // (it does not represent matter on its own, it is a coarse view of the fine).
363 // We restore this consistency by a fine -> coarse cascade identical to that of
364 // solve_fields and of the transport-IMEX path (subcycle_level_mp). Without it, the
365 // amr_mass diagnostic (which sums only the coarse level) counts a ghost coarse source
366 // under the patch. Single-level hierarchy: no covered cell, the loop
367 // does not execute -> strictly bit-identical to history.
368 for (auto& levels : block_levels_)
369 for (int k = nlev_ - 1; k >= 1; --k)
370 mf_average_down_mb(levels[k].U, levels[k - 1].U);
371 }
372
373 // mass of component 0 of the coarse level of block b (sum u*dV over local fabs;
374 // replicated coarse -> local sum = total, otherwise all_reduce).
375 Real mass(std::size_t b) const {
376 const MultiFab& U = block_levels_[b][0].U;
377 const Real dV = geom_.dx() * geom_.dy();
378 Real M = 0;
379 for (int li = 0; li < U.local_size(); ++li) {
380 const ConstArray4 u = U.fab(li).const_array();
381 M += for_each_cell_reduce_sum(U.box(li),
382 [u, dV] POPS_HD(int i, int j) { return u(i, j, 0) * dV; });
383 }
384 return replicated_coarse_ ? M : all_reduce_sum(M);
385 }
386
387 private:
388 System system_;
389 RhsAssembler rhs_assembler_;
390 // Width of the SHARED aux channel: maximum of aux_comps<Model> over all the blocks (at least
391 // kAuxBaseComps). The shared channel per level must be at least as wide as the most demanding
392 // block so that load_aux<aux_comps<Model>> never reads out of bound in the AMR paths
393 // (compute_face_fluxes, mf_apply_source, ...); a less demanding block simply ignores the extra
394 // components. Exact analog of SystemAssembler::system_aux_comps (non-AMR path). Without a
395 // block with an extra field, the width stays 3 -> allocation strictly bit-identical to history.
396 static int system_aux_comps(const System& sys) {
397 int w = kAuxBaseComps;
398 sys.for_each_block([&](const auto& b) {
399 using Model = std::decay_t<decltype(b.model)>;
400 const int c = aux_comps<Model>();
401 if (c > w)
402 w = c;
403 });
404 return w;
405 }
406 // Populates the aux B_z component (index kAuxBaseComps) of the shared channel of EACH level from
407 // bz_(x, y). B_z is static (external to the elliptic): set once (at the ctor / set_bz),
408 // preserved by solve_fields (field_postprocess only writes phi/grad, comp 0..2; we re-set
409 // after the coarse->fine injection which would copy a coarse B_z) and by the advance (the
410 // AMR engine does not touch the aux). Each level has ITS geometry: level k = geom_.refine(1 << k),
411 // same physical extents but refined index domain, so x_cell/y_cell point to the physical
412 // center of the FINE cell. We fill the GROWN box (valid + halos) directly from
413 // bz_(x, y): bz_ being a pure function of the physical position, its evaluation at the ghost
414 // centers gives the physically correct B_z there too (independent of the BC of the fine patch,
415 // without periodicity ambiguity on a patch domain). No-op if the aux width <= kAuxBaseComps (no
416 // block reads B_z) or if bz_ is empty: RUNTIME guard (the width is only known at construction) ->
417 // base model strictly bit-identical to history.
418 void fill_bz() {
419 if (!bz_ || aux_ncomp_ <= kAuxBaseComps)
420 return;
421 for (int k = 0; k < nlev_; ++k) {
422 const Geometry gk = geom_.refine(1 << k); // geometry of level k (dx = dx_coarse / 2^k)
423 MultiFab& A = aux_[k];
424 for (int li = 0; li < A.local_size(); ++li) {
425 Fab2D& f = A.fab(li);
426 // grown box (valid + halos): B_z(x,y) correct everywhere, geometry of level k.
427 detail::fill_bz_box(f, f.grown_box(), gk, bz_);
428 }
429 }
430 }
431
432 Geometry geom_;
433 Box2D dom_;
434 Periodicity base_per_;
435 BCRec bcPhi_, aux_bc_;
436 bool replicated_coarse_;
437 PoissonCadence cadence_;
438 mutable int solve_count_ = 0;
439 int macro_step_ = 0; // macro-step counter (per-block stride cadence)
440 Elliptic mg_;
441 std::vector<std::vector<AmrLevelMP>> block_levels_; // [block][level]
442 std::vector<MultiFab> aux_; // [level], shared
443 int aux_ncomp_ =
444 kAuxBaseComps; // width of the shared aux channel (max aux_comps over the blocks)
445 int nlev_ = 0;
446 std::function<Real(Real, Real)> bz_; // external B_z(x, y) (empty if not provided)
447};
448
449// Default implicit on AMR: backward-Euler (Newton) on the model source, applied
450// to EACH level of the block hierarchy. AMR pendant of ImplicitSourceStepper; same
451// stability (unconditional for a linear relaxation). No solver on the user side.
456 int iters = 2;
457
458 template <class Coupler, class Block, class Levels>
459 void operator()(Coupler& coupler, Block& block, Levels& levels, Real dt) const {
460 const int nlev = static_cast<int>(levels.size());
461 for (int k = 0; k < nlev; ++k)
462 backward_euler_source(block.model, coupler.aux(k), levels[k].U, dt, iters);
463 // COVERAGE INVARIANT (cf. coupled_source_step): the implicit source was
464 // solved independently level by level, so the COVERED coarse cells
465 // carry a ghost coarse source instead of the 2x2 average of their fine
466 // children. We restore consistency by the same fine -> coarse cascade as the
467 // transport-IMEX path, so that a covered coarse cell stays the coarse view of the fine
468 // (otherwise amr_mass, sum of only the coarse level, double-counts the patch source).
469 // Single-level: no covered cell, empty loop -> bit-identical to history.
470 for (int k = nlev - 1; k >= 1; --k)
471 mf_average_down_mb(levels[k].U, levels[k - 1].U);
472 }
473};
474
475// "Advancing" alias (tutor feedback sec.8.2 B, sec.9.6): AmrSystemCoupler assembles (system
476// Poisson + aux per level) AND advances (step, reflux, subcycling). Splitting into two classes is
477// cosmetic and deferred (the unified class is validated bit-identical).
478template <CoupledSystemLike System, class RhsAssembler, class Elliptic = GeometricMG>
480
481} // namespace pops
AmrCouplerMP: MULTI-PATCH E x B AMR coupler (coarse Poisson -> aux = grad phi -> fine injection -> co...
Umbrella for the AMR MultiFab stack: includes the numerics/time sub-headers in dependency order (flux...
Helpers shared by the three couplers (single-block Coupler, SystemAssembler, AmrSystemCoupler) for th...
Box2D: the integer index space of a 2D cell-centered Cartesian grid.
BoxArray: the set of boxes tiling a level (disjoint, covering).
Multi-species system coupler on AMR.
Definition amr_system_coupler.hpp:143
int aux_ncomp() const
Definition amr_system_coupler.hpp:231
MultiFab & aux(int k)
Definition amr_system_coupler.hpp:230
MultiFab & phi()
Definition amr_system_coupler.hpp:224
const System & system() const
Definition amr_system_coupler.hpp:223
std::vector< AmrLevelMP > & levels(std::size_t b)
Definition amr_system_coupler.hpp:232
void step(Real dt, ImplicitAdvance &&implicit_advance)
Advances the system by one macro-step dt.
Definition amr_system_coupler.hpp:280
Real mass(std::size_t b) const
Definition amr_system_coupler.hpp:375
System & system()
Definition amr_system_coupler.hpp:222
int nlev() const
Definition amr_system_coupler.hpp:225
void solve_fields()
Solves the fields: average_down per block, coarse system Poisson (RHS = Sum_s q_s n_s),...
Definition amr_system_coupler.hpp:242
MultiFab & coarse(std::size_t b)
Definition amr_system_coupler.hpp:233
int solve_count() const
Definition amr_system_coupler.hpp:236
void coupled_source_step(CoupledSource &&src, Real dt)
Definition amr_system_coupler.hpp:342
const MultiFab & coarse(std::size_t b) const
Definition amr_system_coupler.hpp:234
void set_bz(std::function< Real(Real, Real)> bz)
Definition amr_system_coupler.hpp:217
void step(Real dt)
Overload for a FULLY explicit system (static_assert if an implicit/IMEX block goes through it).
Definition amr_system_coupler.hpp:328
const MultiFab & aux(int k) const
Definition amr_system_coupler.hpp:226
AmrSystemCoupler(System system, const Geometry &geom, const BoxArray &ba_coarse, const BCRec &bcPhi, RhsAssembler rhs_assembler, std::vector< std::vector< AmrLevelMP > > block_levels, Periodicity base_per=Periodicity{true, true}, bool replicated_coarse=true, PoissonCadence cadence=PoissonCadence::OncePerStep, std::function< bool(Real, Real)> active={}, std::function< Real(Real, Real)> bz={})
Definition amr_system_coupler.hpp:158
Ordered list of boxes tiling a level.
Definition box_array.hpp:22
const std::vector< Box2D > & boxes() const
View on the underlying vector (element-by-element equality = same boxes AND same order).
Definition box_array.hpp:46
Single-block hyperbolic-elliptic coupler.
Definition coupler.hpp:74
const MultiFab & aux() const
Definition coupler.hpp:172
Owning MPI rank of each box, indexed by GLOBAL box index (parallel to a BoxArray).
Definition distribution_mapping.hpp:19
const std::vector< int > & ranks() const
View on the rank vector (element-by-element equality = same assignment).
Definition distribution_mapping.hpp:39
ConstArray4 const_array() const
READ handle (POD device-copyable) over this Fab. Valid as long as the Fab lives.
Definition fab2d.hpp:96
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
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
Coupled multi-species system, composed at runtime from generic bricks.
Definition system.hpp:89
POPS_EXPORT int n_blocks() const
Number of blocks (species) installed.
Parallel seam: minimal MPI abstraction (rank/size + collectives) with serial fallback.
Concept: C is a valid coupling source for System if System is a CoupledSystem and if C exposes apply(...
Definition coupled_source.hpp:24
Definition elliptic_solver.hpp:30
CoupledSourceFor / NoCoupledSource: contract of an inter-species COUPLING source.
CoupledSystem: heterogeneous collection of equation blocks (multi-species, multi-scheme).
DistributionMapping: maps each box (by global index) to its owning MPI rank.
DESCRIPTIVE types of the elliptic stage: EllipticProblem (problem definition) and FieldPostProcess (f...
Elliptic (Poisson) RIGHT-HAND-SIDE assemblers: single-model and N-species.
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...
Implicit / IMEX block step as a named CONTRACT.
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
constexpr bool amr_always_false_v
Definition amr_system_coupler.hpp:95
void fill_bz_box(Fab2D &f, const Box2D &box, const Geometry &g, const Bz &bz)
Writes B_z(x, y) at component kAuxBaseComps on box box of fab f, sampling bz at the cell centers of g...
Definition aux_fill.hpp:42
void same_layout_or_throw(const std::vector< std::vector< AmrLevelMP > > &block_levels)
Definition amr_system_coupler.hpp:115
bool same_level_layout(const BoxArray &a_ba, const DistributionMapping &a_dm, Real a_dx, Real a_dy, const BoxArray &b_ba, const DistributionMapping &b_dm, Real b_dx, Real b_dy)
Definition amr_system_coupler.hpp:101
BCRec derive_aux_bc(const BCRec &b)
Aux-channel BC derived from the potential phi BC: a periodic BC stays periodic, any other becomes Foe...
Definition aux_fill.hpp:27
Definition amr_hierarchy.hpp:29
Real for_each_cell_reduce_sum(const Box2D &b, F f)
SUM reduction of f(i, j) over box b.
Definition for_each.hpp:199
constexpr int kAuxBaseComps
Definition state.hpp:147
double Real
Definition types.hpp:30
void mf_average_down_mb(const MultiFab &Uf, MultiFab &Uc)
Definition amr_subcycling.hpp:305
void backward_euler_source(const Model &model, const MultiFab &aux, MultiFab &U, Real dt, const NewtonOptions &opts, const ImplicitMask< Model::n_vars > &mask={}, NewtonReport *report=nullptr)
Definition implicit_stepper.hpp:486
double all_reduce_sum(double x)
Definition comm.hpp:143
TimeTreatment
Definition time_integrator.hpp:29
constexpr int kAmrRefRatio
The native AMR refinement ratio between two consecutive levels.
Definition refinement_ratio.hpp:30
void field_postprocess(const MultiFab &phi, MultiFab &out, Real cx, Real cy, FieldPostProcess spec)
Definition elliptic_problem.hpp:104
PoissonCadence
Re-solve frequency of the Poisson on AMR: OncePerStep (phi solved once per macro-step,...
Definition amr_system_coupler.hpp:52
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).
Single source of truth for the native AMR refinement ratio.
Minimal scheduler for coupled systems: advance_subcycled reads each block's time policy (traits block...
Single source of truth on the GRID shared by all blocks: per level the BoxArray (boxes AND order),...
Definition amr_system_coupler.hpp:64
std::vector< Real > dy
Definition amr_system_coupler.hpp:67
std::vector< BoxArray > ba
Definition amr_system_coupler.hpp:65
int nlev() const
Number of levels (= ba.size()).
Definition amr_system_coupler.hpp:70
std::vector< DistributionMapping > dm
Definition amr_system_coupler.hpp:66
static AmrHierarchyLayout from_levels(const std::vector< AmrLevelMP > &levels)
Extracts the layout (BoxArray + DistributionMapping + dx/dy per level) from the level stack of ONE bl...
Definition amr_system_coupler.hpp:76
std::vector< Real > dx
Definition amr_system_coupler.hpp:67
Default implicit callback for AmrSystemCoupler::step: backward-Euler (Newton) on the model source,...
Definition amr_system_coupler.hpp:455
void operator()(Coupler &coupler, Block &block, Levels &levels, Real dt) const
Definition amr_system_coupler.hpp:459
int iters
Definition amr_system_coupler.hpp:456
Boundary conditions for the FOUR faces of the domain (type + associated Dirichlet value).
Definition physical_bc.hpp:29
READ-only handle (const counterpart of Array4): same layout and same contract (POD device-copyable,...
Definition fab2d.hpp:44
Definition elliptic_problem.hpp:73
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
Geometry refine(int r) const
Geometry refined by ratio r: SAME physical extent, refined index domain (dx -> dx/r).
Definition geometry.hpp:40
Per-direction periodicity: halo wrapping in x and/or y during the exchange (false = open edge,...
Definition fill_boundary.hpp:37
SourceFreeModel<M>: adapter that cancels the source of M (explicit IMEX half-step).
Definition state_access.hpp:50
Base scalar types and the POPS_HD macro (host+device portability).
#define POPS_HD
Definition types.hpp:25