include/pops/runtime/system/system_block_store.hpp Source FileΒΆ

adc_cpp: include/pops/runtime/system/system_block_store.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
system_block_store.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <pops/core/state/state.hpp> // kAuxBaseComps (default aux channel of the Schur stage: B_z)
4#include <pops/core/foundation/types.hpp> // Real
5#include <pops/core/state/variables.hpp> // VariableSet (role descriptor carried by each block)
6#include <pops/mesh/index/box2d.hpp> // Box2D
7#include <pops/mesh/execution/for_each.hpp> // device_fence (marshaling synchronizes the device before reading the host)
8#include <pops/mesh/storage/multifab.hpp> // MultiFab, Array4, ConstArray4
9
10#include <functional>
11#include <map>
12#include <memory>
13#include <stdexcept>
14#include <string>
15#include <vector>
16
44
45namespace pops {
46
50class CondensedSchurSourceStepper;
54class PolarCondensedSchurSourceStepper;
55
59 public:
63 using CellConvert = std::function<void(const double* in, double* out)>;
64
71 struct BlockState {
72 std::string name;
74 int ncomp;
75 int substeps; // static substeps (add_block)
76 bool evolve; // false = frozen species (fixed background, not advanced)
77 int stride = 1; // cadence: advance once every stride macro-steps
78 double gamma; // for the rest energy (4 var)
79 std::function<void(MultiFab&, Real, int)> advance; // (U, dt, n): n substeps of dt/n
80 std::function<void(MultiFab&, MultiFab&)> rhs_into; // R <- -div F + S (Poisson frozen)
81 std::function<Real(const MultiFab&)> max_speed; // max |wave speed| of the block
82 std::function<void(const MultiFab&, MultiFab&)> add_poisson_rhs; // += elliptic_rhs(U)
83 // Descriptor of the conservative / primitive variables (names + physical ROLES) of the block.
84 // The roles (provided by M::conservative_vars()) let inter-species couplings target a component
85 // by its MEANING (momentum, energy) instead of a hard-coded index u[1]/u[3].
87 // POINTWISE cons <-> prim conversions OF THE BLOCK MODEL (one cell, ncomp doubles in/out).
88 // Set at add time (install_block / push_dynamic) from the real model; empty -> identity (the
89 // model exposes no conversion, e.g. pure scalar or .so generated before this work).
90 // Consumed by set_primitive_state / get_primitive_state (init/diagnostic in primitive).
92 // Schur-CONDENSED SOURCE STAGE (OPT-IN, pops.Split(source=CondensedSchur), cf. set_source_stage).
93 // nullptr (default) = no condensed source stage: the block advances EXACTLY as before
94 // (bit-identical). Non null = after the hyperbolic transport, the step runs the standalone source stage
95 // (CondensedSchurSourceStepper, #126) in place of the explicit / IMEX source. shared_ptr:
96 // keeps BlockState MOVABLE (the stepper carries a GeometricMG, neither copyable nor trivially movable).
97 std::shared_ptr<CondensedSchurSourceStepper> schur;
98 // POLAR counterpart of the condensed source stage (ring (r, theta), Voie A step 2c). Exclusive with
99 // schur above: set_source_stage builds ONE or the OTHER depending on the System geometry (polar
100 // -> schur_polar, Cartesian -> schur). run_source_stage dispatches on whichever is non null. The
101 // Cartesian path stays BIT-IDENTICAL (schur_polar == nullptr when the System is Cartesian).
102 std::shared_ptr<PolarCondensedSchurSourceStepper> schur_polar;
103 double schur_theta = 0.5; // theta-scheme of the source stage (0.5 = Crank-Nicolson)
104 // Component of the aux channel read as magnetic field Omega = B_z by the Schur stage (audit
105 // wave 2: field TRANSPORTED in the ABI instead of the frozen literal kAuxBaseComps). Default =
106 // kAuxBaseComps (canonical B_z channel), bit-identical; set_source_stage may redirect it.
108 // GENERIC SOURCE STAGE (optional): a callable (U, dt) that advances IN PLACE the source stage of the
109 // block. nullptr (default) = no generic source stage (BIT-IDENTICAL path). run_source_stage runs it
110 // ONLY if no condensed Schur stage (schur / schur_polar) is wired, so it changes
111 // NOTHING in the production Schur path. Used for generic splitting (pops.Strang on an arbitrary
112 // source stage) and for the stepper time-order tests (non-commuting toy operators). Trailing
113 // + nullptr default: the positional aggregate init of the other members stays unchanged.
114 std::function<void(MultiFab&, Real)> source_step;
115 // DISK TRANSPORT ADVANCES (work T5-PR3, OPT-IN). Empty (default) -> no disk routing:
116 // the stepper advances via `advance` (full Cartesian path, BIT-IDENTICAL). Non empty, they MIMIC
117 // `advance` (same RK / IMEX scheme, same limiter / flux) but dispatch the transport residual
118 // to the disk operator, and are SELECTED only if the System is in Staircase mode (resp.
119 // CutCell) AND a disk is set (set_disc_domain). Built at block add time (build_block)
120 // AT THE SAME TIME as `advance`, they read the System mask / level set by pointer at step time
121 // (stable address): the order add_block / set_disc_domain is irrelevant. Trailing + empty default:
122 // the positional aggregate init of the other members stays unchanged.
123 std::function<void(MultiFab&, Real, int)>
124 advance_masked; // residual via assemble_rhs_masked (Staircase)
125 std::function<void(MultiFab&, Real, int)> advance_eb; // residual via assemble_rhs_eb (CutCell)
126 // dt_hotspot DIAGNOSTIC (ADC-182): (U, w, i, j) -> GLOBAL cell dominating the transport CFL bound
127 // of the block + its speed w = max(wx, wy). ON DEMAND only (System::dt_hotspot):
128 // never queried by step/step_cfl (hot path bit-identical). Trailing + empty default.
129 std::function<void(const MultiFab&, Real&, int&, int&)> hotspot;
130 // OPTIONAL STEP BOUNDS of the block (audit 2026-06, step_cfl work). EMPTY (default) -> the
131 // stepper does not query them: STRICTLY historical step policy (transport only,
132 // bit-identical). Set by set_block_dt_bounds when the model declares the traits
133 // HasSourceFrequency / HasStabilityDt (cf. core/physical_model.hpp for the semantics).
134 // Trailing + empty default: the positional aggregate init of the other members stays unchanged.
135 std::function<Real(const MultiFab&)>
136 source_frequency; // max over cells of mu [1/s] (0 = no constraint)
137 std::function<Real(const MultiFab&)>
138 stability_dt; // min over cells of the admissible step (0 = no constraint)
139 // PROJECTION PONCTUELLE post-pas (ADC-177) : U <- project(U, aux) sur les cellules VALIDES,
140 // appliquee par le stepper a la FIN de chaque macro-pas ENTIER (apres transport + etage source +
141 // couplages ; jamais par etage RK). VIDE (defaut) -> jamais interrogee (cout nul, chemin
142 // bit-identique). Trailing + defaut vide : l'init par agregat positionnel reste inchangee.
143 std::function<void(MultiFab&)> project;
144 // FLUX-ONLY residual R <- -div F(U) (NO default/composite source), Poisson frozen (ADC-425). The
145 // SAME transport assembly as rhs_into evaluated on SourceFreeModel<Model> (zero source), so the
146 // flux / ghost / geometry handling is bit-identical -- only the source is dropped (with
147 // limiter='none'; the HLL wave-speed cache -- rejected on the aot/production backends compiled
148 // Programs use -- is the only path where cached cell-center speeds differ from the per-face
149 // reconstruction). Read by
150 // System::block_neg_div_flux_into, which a compiled time Program's hyperbolic stage calls so a
151 // Lie/Strang split assembles "flux but no source" (spec criterion 17). EMPTY (default) for paths
152 // that do not build it (the host .so prototype loader); block_neg_div_flux_into fails loud then.
153 // Trailing + empty default: the positional aggregate init of the other members stays unchanged.
154 std::function<void(MultiFab&, MultiFab&)> rhs_flux_only;
155 // NAMED elliptic-field RHS closures (ADC-428): field name -> (+= elliptic_field_rhs(U)). A model
156 // declaring m.elliptic_field("phi2", rhs=...) carries here a SECOND Poisson right-hand side
157 // (distinct from add_poisson_rhs, the default Poisson coupling), assembled the same way (host loop,
158 // += per cell). EMPTY (default) -> no named elliptic field: bit-identical to the historical block.
159 // The SystemFieldSolver gathers these per field (sum over blocks) into a SEPARATE elliptic solve
160 // whose phi/grad are written to the field's OWN aux channel. Trailing + empty default: the
161 // positional aggregate init of the other members stays unchanged.
162 std::map<std::string, std::function<void(const MultiFab&, MultiFab&)>> named_poisson_rhs;
163 // SOURCE-ONLY residual R <- S(U, aux) (the default/composite source, NO flux divergence), Poisson
164 // frozen (ADC-430). The exact MIRROR of rhs_flux_only: together they split rhs_into (-div F + S).
165 // SourceInto evaluates m.source per cell (the SAME source term assemble_rhs adds) with no
166 // numerical-flux dispatch, so it is bit-identical to the source half of rhs_into. Read by
167 // System::block_source_into, which a compiled time Program's source stage calls so a Lie/Strang
168 // split assembles "the default source but no flux" (spec: rhs flux=False is source-only). EMPTY
169 // (default) for paths that do not build it (the host .so prototype loader); block_source_into fails
170 // loud then. Trailing + empty default: the positional aggregate init of the other members stays
171 // unchanged.
172 std::function<void(MultiFab&, MultiFab&)> source_only;
173 };
174
177 std::vector<BlockState> blocks;
178
179 // --- access by NAME (0-based indexing, insertion order) ------------------------------------------
181 BlockState& find(const std::string& name) {
182 for (auto& s : blocks)
183 if (s.name == name)
184 return s;
185 throw std::runtime_error("System : bloc inconnu '" + name + "'");
186 }
188 const BlockState& find(const std::string& name) const {
189 for (auto& s : blocks)
190 if (s.name == name)
191 return s;
192 throw std::runtime_error("System : bloc inconnu '" + name + "'");
193 }
195 int index(const std::string& name) const {
196 for (std::size_t k = 0; k < blocks.size(); ++k)
197 if (blocks[k].name == name)
198 return static_cast<int>(k);
199 throw std::runtime_error("System : bloc inconnu '" + name + "'");
200 }
201
203 int size() const { return static_cast<int>(blocks.size()); }
205 std::vector<std::string> names() const {
206 // Reads the UNIQUE block registry, populated by all add paths: a block loaded via
207 // add_dynamic_block / add_compiled_block (.so) appears just like an add_block.
208 std::vector<std::string> out;
209 out.reserve(blocks.size());
210 for (const auto& s : blocks)
211 out.push_back(s.name);
212 return out;
213 }
214
215 // --- state marshaling (host <-> MultiFab copy; device_fence included) ---------------------------
218 std::vector<double> copy_comp0(const MultiFab& mf) const {
219 device_fence();
220 // MPI single-box: the box lives on the owner rank (rank 0). A rank without a box (local_size()==0)
221 // has NO fab(0) -> return EMPTY rather than an OUT-OF-BOUNDS access (UB). Single-rank: local_size()
222 // is always 1, behavior UNCHANGED. For the global multi-rank field: System::density_global.
223 if (mf.local_size() == 0)
224 return {};
225 const ConstArray4 u = mf.fab(0).const_array();
226 const Box2D v = mf.box(0);
227 std::vector<double> out;
228 out.reserve(static_cast<std::size_t>(v.nx()) * v.ny());
229 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
230 for (int i = v.lo[0]; i <= v.hi[0]; ++i)
231 out.push_back(u(i, j, 0));
232 return out;
233 }
235 std::vector<double> copy_state(const MultiFab& mf, int ncomp) const {
236 device_fence();
237 // Rank without a box (MPI single-box, non-owner): return EMPTY (no fab(0)). Cf. copy_comp0;
238 // the global multi-rank field goes through System::state_global (collective gather).
239 if (mf.local_size() == 0)
240 return {};
241 const ConstArray4 u = mf.fab(0).const_array();
242 const Box2D v = mf.box(0);
243 std::vector<double> out;
244 out.reserve(static_cast<std::size_t>(ncomp) * v.nx() * v.ny());
245 for (int c = 0; c < ncomp; ++c)
246 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
247 for (int i = v.lo[0]; i <= v.hi[0]; ++i)
248 out.push_back(u(i, j, c));
249 return out;
250 }
254 void write_state(MultiFab& mf, int ncomp, const std::vector<double>& in) {
255 // Rank without a box (MPI single-box, non-owner): NO-OP (no fab(0) to write). Lets
256 // sim.set_state / sim.restart be called on ALL ranks with the GLOBAL field: only the
257 // owner rank (rank 0, box = full domain) writes, the others do nothing. Single-rank:
258 // local_size()==1, validation + write UNCHANGED (bit-identical).
259 if (mf.local_size() == 0)
260 return;
261 const Box2D v = mf.box(0);
262 const std::size_t need = static_cast<std::size_t>(ncomp) * v.nx() * v.ny();
263 if (in.size() != need)
264 throw std::runtime_error("System::set_state : taille != ncomp*n*n");
265 Array4 u = mf.fab(0).array();
266 std::size_t k = 0;
267 for (int c = 0; c < ncomp; ++c)
268 for (int j = v.lo[1]; j <= v.hi[1]; ++j)
269 for (int i = v.lo[0]; i <= v.hi[0]; ++i)
270 u(i, j, c) = in[k++];
271 }
272};
273
274} // namespace pops
Box2D: the integer index space of a 2D cell-centered Cartesian grid.
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
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
ORDERED registry of the System blocks + state marshaling helpers.
Definition system_block_store.hpp:58
void write_state(MultiFab &mf, int ncomp, const std::vector< double > &in)
Writes the ncomp components into fab(0) from a component-major buffer (same layout as copy_state).
Definition system_block_store.hpp:254
const BlockState & find(const std::string &name) const
Reference to block name (for reading).
Definition system_block_store.hpp:188
std::vector< double > copy_comp0(const MultiFab &mf) const
Copies component 0 of fab(0) (density) in row-major (j slow, i fast).
Definition system_block_store.hpp:218
BlockState & find(const std::string &name)
Reference to block name (for writing).
Definition system_block_store.hpp:181
int index(const std::string &name) const
0-based index of block name (insertion order).
Definition system_block_store.hpp:195
std::vector< BlockState > blocks
ORDERED registry of the blocks (UNIQUE source of truth).
Definition system_block_store.hpp:177
std::vector< double > copy_state(const MultiFab &mf, int ncomp) const
Copies the ncomp components of fab(0) in component-major layout (c slow, then j, then i).
Definition system_block_store.hpp:235
std::vector< std::string > names() const
Block names, in insertion order (UNIQUE source: all add paths appear here).
Definition system_block_store.hpp:205
std::function< void(const double *in, double *out)> CellConvert
Type-erase of the POINTWISE (one cell) cons <-> prim conversion of a block: in/out are arrays of ncom...
Definition system_block_store.hpp:63
int size() const
Number of registered blocks.
Definition system_block_store.hpp:203
for_each_cell and reductions: the parallelism SEAM over the cells of a Box2D; sync_host / sync_device...
MultiFab: a field DISTRIBUTED over a level (equivalent of AMReX's MultiFab).
Definition amr_hierarchy.hpp:29
constexpr int kAuxBaseComps
Definition state.hpp:147
double Real
Definition types.hpp:30
void device_fence()
Device barrier: waits for in-flight kernels to finish before a HOST access to unified memory.
Definition kokkos_env.hpp:43
Pointwise types of the physics layer: StateVec<N> (conserved state) and Aux (auxiliary fields from th...
WRITE POD handle (raw pointer + strides) over a Fab2D buffer, indexed by (i, j, c) IN GLOBAL INDICES ...
Definition fab2d.hpp:29
2D integer index space, cell-centered.
Definition box2d.hpp:37
POPS_HD int nx() const
Width (direction 0). POPS_HD (called from Geometry::dx() in a device kernel).
Definition box2d.hpp:50
int hi[2]
Definition box2d.hpp:39
POPS_HD int ny() const
Height (direction 1). POPS_HD (called from Geometry::dy() in a device kernel).
Definition box2d.hpp:52
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
Compiled closures frozen at block add time (composite model + spatial scheme + time).
Definition system_block_store.hpp:71
std::function< void(MultiFab &)> project
Definition system_block_store.hpp:143
std::function< Real(const MultiFab &)> source_frequency
Definition system_block_store.hpp:136
int stride
Definition system_block_store.hpp:77
CellConvert prim_to_cons
Definition system_block_store.hpp:91
VariableSet cons_vars
Definition system_block_store.hpp:86
std::function< void(MultiFab &, Real)> source_step
Definition system_block_store.hpp:114
double schur_theta
Definition system_block_store.hpp:103
MultiFab U
Definition system_block_store.hpp:73
std::function< void(MultiFab &, Real, int)> advance_eb
Definition system_block_store.hpp:125
std::function< Real(const MultiFab &)> stability_dt
Definition system_block_store.hpp:138
std::map< std::string, std::function< void(const MultiFab &, MultiFab &)> > named_poisson_rhs
Definition system_block_store.hpp:162
std::shared_ptr< PolarCondensedSchurSourceStepper > schur_polar
Definition system_block_store.hpp:102
std::function< void(MultiFab &, MultiFab &)> rhs_flux_only
Definition system_block_store.hpp:154
std::function< void(MultiFab &, MultiFab &)> source_only
Definition system_block_store.hpp:172
std::function< void(MultiFab &, Real, int)> advance
Definition system_block_store.hpp:79
VariableSet prim_vars
Definition system_block_store.hpp:86
int schur_bz_comp
Definition system_block_store.hpp:107
int substeps
Definition system_block_store.hpp:75
bool evolve
Definition system_block_store.hpp:76
std::string name
Definition system_block_store.hpp:72
std::function< void(MultiFab &, MultiFab &)> rhs_into
Definition system_block_store.hpp:80
std::function< void(MultiFab &, Real, int)> advance_masked
Definition system_block_store.hpp:124
CellConvert cons_to_prim
Definition system_block_store.hpp:91
std::function< void(const MultiFab &, MultiFab &)> add_poisson_rhs
Definition system_block_store.hpp:82
std::shared_ptr< CondensedSchurSourceStepper > schur
Definition system_block_store.hpp:97
std::function< void(const MultiFab &, Real &, int &, int &)> hotspot
Definition system_block_store.hpp:129
double gamma
Definition system_block_store.hpp:78
std::function< Real(const MultiFab &)> max_speed
Definition system_block_store.hpp:81
int ncomp
Definition system_block_store.hpp:74
A model's variable set: kind (cons/prim), names, size, canonical roles (optional, parallel to names; ...
Definition variables.hpp:58
Base scalar types and the POPS_HD macro (host+device portability).
Descriptor of a model's variables (Vars).