System Class ReferenceΒΆ
|
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
|
Coupled multi-species system, composed at runtime from generic bricks. More...
#include <system.hpp>
Collaboration diagram for pops::System:Classes | |
| struct | SourceNewtonReport |
| Report of the implicit source Newton (IMEX) of a block, AGGREGATED over the substeps of the LAST advance of the block. More... | |
Public Types | |
| using | CellConvert = std::function< void(const double *in, double *out)> |
| Type-erasure of the POINTWISE (one cell) cons <-> prim conversion of a block: in/out are arrays of ncomp doubles. | |
Public Member Functions | |||
| System (const SystemConfig &cfg) | |||
| ~System () | |||
| System (System &&) noexcept | |||
| System & | operator= (System &&) noexcept | ||
| void | add_block (const std::string &name, const ModelSpec &model, const std::string &limiter="minmod", const std::string &riemann="rusanov", const std::string &recon="conservative", const std::string &time="explicit", int substeps=1, bool evolve=true, int stride=1, const std::vector< std::string > &implicit_vars={}, const std::vector< std::string > &implicit_roles={}, const NewtonOptions &newton={}, bool newton_diagnostics=false, double positivity_floor=0.0, bool wave_speed_cache=false) | ||
| Adds an equation block (one species). | |||
| SourceNewtonReport | newton_report (const std::string &name) const | ||
| void | add_dynamic_block (const std::string &name, const std::string &so_path, int substeps=1, const std::vector< std::string > &names={}, const std::string &recon="none") | ||
| Adds a block whose model is LOADED AT RUNTIME from a shared library (.so) generated by the DSL (emit_cpp_brick -> ModelAdapter -> extern "C" factory). | |||
| void | add_compiled_block (const std::string &name, const std::string &so_path, const std::string &limiter="minmod", const std::string &riemann="rusanov", const std::string &recon="conservative", const std::string &time="explicit", int substeps=1, const std::vector< std::string > &names={}, double positivity_floor=0.0) | ||
| Adds a block whose model is COMPILED AOT from a .so generated by the DSL (dsl.compile_aot / compile_or_jit(mode="compile")). | |||
| void | set_block_params (const std::string &name, const std::vector< double > &values) | ||
| Changes the values of the RUNTIME parameters of an AOT block (add_compiled_block) WITHOUT recompiling the .so (P7-b). | |||
| void | add_native_block (const std::string &name, const std::string &so_path, const std::string &limiter="minmod", const std::string &riemann="rusanov", const std::string &recon="conservative", const std::string &time="explicit", double gamma=1.4, int substeps=1, bool evolve=true, int stride=1, double positivity_floor=0.0) | ||
| Adds a block whose model is compiled into a NATIVE LOADER .so generated by the DSL (dsl.compile_native / compile(backend="production")). | |||
| void | set_poisson (const std::string &rhs="charge_density", const std::string &solver="geometric_mg", const std::string &bc="auto", const std::string &wall="none", double wall_radius=0.0, double epsilon=1.0, double abs_tol=0.0) | ||
| Configures the shared Poisson. | |||
| std::string | poisson_solver () const | ||
| Configured field (Poisson) solver token, e.g. | |||
| void | set_disc_domain (double cx, double cy, double R, const std::string &mode="none") | ||
Sets the TRANSPORT DOMAIN as a DISC centered at (cx, cy) with radius R (T2 work, CONTRACT inert by default). | |||
| void | set_geometry_mode (const std::string &mode) | ||
| Sets ONLY the disc transport mode (without (re)defining the disc): "none" | "staircase" | "cutcell". | |||
| std::vector< double > | disc_mask () const | ||
| void | set_epsilon_field (const std::vector< double > &eps) | ||
| Sets a VARIABLE permittivity eps(x), n*n row-major field (> 0), at the cell CENTER. | |||
| void | set_epsilon_anisotropic_field (const std::vector< double > &eps_x, const std::vector< double > &eps_y) | ||
| Sets an ANISOTROPIC permittivity eps_x(x), eps_y(x), two n*n row-major fields (> 0), at the CENTER of the cells. | |||
| void | set_reaction_field (const std::vector< double > &kappa) | ||
| Enables a REACTION term kappa(x) >= 0: the system Poisson operator goes from div(eps grad phi) = f to div(eps grad phi) - kappa phi = f (SCREENED Poisson / Helmholtz; kappa = 1/lambda_D^2 for Debye screening). | |||
| void | set_magnetic_field (const std::vector< double > &bz) | ||
| Sets an out-of-plane magnetic field B_z(x, y) SHARED by the blocks, n*n row-major. | |||
| void | set_electron_temperature_from (const std::string &name) | ||
| Designates a COMPRESSIBLE fluid block (4 var) as the source of the electron temperature T_e: the T_e aux channel (next canonical component) is filled with T = p/rho of this block, RECOMPUTED at each solve_fields. | |||
| POPS_EXPORT void | ensure_aux_width (int ncomp) | ||
Guarantees that the SHARED aux channel has at least ncomp components. | |||
| void | set_aux_field_component (int comp, const std::vector< double > &field) | ||
Sets a NAMED aux field (ADC-70 phase 1) on the canonical component comp (>= kAuxNamedBase = 5), row-major n*n (cartesian) / nr*ntheta (polar) array. | |||
| void | set_aux_field_halo_component (int comp, int bc_type, double value) | ||
Declares a per-field aux HALO policy (ADC-369) for the NAMED component comp (>= kAuxNamedBase): bc_type is pops::BCType (Foextrap=1 / Dirichlet=2), value the Dirichlet boundary value (ignored for Foextrap). | |||
| std::vector< double > | aux_field_component (int comp) const | ||
Reads a NAMED aux field (component comp >= kAuxNamedBase): valid cells of the aux channel, row-major n*n (cartesian) / nr*ntheta (polar). | |||
| void | set_density (const std::string &name, const std::vector< double > &rho) | ||
| Sets the density of a species (component 0), n*n row-major array. | |||
| void | set_primitive_state (const std::string &name, const std::vector< double > &prim) | ||
Initializes the state of a block from its PRIMITIVE variables (rho, u, v, p ...): prim is a flat ncomp*n*n component-major array in the order of primitive_vars(name). | |||
| std::vector< double > | get_primitive_state (const std::string &name) | ||
| Reads the CONSERVATIVE state of the block and converts it to PRIMITIVE variables via the model conversion (M.to_primitive). | |||
| POPS_EXPORT void | set_block_conversion (const std::string &name, CellConvert prim_to_cons, CellConvert cons_to_prim) | ||
| Installs the pointwise cons <-> prim conversions of a block (after install_block). | |||
| POPS_EXPORT void | set_block_dt_bounds (const std::string &name, std::function< Real(const MultiFab &)> source_frequency, std::function< Real(const MultiFab &)> stability_dt) | ||
| Installs the optional STEP BOUNDS of a block (after install_block): reduction of the max source frequency (HasSourceFrequency trait, bound dt <= cfl*substeps/(stride*mu)) and of the min admissible step (HasStabilityDt trait, bound dt <= dt_adm*substeps/stride, without cfl). | |||
| void | add_dt_bound (const std::string &label, std::function< double()> fn) | ||
| Adds a GLOBAL time-step bound, evaluated ONCE per step (host) by step_cfl / step_adaptive: dt <= fn() when fn() > 0 and finite (otherwise the bound does not constrain this step). | |||
| std::string | last_dt_bound () const | ||
| Name of the ACTIVE bound (the one that set dt) of the last step_cfl: "transport:<block>", "source_frequency:<block>", "stability_dt:<block>", "global:<label>", "degenerate" (no evolving block), or "" if no step_cfl has run. | |||
| void | add_ionization (const std::string &electron, const std::string &ion, const std::string &neutral, double rate) | ||
| Adds an IONIZATION coupling (operator-split): rate k n_e n_g; a neutral becomes an ion and an electron. | |||
| void | add_collision (const std::string &a, const std::string &b, double rate) | ||
| Adds an inter-species COLLISION / friction (operator-split): force k (u_a - u_b) on the momentum, opposite on each species (total momentum conserved). | |||
| void | add_thermal_exchange (const std::string &a, const std::string &b, double rate) | ||
| Adds an inter-species THERMAL EXCHANGE (operator-split): heat flux k (T_a - T_b) on the energy, opposite on each species (total energy conserved); T = p/rho. | |||
| void | set_source_stage (const std::string &name, const std::string &kind, double theta, double alpha, const SourceStageOptions &opts={}) | ||
Enables a Schur-condensed SOURCE STAGE on block name (EXPLICIT / IMPLICIT splitting, cf. | |||
| void | set_time_scheme (const std::string &scheme) | ||
| Time SPLITTING POLICY of the macro-step (hyperbolic transport H + source stage S): | |||
| void | set_gauss_policy (const std::string &policy) | ||
| Gauss-law policy. | |||
| void | add_coupled_source (const CoupledSourceProgram &prog, double frequency=0.0, const std::string &label="coupled_source") | ||
| Adds a GENERIC inter-species COUPLED SOURCE described by a BYTECODE (pops.dsl.CoupledSource, P5 phase 1, EXPLICIT forward-Euler splitting after transport). | |||
| POPS_EXPORT void | solve_fields () | ||
| solves Poisson then derives aux = (phi, grad phi); exported | |||
| POPS_EXPORT void | solve_fields_from_state (int block_idx, const MultiFab &U_stage) | ||
Per-stage field solve (ADC-409): SAME elliptic solve + aux derivation as solve_fields(), but block block_idx assembles its Poisson RHS from U_stage instead of its live state (the other blocks keep theirs). | |||
| POPS_EXPORT void | solve_fields_from_blocks (const std::vector< const MultiFab * > &U_stages) | ||
| Coupled multi-block field solve (Spec 3 criterion 24, ADC-457): SAME elliptic solve + aux derivation as solve_fields(), but the system Poisson RHS is assembled from the SIMULTANEOUS stage states of MULTIPLE blocks at once – every coupled block reads its OWN stage state, not a single- target override. | |||
| void | step (double dt) | ||
| solve_fields, then advances each block according to its scheme | |||
| void | advance (double dt, int nsteps) | ||
| double | step_cfl (double cfl) | ||
| Advances one step at dt = cfl * h / max wave speed of the system. | |||
| std::array< double, 3 > | dt_hotspot (const std::string &name) | ||
| Diagnostic (ADC-182): {w, i, j} of the GLOBAL cell that dominates the transport CFL bound of the block – to locate a realizability erosion / a collapsing dt. | |||
| double | step_adaptive (double cfl) | ||
| Advances one MULTIRATE macro-step: the slowest block sets the macro-step, each block that is faster is sub-cycled n = ceil(w_block / w_min) times. | |||
| POPS_EXPORT void | block_rhs_into (int b, MultiFab &U, MultiFab &R) | ||
R <- -div F(U) + S(U, aux) for block b (the block's frozen-Poisson residual closure). | |||
| POPS_EXPORT void | block_neg_div_flux_into (int b, MultiFab &U, MultiFab &R) | ||
R <- -div F(U) for block b – the SAME flux divergence as block_rhs_into but WITHOUT the model's default/composite source (Poisson frozen, ghosts filled identically). | |||
| POPS_EXPORT void | block_source_into (int b, MultiFab &U, MultiFab &R) | ||
R <- S(U, aux) for block b – the model's default/composite SOURCE only, WITHOUT the flux divergence (the exact MIRROR of block_neg_div_flux_into, which is flux without source). | |||
| POPS_EXPORT Real | block_max_speed (int b, const MultiFab &U) const | ||
The maximum |wave speed| of block b evaluated on U – the SAME per-block reduction step_cfl reads (BlockState::max_speed, the HasStabilitySpeed / max_wave_speed closure set at add_block time): a collective reduction over the block's cells. | |||
| POPS_EXPORT Real | cfl_min_dx () const | ||
| The MIN physical cell size of the grid (Cartesian min(dx, dy); polar min(dr, r_min*dtheta)) – the SAME hmin the native CFL uses (SystemStepper::cfl_grid_h). | |||
| POPS_EXPORT MultiFab | alloc_scalar_field (int n_comp, int n_ghost) | ||
A fresh scalar field co-distributed with the System mesh: block 0's BoxArray and DistributionMapping, n_comp components, n_ghost ghost layers, zero-initialized. | |||
| POPS_EXPORT void | install_program (const std::string &so_path) | ||
| Load a generated problem.so and install its compiled time Program. | |||
| POPS_EXPORT std::string | installed_program_hash () const | ||
| IR hash of the installed compiled Program (the string returned by the .so's pops_program_hash), or "" if no program is installed. | |||
| POPS_EXPORT void | block_project (int b, MultiFab &u) | ||
Apply block b's post-step positivity projection to u in place (ADC-177): U <- project(U,
aux) over the valid cells, the SAME closure the native per-step path runs (s.project). | |||
AOT-COMPILED block seam (native parity, no marshaling) | |||
To wire a DSL-generated model by COMPOSING at COMPILATION time (production Kokkos + MPI + AMR binary), via the free template pops::add_compiled_model<Model> of pops/runtime/dsl_block.hpp: it builds the closures with block_builder.hpp on the REAL CONTEXT of the System (grid_context) – so the block runs the same path as add_block (fill_boundary = MPI halos, assemble_rhs device), without copying the arrays. That is the difference with add_compiled_block (.so + host marshaling, runtime CPU prototyping). DEFAULT VISIBILITY (POPS_EXPORT): grid_context / install_block / ensure_aux_width are the only methods called by the header template add_compiled_model. A generated loader .so (DSL "production" path, cf. emit_cpp_native_loader / add_native_block) inlines this template and must resolve these symbols from the already loaded _pops module. Compiled with -fvisibility=hidden (pybind11), the module would not export them without this annotation and the loader's dlopen would fail. | |||
| POPS_EXPORT GridContext | grid_context () | ||
| REAL mesh + BC + aux of the System (aux not owned) | |||
| POPS_EXPORT void | install_block (const std::string &name, int ncomp, const VariableSet &cons_vars, const VariableSet &prim_vars, double gamma, BlockClosures closures, std::function< Real(const MultiFab &)> max_speed, std::function< void(const MultiFab &, MultiFab &)> poisson_rhs, int substeps, bool evolve, int stride=1) | ||
| Installs a block from already-built closures (cf. | |||
| POPS_EXPORT void | set_block_ghosts (const std::string &name, int n_ghost) | ||
Guarantees that the state U of block name carries at least n_ghost ghosts (width of the spatial stencil). | |||
Named multi-elliptic fields (ADC-428) | |||
A SECOND elliptic solve (beyond the default Poisson) for a user-named field (m.elliptic_field("phi2", rhs=..., aux=[...])). The named field owns its RHS (a per-block brick, distinct from the default elliptic coupling), a DEDICATED native elliptic solver instance, and its OWN aux output components (the model's named aux slots). The default Poisson path (solve_fields / solve_fields_from_state) is untouched / bit-identical. POPS_EXPORT: resolved by the generated problem.so / native loader across the dlopen boundary. Solve named
| |||
| POPS_EXPORT void | solve_fields_from_state (const std::string &field, int block_idx, const MultiFab &U_stage) | ||
| POPS_EXPORT void | register_elliptic_field (const std::string &field, int phi_comp, int gx_comp, int gy_comp) | ||
Register named field's aux output components (where its solved phi / centered grad land). | |||
| POPS_EXPORT void | set_block_elliptic_field (const std::string &block_name, const std::string &field, std::function< void(const MultiFab &, MultiFab &)> rhs) | ||
Attach named field's RHS closure (+= elliptic_field_rhs(U)) to block block_name. | |||
Profiling (Spec 3 section 29-30, ADC-459) | |||
Per-phase / per-brick wall-clock timing of the step. Disabled by default (no hot-path cost when off). enable_profiling() then step()/step_cfl() then profile_report() returns the table; reset_profiling() clears it. Per-rank (no MPI reduction); the per-Program-node granularity is wired through the compiled-program path as a follow-up. | |||
| void | enable_profiling () | ||
| void | disable_profiling () | ||
| bool | is_profiling () const | ||
| void | reset_profiling () | ||
| std::string | profile_report () const | ||
| POPS_EXPORT runtime::program::Profiler & | profiler () | ||
| The System-owned Profiler (a non-owning reference; lives as long as the System). | |||
Primitives for a time integrator written in Python | |||
solve_fields(); R = eval_rhs(name); U = get_state(name); ...; set_state(name, U). | |||
| std::vector< double > | eval_rhs (const std::string &name) | ||
| -div F + S, size ncomp*n*n | |||
| std::vector< double > | get_state (const std::string &name) | ||
| U, ncomp*n*n (component-major) | |||
| void | set_state (const std::string &name, const std::vector< double > &u) | ||
| int | n_vars (const std::string &name) const | ||
| std::vector< std::string > | variable_names (const std::string &name, const std::string &kind="conservative") const | ||
| Variable names of a block (introspection): kind = "conservative" | "primitive". | |||
| std::vector< std::string > | variable_roles (const std::string &name, const std::string &kind="conservative") const | ||
| PHYSICAL roles of the variables of a block (parallel to variable_names): "density", "momentum_x", "energy", ... or "custom" if the block does not provide its roles. | |||
| double | block_gamma (const std::string &name) const | ||
| Adiabatic index (gamma) of the block, read by the inter-species couplings (collision, thermal exchange, T_e). | |||
Compiled time-program seam (epic ADC-399 / ADC-401) | |||
Lets a generated problem.so (via pops::runtime::program::ProgramContext) run a time Program during sim.step(dt): install a macro-step body and reach per-block storage. The .so reimplements nothing – it composes these primitives (solve_fields(); block_rhs_into(b, U, R); saxpy(U, dt, R)). Install the macro-step body. When set, SystemStepper::step calls it instead of the historical path (and keeps t / macro_step coherent). Pass an empty std::function to clear it. POPS_EXPORT: a generated problem.so resolves these across the dlopen boundary (RTLD_GLOBAL), like install_block / grid_context; without default visibility the .so could not find them (_pops is built with hidden visibility). | |||
| POPS_EXPORT void | install_program_step (std::function< void(double)> step) | ||
| POPS_EXPORT void | set_program_cadence (int substeps, int stride) | ||
Set the compiled-Program macro-step cadence (ADC-411): SYSTEM-level substeps and stride around the installed program closure (cf. | |||
| POPS_EXPORT int | n_blocks () const | ||
| Number of blocks (species) installed. | |||
| POPS_EXPORT MultiFab & | block_state (int b) | ||
The conservative state MultiFab of block b (zero-copy, non-owning reference). | |||
Compiled-Program NAME-based block binding (Spec 3 criterion 23, ADC-457) | |||
A compiled Program numbers its blocks in P.state declaration order (the .so's pops_program_block_name table); the System numbers its blocks in add_block / add_equation order (block_names). They need NOT agree. install_program reads the .so's block names, matches each to the System block of that name, and stores the resulting program-index -> system-index map here so ProgramContext::state / rhs_into / commit resolve a Program block index to the name-matched System block – NOT the positional index. An EMPTY map is the identity (a single-block or order-matching Program lowers byte-identically; ProgramContext built directly, e.g. in a C++ test, also sees identity). Lives in Impl (private to the _pops TU) so it survives the dlopen boundary; the seam is POPS_EXPORT so the generated .so and ProgramContext resolve it via RTLD_GLOBAL. Install the program-index -> system-index map (entry p = the System block index of Program block p). Empty clears it (identity). Set by install_program after matching the .so's block names. | |||
| POPS_EXPORT void | set_program_block_map (const std::vector< int > &prog_to_sys) | ||
| POPS_EXPORT const std::vector< int > & | program_block_map () const | ||
| The installed program-index -> system-index map (empty = identity). Read by ProgramContext. | |||
Multistep history (epic ADC-399 / ADC-406a) | |||
SYSTEM-OWNED history ring buffers for multistep schemes (Adams-Bashforth and friends): a named field carried ACROSS macro-steps (e.g. the previous RHS R_{n-1}). The history lives in the System (a HistoryManager in Impl), NOT in the .so closure, so a later checkpoint slice (ADC-406b) can serialize it. A generated problem.so reaches it through ProgramContext::history / store_history / rotate_histories; these are POPS_EXPORT so the .so resolves them across the dlopen boundary. Register (idempotent) a history named
| |||
| POPS_EXPORT MultiFab & | register_history (const std::string &name, int lag) | ||
| POPS_EXPORT MultiFab & | read_history (const std::string &name, int lag) | ||
The history slot lag macro-steps back (lag 0 = the current slot, lag 1 = the previous step's stored value, ...). | |||
| POPS_EXPORT void | store_history (const std::string &name, const MultiFab &value) | ||
Copy value (valid cells) into the CURRENT slot [0] of history name and mark it initialized. | |||
| POPS_EXPORT void | rotate_histories () | ||
| Shift every history ring buffer one step (slot k <- slot k-1, for k = depth-1 . | |||
Multistep history checkpoint/restart (epic ADC-399 / ADC-406b) | |||
SERIALIZE / RESTORE the System-owned history rings across a checkpoint. The history lives in the System (HistoryManager in Impl), so the checkpoint facade (sim.checkpoint / sim.restart) gathers and restores it DIRECTLY – no .so checkpoint_extra ABI is needed for the buffers (only the program hash, installed_program_hash() below, is recorded to reject a restart against a different Program). Names of every registered history (the keys of the HistoryManager), so the facade can iterate the rings to serialize. Empty when no history exists (the single-step paths). Order is the map order. | |||
| POPS_EXPORT std::vector< std::string > | history_names () const | ||
| POPS_EXPORT int | history_depth (const std::string &name) const | ||
Ring depth (max lag + 1) of history name. | |||
| POPS_EXPORT int | history_ncomp (const std::string &name) const | ||
Component count of the slots of history name (the block's ncomp). | |||
| POPS_EXPORT std::vector< double > | history_global (const std::string &name, int slot) const | ||
GLOBAL (collective, MPI-safe) gather of slot slot (0 = current, k = k macro-steps back) of history name into a component-major buffer of size ncomp*ny*nx, EXACTLY like state_global (every rank fills its local boxes then all_reduce_sum). | |||
| POPS_EXPORT bool | history_initialized (const std::string &name) const | ||
Whether history name has been stored at least once (the cold-start fill already happened). | |||
| POPS_EXPORT void | restore_history (const std::string &name, int slot, const std::vector< double > &values) | ||
RESTORE (restart) slot slot of history name from a GLOBAL component-major buffer (same layout as history_global / set_state): the owner rank writes its box, the others are no-ops (MPI-safe, all ranks call it). | |||
| POPS_EXPORT void | set_history_initialized (const std::string &name, bool initialized) | ||
Mark history name initialized (or not) after a restart: a restored, already-stored ring must read at lag without a phantom cold-start re-fill on its first post-restart store. | |||
Scheduler value cache (epic ADC-399 / ADC-458, Spec 3 section 17-18 + 30) | |||
The held-node value cache (every(N).hold / accumulate_dt) lives in the SYSTEM (one CacheManager per installed Program), NOT in the .so step closure – so the checkpoint can reach it, exactly the way the history rings do. Every ProgramContext (the step closure's copy and any fresh one) forwards its cache_* seam ops to this single manager. POPS_EXPORT so the generated problem.so resolves it across the dlopen boundary like the other ProgramContext seam accessors. The System-owned scheduler cache (a non-owning reference; lives as long as the System). A compiled Program's cache_store_aux / cache_restore_aux / cache_should_update reach it through ProgramContext. | |||
| POPS_EXPORT runtime::program::CacheManager & | program_cache () | ||
Scheduler-cache checkpoint/restart (Spec 3 section 30, ADC-458) | |||
SERIALIZE / RESTORE the System-owned cache across a checkpoint, mirroring the history seam: the facade (sim.checkpoint / sim.restart) gathers each VALID slot (gather_global, MPI-safe) and scatters it back (write_state) alongside the block state and histories. The program-hash guard (installed_program_hash) rejects a restart against a different compiled Program; a held scheduled node the checkpoint never recorded fails loud at restart (the facade compares the restored ids). Node ids of every VALID cached slot (ascending). Empty when no schedule cached a value. | |||
| POPS_EXPORT std::vector< int > | program_cache_nodes () const | ||
| POPS_EXPORT std::string | program_cache_name (int node_id) const | ||
The scheduled node name of slot node_id ("fields_from_state"), or "node_<id>" if it was stored without one (the current nameless codegen). | |||
| POPS_EXPORT int | program_cache_last_update_step (int node_id) const | ||
The macro step at slot node_id's last recompute. | |||
| POPS_EXPORT double | program_cache_accumulated_dt (int node_id) const | ||
The accumulated skipped dt of slot node_id (accumulate_dt policy). 0 if none. | |||
| POPS_EXPORT int | program_cache_ncomp (int node_id) const | ||
The component count of slot node_id's cached value. | |||
| POPS_EXPORT int | program_cache_ngrow (int node_id) const | ||
The ghost-cell width of slot node_id's cached value (1 for the aux, the block-state width for a held scratch) – serialized so restore rebuilds with the same ngrow. | |||
| POPS_EXPORT std::vector< double > | program_cache_global (int node_id) const | ||
GLOBAL (collective, MPI-safe) gather of slot node_id's cached MultiFab into a component-major buffer of size ncomp*ny*nx, EXACTLY like state_global / history_global. | |||
| POPS_EXPORT void | restore_program_cache (int node_id, int ncomp, int ngrow, int last_update_step, double accumulated_dt, const std::string &name, const std::vector< double > &values) | ||
RESTORE (restart) slot node_id from a GLOBAL component-major buffer (same layout as program_cache_global / set_state): allocate a value MultiFab co-distributed with block 0 (ncomp components), scatter the buffer into it (owner rank writes, others no-op – MPI-safe, all ranks call it), and re-key the slot with its bookkeeping (name may be empty). | |||
Compiled-Program scalar diagnostics (epic ADC-399 / ADC-414, spec op 23) | |||
A name -> Real map a compiled Program writes via P.record_scalar (ProgramContext::record_scalar), retrievable AFTER sim.step for inspection / logging. Lives in Impl (private to the _pops TU) so it survives across the dlopen boundary; the .so writes it through the POPS_EXPORT setter below. Store | |||
| POPS_EXPORT void | record_program_diagnostic (const std::string &name, Real value) | ||
| POPS_EXPORT Real | program_diagnostic (const std::string &name) const | ||
The recorded value of diagnostic name. | |||
| POPS_EXPORT std::map< std::string, Real > | program_diagnostics () const | ||
| All recorded diagnostics (name -> last recorded value). | |||
Compiled-Program RUNTIME parameters (epic ADC-479 / ADC-510, Spec 5 C5) | |||
A compiled time Program whose physics reads a dsl.Param(..., kind="runtime") carries the value in a per-PROGRAM-block RuntimeParams owned HERE (not in the .so closure), so set_program_params changes it at run time WITHOUT recompiling – the SAME no-recompile contract as the AOT-native set_block_params (the closure reads the System-owned current value each step, not a baked literal). Mirrors the program diagnostics / history rings: System-owned state the step closure reaches through ProgramContext. install_program seeds each block's defaults from the .so pops_program_param_* metadata. The lowered source / linear-source kernels read the CURRENT value via ProgramContext::program_params(block).get(index). Overwrite block
| |||
| POPS_EXPORT void | set_program_params (int prog_block, const std::vector< double > &values) | ||
| POPS_EXPORT RuntimeParams | program_params (int prog_block) const | ||
Block prog_block's CURRENT RuntimeParams (a device-clean by-value copy: trivially copyable, readable in a kernel). | |||
| POPS_EXPORT void | seed_program_params (int prog_block, const std::vector< double > &defaults) | ||
Seed block prog_block's RuntimeParams to its DECLARATION defaults (count values, the .so pops_program_param_default metadata), establishing the no-set baseline. | |||
Diagnostics | |||
| int | nx () const | ||
| POPS_EXPORT int | macro_step () const | ||
| MACRO-STEP counter (0-indexed; incremented by step / step_cfl / step_adaptive). | |||
| void | set_clock (double t, int macro_step) | ||
| RESTORES the clock (t, macro_step) – reserved for the RESTART (sim.restart). | |||
| int | ny () const | ||
| Extent of the SLOW axis of the field (rows of the (ny, nx) row-major array returned by density / potential / get_state). | |||
| double | time () const | ||
| int | n_species () const | ||
| std::vector< std::string > | block_names () const | ||
| Block names, in the order of addition. | |||
| double | mass (const std::string &name) const | ||
| std::vector< double > | density (const std::string &name) const | ||
| ny*nx row-major (j slow, i fast) | |||
| std::vector< double > | potential () | ||
| phi, ny*nx row-major (j slow, i fast) | |||
| void | set_potential (const std::vector< double > &phi) | ||
| RESTORES the potential phi (IO v1, reserved for restart): without it the multigrid would restart from a blank phi and the resume would not be bit-identical (warm start lost); in gauss_policy="evolve", phi IS the physical state and its restoration is indispensable. | |||
GLOBAL accessors (MPI-safe collectives) – outputs / multi-rank checkpoint (IO v1) | |||
The System builds ONE box covering the whole domain (cf. ctor: mono-box ba, round-robin dm -> box 0 on rank 0). The accessors above (density / get_state / potential) read fab(0): VALID on the owner rank (mono-rank OR rank 0 under MPI), but fab(0) is OUT OF BOUNDS on a rank without a box (local_size()==0). The _global variants fill a GLOBAL buffer from the LOCAL fabs (in GLOBAL indices; nothing on an empty rank) then all_reduce_sum_inplace -> EACH rank holds the complete field (AMR reflux pattern, comm.hpp). They are COLLECTIVE: all the ranks MUST call them. On mono-rank they return EXACTLY the same array as the non-global accessors (all_reduce = identity, box = complete domain) -> bit-identical output. The IO facade (sim.write / sim.checkpoint) uses them then writes the file only on rank 0. | |||
| std::vector< double > | density_global (const std::string &name) const | ||
| comp0, ny*nx global | |||
| std::vector< double > | state_global (const std::string &name) const | ||
| U, ncomp*ny*nx global. | |||
| std::vector< double > | potential_global () | ||
| phi, ny*nx global | |||
LOCAL per-fab accessors – PARALLEL HDF5 write by hyperslabs (IO PR-IO-3, opt-in) | |||
Local counterpart of the _global accessors: instead of gathering the whole field by all_reduce_sum, they expose per rank the list of LOCAL boxes and the state of EACH fab. The parallel HDF5 facade (sim.write(format='hdf5', parallel=True)) creates the GLOBAL datasets then each rank writes ITS boxes as hyperslabs – no global gather. They are NON COLLECTIVE (purely local: no MPI comm; a rank without a box returns an empty list). The cartesian System is MONO-BOX (one box covering the domain, on rank 0): local_boxes thus returns ONE box on rank 0 and nothing elsewhere – true hyperslab parallelism appears only on a MULTI-BOX geometry (cf. AMR). The API stays correct in the general case (iteration over all the local fabs, GLOBAL indices in the box). Layout of local_state IDENTICAL to state_global but relative to the local box: (c*bny + (j - jlo))*bnx + (i - ilo), component-major. | |||
| std::vector< std::array< int, 4 > > | local_boxes (const std::string &name) const | ||
| (ilo,jlo,ihi,jhi) per local fab | |||
| std::vector< double > | local_state (const std::string &name, int li) const | ||
| U of fab li, flat (ncomp*bny*bnx) | |||
Static Public Member Functions | |
| static std::string | abi_key () |
| ABI key of the module (compiler + C++ standard + signature of the pops headers, frozen at compilation). | |
Detailed Description
Coupled multi-species system, composed at runtime from generic bricks.
Member Typedef Documentation
◆ CellConvert
| using pops::System::CellConvert = std::function<void(const double* in, double* out)> |
Type-erasure of the POINTWISE (one cell) cons <-> prim conversion of a block: in/out are arrays of ncomp doubles.
Installed by install_block / add_compiled_model / push_dynamic from the block's model, consumed by set_primitive_state / get_primitive_state.
Constructor & Destructor Documentation
◆ System() [1/2]
|
explicit |
◆ ~System()
| pops::System::~System | ( | ) |
◆ System() [2/2]
|
noexcept |
Member Function Documentation
◆ abi_key()
|
static |
ABI key of the module (compiler + C++ standard + signature of the pops headers, frozen at compilation).
Compared to the key baked into a native loader .so by add_native_block; also exposed on the Python side so that emit_cpp_native_loader (or a diagnostic) can consult it.
◆ add_block()
| void pops::System::add_block | ( | const std::string & | name, |
| const ModelSpec & | model, | ||
| const std::string & | limiter = "minmod", |
||
| const std::string & | riemann = "rusanov", |
||
| const std::string & | recon = "conservative", |
||
| const std::string & | time = "explicit", |
||
| int | substeps = 1, |
||
| bool | evolve = true, |
||
| int | stride = 1, |
||
| const std::vector< std::string > & | implicit_vars = {}, |
||
| const std::vector< std::string > & | implicit_roles = {}, |
||
| const NewtonOptions & | newton = {}, |
||
| bool | newton_diagnostics = false, |
||
| double | positivity_floor = 0.0, |
||
| bool | wave_speed_cache = false |
||
| ) |
Adds an equation block (one species).
- Parameters
-
model composition of bricks (transport/source/elliptic + parameters) limiter reconstruction: "none" | "minmod" | "vanleer" | "weno5" riemann numerical flux: "rusanov" (minimal generic) | "hll" (generic, requires model.wave_speeds) | "hllc" | "roe" (generic when the model supplies the HasHLLCStructure / HasRoeDissipation hooks; canonical Euler 2D fallback otherwise: 4 variables + ideal-gas pressure) recon reconstructed variables: "conservative" | "primitive" (Euler: primitive more robust, positivity of rho and p) time "explicit" (SSPRK2) | "ssprk3" | "imex" (explicit transport, local implicit backward-Euler source, order 1) | "imexrk_ars222" (IMEX-RK family, ARS(2,2,2) scheme, order 2, cartesian only; source FULLY implicit -> incompatible with implicit_vars/implicit_roles) substeps substeps per macro-step: the block advances N times per macro-step, each substep of length dt/N (fast electrons: substeps=10, step dt/10). stride block cadence, HOLD-THEN-CATCH-UP semantics: 1 = every macro-step (default, bit-identical); M > 1 = block HELD (not advanced) while (macro_step + 1) % M != 0, then advanced by one effective step M*dt at the macro-step where (macro_step + 1) % M == 0 (end of an M-step window), thus temporally consistent with the fast blocks (slow block, e.g. neutrals on stride=20). substeps and stride are ORTHOGONAL: stride=M, substeps=N -> N substeps of M*dt/N, once at the end of the window. COUPLING: between two catch-ups, the held block enters the Poisson sum with its STALE state (last frozen advance). step_cfl honors the cadence (dt <= cfl*h*substeps / (stride*w)). evolve false = FROZEN species (fixed background): not advanced in time, but seen by the system Poisson (and, in the future, by coupled sources) implicit_vars IMEX only: names of the conservative variables to treat IMPLICITLY in the source step (backward-Euler); the others stay explicit (forward Euler). The mask is CARRIED BY THE BLOCK / time policy (and NOT by the model): the SAME model can thus be reused with different implicit treatments. EMPTY (default) + EMPTY implicit_roles -> model default (Model::is_implicit, or all implicit absent a trait) -> bit-identical. Resolved against the conservative names of the block; an absent name raises an EXPLICIT error. implicit_roles IMEX only: same implicit mask but by physical ROLE ("density", "momentum_x", "energy", ...) instead of the name (cf. variable_roles). Union with implicit_vars. A role absent from the block raises an EXPLICIT error. newton IMEX only: options of the local Newton of the implicit source (backward-Euler), grouped in a POD (ADC-214; cf. NewtonOptions). max_iters (default 2 = historical constant), rel_tol / abs_tol (PER-CELL stopping criterion ||F||_inf <= abs_tol + rel_tol*||F0||_inf, 0/0 = disabled -> historical fixed iterations), fd_eps (step of the finite-difference Jacobian, default 1e-7), damping (damping W -= damping*delta in (0, 1], 1 = full Newton), fail_policy (kFailNone / kFailWarn / kFailThrow: reaction to failed cells). Default {} = historical constants, bit-identical. newton_diagnostics IMEX only: enables the block's Newton report (max residual, max iterations, failed cells – non-finite / degenerate pivot / non-convergence), aggregated over the substeps of each advance and available via newton_report(name). OPT-IN: false (default) = historical path with no extra cost. Stays flat (a separate bool, outside the homogeneous family of convergence options). wave_speed_cache riemann='hll' + explicit ONLY: pre-computes model.wave_speeds ONCE per cell and direction in a scratch, then bounds each face by min/max of the two neighbor cells, instead of recalling wave_speeds per face. Net gain when wave_speeds is expensive (moment hierarchy). With recon='conservative' + limiter 'none' it is BIT-IDENTICAL to the per-face path; with a 2nd-order+ limiter it is a Davis bound on the cell values (different result). false (default) = per-face path unchanged. Wired on the FULL cartesian advance only: refused if riemann != 'hll', time IMEX, polar geometry, or a staircase/cutcell disc transport mode is active (explicit error, never a silent ignore).
◆ add_collision()
| void pops::System::add_collision | ( | const std::string & | a, |
| const std::string & | b, | ||
| double | rate | ||
| ) |
Adds an inter-species COLLISION / friction (operator-split): force k (u_a - u_b) on the momentum, opposite on each species (total momentum conserved).
The two blocks must be fluids (>= 3 variables). Frictional heating neglected (refinement).
◆ add_compiled_block()
| void pops::System::add_compiled_block | ( | const std::string & | name, |
| const std::string & | so_path, | ||
| const std::string & | limiter = "minmod", |
||
| const std::string & | riemann = "rusanov", |
||
| const std::string & | recon = "conservative", |
||
| const std::string & | time = "explicit", |
||
| int | substeps = 1, |
||
| const std::vector< std::string > & | names = {}, |
||
| double | positivity_floor = 0.0 |
||
| ) |
Adds a block whose model is COMPILED AOT from a .so generated by the DSL (dsl.compile_aot / compile_or_jit(mode="compile")).
Unlike the dynamic block (host path, IModel virtual dispatch, order-1 Rusanov), this block runs the PRODUCTION path: the .so runs assemble_rhs<Limiter, Flux> (HLLC/Roe by choice, order 2) and the core's SSPRK2/IMEX on the generated model; only flat arrays cross (extern "C" ABI, cf. compiled_block_abi.hpp).
- Parameters
-
limiter "none" | "minmod" | "vanleer" | "weno5" (weno5: the .so allocates block_n_ghost = 3 ghosts, cf. compiled_block_abi.hpp) riemann "rusanov" | "hll" | "hllc" | "roe" recon "conservative" | "primitive" time "explicit" | "imex" ONLY: the .so's extern "C" ABI only wires SSPRK2 in explicit – "ssprk3" / "euler" are rejected (cf. add_native_block, which carries them)
◆ add_coupled_source()
| void pops::System::add_coupled_source | ( | const CoupledSourceProgram & | prog, |
| double | frequency = 0.0, |
||
| const std::string & | label = "coupled_source" |
||
| ) |
Adds a GENERIC inter-species COUPLED SOURCE described by a BYTECODE (pops.dsl.CoupledSource, P5 phase 1, EXPLICIT forward-Euler splitting after transport).
Unlike the named couplings (add_ionization / add_collision / add_thermal_exchange) which freeze a formula, this one reads (block, role) fields as INPUT and writes source terms (block, role) computed by symbolic EXPRESSIONS compiled to postfix bytecode (stack machine, evaluated in the same for_each_cell device; no per-cell Python callback). Reuses EXACTLY the coupling application seam (P->couplings); MPI-safe (iteration over the local fabs, local_size()==0 -> no-op).
FLAT ABI (no C++ object crosses the boundary):
- Parameters
-
prog bytecode description of the coupling grouped in a POD (ADC-214; cf. CoupledSourceProgram): in_blocks / in_roles (inputs read and their roles), consts (.param() parameters, loaded after the inputs), out_blocks / out_roles (targets of each term), prog_ops / prog_args / prog_lens (concatenated opcodes of ALL terms, stack machine cf. CsOp, parallel arguments, and length per term), and freq_prog_ops / freq_prog_args (OPTIONAL program of a PER-CELL frequency mu(U), same stack machine / register table; EMPTY = constant frequency only, bit-identical). These arrays were a long list of std::vectorof the same type, interchangeable at the call site.frequency declared CONSTANT frequency mu [1/s] of the coupling (audit wave 3, CoupledSource.frequency): step bound dt <= cfl / mu aggregated by step_cfl / step_adaptive (the couplings apply ONCE per macro-step, the bound is on the macro-dt, without a substeps/stride factor). <= 0 (default) = no bound, bit-identical. Stays flat (a double, outside the homogeneous family). label name of the coupling (reason "coupled_source:<label>" of last_dt_bound). Stays flat (a string, outside the homogeneous family). When prog.freq_prog_ops/_args are non-empty, step_cfl / step_adaptive reduces the MAX of mu over the cells (global all_reduce_max) and bounds dt <= cfl / max(mu) (reason "coupled_source:<label>"). max(mu) <= 0 = no bound this step. Unknown blocks / roles, an exceeded capacity or a malformed program raise an EXPLICIT error (before any step). Without a call, the default path stays BIT-IDENTICAL.
◆ add_dt_bound()
| void pops::System::add_dt_bound | ( | const std::string & | label, |
| std::function< double()> | fn | ||
| ) |
Adds a GLOBAL time-step bound, evaluated ONCE per step (host) by step_cfl / step_adaptive: dt <= fn() when fn() > 0 and finite (otherwise the bound does not constrain this step).
It is the hook for NON cell-local constraints: multi-block coupling, Schur/Poisson stage, AMR/scheduler, or a user policy (startup ramp...). label names the bound in last_dt_bound() ("global:<label>"). A Python callback is acceptable HERE (one evaluation per step, never per cell).
◆ add_dynamic_block()
| void pops::System::add_dynamic_block | ( | const std::string & | name, |
| const std::string & | so_path, | ||
| int | substeps = 1, |
||
| const std::vector< std::string > & | names = {}, |
||
| const std::string & | recon = "none" |
||
| ) |
Adds a block whose model is LOADED AT RUNTIME from a shared library (.so) generated by the DSL (emit_cpp_brick -> ModelAdapter -> extern "C" factory).
The .so must expose pops_model_nvars(), pops_make_model() (returns an IModel<NV>*) and pops_destroy_model(void*). HOST PATH (virtual dispatch, global periodic Rusanov a_max, explicit Euler): to prototype a brand-new model, written as formulas on the Python side, without recompiling the core. cf. dynamic_model.hpp.
- Parameters
-
names variable names (introspection); default u0..u{NV-1}. recon MUSCL reconstruction of the face states (conservative): "none" (order 1) | "minmod" | "vanleer" (order 2, TVD). The FLUX choice (HLLC/Roe) stays on the compiled path.
◆ add_ionization()
| void pops::System::add_ionization | ( | const std::string & | electron, |
| const std::string & | ion, | ||
| const std::string & | neutral, | ||
| double | rate | ||
| ) |
Adds an IONIZATION coupling (operator-split): rate k n_e n_g; a neutral becomes an ion and an electron.
Mass transferred from the neutral to the ion (n_i + n_g conserved). The three blocks must exist. First inter-species source brick (on the density, comp 0).
◆ add_native_block()
| void pops::System::add_native_block | ( | const std::string & | name, |
| const std::string & | so_path, | ||
| const std::string & | limiter = "minmod", |
||
| const std::string & | riemann = "rusanov", |
||
| const std::string & | recon = "conservative", |
||
| const std::string & | time = "explicit", |
||
| double | gamma = 1.4, |
||
| int | substeps = 1, |
||
| bool | evolve = true, |
||
| int | stride = 1, |
||
| double | positivity_floor = 0.0 |
||
| ) |
Adds a block whose model is compiled into a NATIVE LOADER .so generated by the DSL (dsl.compile_native / compile(backend="production")).
This is the PRODUCTION path: the loader inlines the header template pops::add_compiled_model<ProdModel>, which builds the closures on the REAL CONTEXT of the System (grid_context) and installs the block via install_block. The block then runs EXACTLY the native path of add_block (fill_boundary = MPI halos, assemble_rhs device), ZERO-COPY – unlike add_compiled_block (.so + marshaling of flat arrays, extern "C" host ABI). Since the inline loader calls out-of-line methods of this module (install_block /grid_context/ensure_aux_width, exported POPS_EXPORT), the boundary is NOT a flat ABI: loader and module MUST share the same C++ ABI. add_native_block reads the loader's ABI key (pops_native_abi_key) and COMPARES it to abi_key(); a mismatch raises an EXPLICIT error (no UB).
- Parameters
-
limiter "none" | "minmod" | "vanleer" | "weno5" (weno5: add_compiled_model reallocates the block state to block_n_ghost = 3 ghosts after install_block, like add_block) riemann "rusanov" | "hll" | "hllc" | "roe" recon "conservative" | "primitive" time "explicit" (SSPRK2) | "ssprk3" | "euler" | "imex" (the template marshals the explicit RK scheme down to the loader's make_block, parity with add_block) gamma adiabatic index of the block (set_density / inter-species couplings) stride block cadence (1 = every step, default; cf. add_block)
◆ add_thermal_exchange()
| void pops::System::add_thermal_exchange | ( | const std::string & | a, |
| const std::string & | b, | ||
| double | rate | ||
| ) |
Adds an inter-species THERMAL EXCHANGE (operator-split): heat flux k (T_a - T_b) on the energy, opposite on each species (total energy conserved); T = p/rho.
The two blocks must be compressible Euler (4 variables, energy equation).
◆ advance()
| void pops::System::advance | ( | double | dt, |
| int | nsteps | ||
| ) |
◆ alloc_scalar_field()
| POPS_EXPORT MultiFab pops::System::alloc_scalar_field | ( | int | n_comp, |
| int | n_ghost | ||
| ) |
A fresh scalar field co-distributed with the System mesh: block 0's BoxArray and DistributionMapping, n_comp components, n_ghost ghost layers, zero-initialized.
Scratch a compiled time Program allocates for a matrix-free Krylov solve (the residual / search-direction fields that feed cg_solve / bicgstab_solve via ProgramContext::laplacian); shares the block (ba, dm) so a per-cell kernel pairs it with the state and aux by local fab index.
Here is the caller graph for this function:◆ aux_field_component()
| std::vector< double > pops::System::aux_field_component | ( | int | comp | ) | const |
Reads a NAMED aux field (component comp >= kAuxNamedBase): valid cells of the aux channel, row-major n*n (cartesian) / nr*ntheta (polar).
Counterpart of potential() for a named component. Is 0 everywhere as long as no set_aux_field_component has written this component (the aux channel is initialized to zero and solve_fields never touches components >= kAuxNamedBase).
◆ block_gamma()
| double pops::System::block_gamma | ( | const std::string & | name | ) | const |
Adiabatic index (gamma) of the block, read by the inter-species couplings (collision, thermal exchange, T_e).
Equals the historical default 1.4 unless the block declares it (add_block: ModelSpec gamma; compiled / dynamic block: optional symbol pops_compiled_gamma of the .so ABI).
◆ block_max_speed()
| POPS_EXPORT Real pops::System::block_max_speed | ( | int | b, |
| const MultiFab & | U | ||
| ) | const |
The maximum |wave speed| of block b evaluated on U – the SAME per-block reduction step_cfl reads (BlockState::max_speed, the HasStabilitySpeed / max_wave_speed closure set at add_block time): a collective reduction over the block's cells.
A compiled time Program reads it (ProgramContext::max_wave_speed) to express its own dt bound (epic ADC-399 / ADC-417, spec s18). REUSES the block's wave-speed closure – it does not recompute the speed. POPS_EXPORT: resolved by the generated problem.so across the dlopen boundary, like the other seam accessors.
Here is the caller graph for this function:◆ block_names()
| std::vector< std::string > pops::System::block_names | ( | ) | const |
Block names, in the order of addition.
SINGLE SOURCE: the internal block registry, populated by ALL the addition paths (add_block / add_dynamic_block / add_compiled_block / install_block). An integrator written in Python iterates over it, so it must also see the blocks loaded from a .so (JIT / AOT); counting them by n_species() alone would skip them.
◆ block_neg_div_flux_into()
| POPS_EXPORT void pops::System::block_neg_div_flux_into | ( | int | b, |
| MultiFab & | U, | ||
| MultiFab & | R | ||
| ) |
R <- -div F(U) for block b – the SAME flux divergence as block_rhs_into but WITHOUT the model's default/composite source (Poisson frozen, ghosts filled identically).
The block's flux-only closure is the rhs_into path on SourceFreeModel<Model> (the zero-source adapter the IMEX explicit half-step already uses), so the flux / ghost / geometry handling is bit-identical – only the source is dropped (with limiter='none'; the HLL wave-speed cache – rejected on the aot/production backends compiled Programs use – is the only path where cached cell-center speeds differ from the per-face reconstruction). A compiled time Program's hyperbolic stage (ProgramContext::neg_div_flux_default_into) reads it so a Lie/Strang split assembles "flux but no source" without the default source leaking in (epic ADC-399 / ADC-425, spec criterion 17). FAILS LOUD (std::runtime_error) on a block whose path did not build the closure (the host .so prototype loader) – never a silent source leak. POPS_EXPORT: resolved by the generated problem.so across the dlopen boundary, like block_rhs_into.
Here is the caller graph for this function:◆ block_project()
| POPS_EXPORT void pops::System::block_project | ( | int | b, |
| MultiFab & | u | ||
| ) |
Apply block b's post-step positivity projection to u in place (ADC-177): U <- project(U,
aux) over the valid cells, the SAME closure the native per-step path runs (s.project).
A compiled time Program reaches it through ProgramContext::apply_projection (spec op 21). REUSES the block's own projection (set at add_block time); a block without a projection is a NO-OP (cost-free). POPS_EXPORT so a generated problem.so resolves it across the dlopen boundary.
Here is the caller graph for this function:◆ block_rhs_into()
| POPS_EXPORT void pops::System::block_rhs_into | ( | int | b, |
| MultiFab & | U, | ||
| MultiFab & | R | ||
| ) |
R <- -div F(U) + S(U, aux) for block b (the block's frozen-Poisson residual closure).
Here is the caller graph for this function:◆ block_source_into()
| POPS_EXPORT void pops::System::block_source_into | ( | int | b, |
| MultiFab & | U, | ||
| MultiFab & | R | ||
| ) |
R <- S(U, aux) for block b – the model's default/composite SOURCE only, WITHOUT the flux divergence (the exact MIRROR of block_neg_div_flux_into, which is flux without source).
Together they split block_rhs_into = -div F + S into its two halves (ADC-430, sibling of ADC-425). The block's source-only closure evaluates m.source per cell into R (the SAME source term assemble_rhs adds), with NO numerical-flux dispatch – so it is flux-template agnostic (unlike a zero-flux model adapter, which HLL/Roe would not zero) and bit-identical to the source term of rhs_into. A compiled time Program's source stage (ProgramContext::source_default_into) reads it so a Lie/Strang split assembles "the default source but no flux" – P.rhs(flux=False, sources with "default") – without the -div F base leaking in (epic ADC-399 / ADC-430, spec: rhs flux=False is source-only). FAILS LOUD (std::runtime_error) on a block whose path did not build the closure (the host .so prototype loader) – never a silent flux leak. POPS_EXPORT: resolved by the generated problem.so across the dlopen boundary, like block_neg_div_flux_into.
Here is the caller graph for this function:◆ block_state()
| POPS_EXPORT MultiFab & pops::System::block_state | ( | int | b | ) |
The conservative state MultiFab of block b (zero-copy, non-owning reference).
Here is the caller graph for this function:◆ cfl_min_dx()
| POPS_EXPORT Real pops::System::cfl_min_dx | ( | ) | const |
The MIN physical cell size of the grid (Cartesian min(dx, dy); polar min(dr, r_min*dtheta)) – the SAME hmin the native CFL uses (SystemStepper::cfl_grid_h).
A compiled time Program reads it (ProgramContext::hmin) to express its own dt bound (epic ADC-399 / ADC-417, spec s18). POPS_EXPORT: resolved by the generated problem.so across the dlopen boundary.
Here is the caller graph for this function:◆ density()
| std::vector< double > pops::System::density | ( | const std::string & | name | ) | const |
ny*nx row-major (j slow, i fast)
◆ density_global()
| std::vector< double > pops::System::density_global | ( | const std::string & | name | ) | const |
comp0, ny*nx global
◆ disable_profiling()
| void pops::System::disable_profiling | ( | ) |
◆ disc_mask()
| std::vector< double > pops::System::disc_mask | ( | ) | const |
- Returns
- the 0/1 cell-centered domain mask, ny*nx row-major (j slow, i fast). Without set_disc_domain, returns an ALL-ACTIVE mask (only 1.0): the transport sub-domain is the entire domain (default path). Diagnostic / contract verification.
◆ dt_hotspot()
| std::array< double, 3 > pops::System::dt_hotspot | ( | const std::string & | name | ) |
Diagnostic (ADC-182): {w, i, j} of the GLOBAL cell that dominates the transport CFL bound of the block – to locate a realizability erosion / a collapsing dt.
On demand, off the hot path (step/step_cfl unchanged).
◆ enable_profiling()
| void pops::System::enable_profiling | ( | ) |
◆ ensure_aux_width()
| POPS_EXPORT void pops::System::ensure_aux_width | ( | int | ncomp | ) |
Guarantees that the SHARED aux channel has at least ncomp components.
Called by add_compiled_model (cf. dsl_block.hpp) with aux_comps<Model> when adding a block that reads extra auxiliary fields. Reallocating preserves the ADDRESS of the System's aux (the already-installed block closures point to &aux), and re-applies B_z if it was supplied. POPS_EXPORT: called by add_compiled_model (header) -> must be exported for the loader .so.
Here is the caller graph for this function:◆ eval_rhs()
| std::vector< double > pops::System::eval_rhs | ( | const std::string & | name | ) |
-div F + S, size ncomp*n*n
◆ get_primitive_state()
| std::vector< double > pops::System::get_primitive_state | ( | const std::string & | name | ) |
Reads the CONSERVATIVE state of the block and converts it to PRIMITIVE variables via the model conversion (M.to_primitive).
- Returns
- a flat ncomp*n*n component-major array in the order of primitive_vars(name) (diagnostics: velocities, pressure). Exact round-trip with set_primitive_state.
◆ get_state()
| std::vector< double > pops::System::get_state | ( | const std::string & | name | ) |
U, ncomp*n*n (component-major)
◆ grid_context()
| POPS_EXPORT GridContext pops::System::grid_context | ( | ) |
◆ history_depth()
| POPS_EXPORT int pops::System::history_depth | ( | const std::string & | name | ) | const |
Ring depth (max lag + 1) of history name.
- Exceptions
-
if nameis unknown.
◆ history_global()
| POPS_EXPORT std::vector< double > pops::System::history_global | ( | const std::string & | name, |
| int | slot | ||
| ) | const |
GLOBAL (collective, MPI-safe) gather of slot slot (0 = current, k = k macro-steps back) of history name into a component-major buffer of size ncomp*ny*nx, EXACTLY like state_global (every rank fills its local boxes then all_reduce_sum).
All ranks MUST call it.
- Exceptions
-
if nameis unknown orslotis out of range. Reads the slot even before the first store (the checkpoint of a never-stored ring is its zero fill); the initialized flag is serialized separately.
◆ history_initialized()
| POPS_EXPORT bool pops::System::history_initialized | ( | const std::string & | name | ) | const |
Whether history name has been stored at least once (the cold-start fill already happened).
The facade records it so a restart restores the initialized state without a phantom re-fill.
- Exceptions
-
if nameis unknown.
◆ history_names()
| POPS_EXPORT std::vector< std::string > pops::System::history_names | ( | ) | const |
◆ history_ncomp()
| POPS_EXPORT int pops::System::history_ncomp | ( | const std::string & | name | ) | const |
Component count of the slots of history name (the block's ncomp).
- Exceptions
-
if unknown.
◆ install_block()
| POPS_EXPORT void pops::System::install_block | ( | const std::string & | name, |
| int | ncomp, | ||
| const VariableSet & | cons_vars, | ||
| const VariableSet & | prim_vars, | ||
| double | gamma, | ||
| BlockClosures | closures, | ||
| std::function< Real(const MultiFab &)> | max_speed, | ||
| std::function< void(const MultiFab &, MultiFab &)> | poisson_rhs, | ||
| int | substeps, | ||
| bool | evolve, | ||
| int | stride = 1 |
||
| ) |
Installs a block from already-built closures (cf.
add_compiled_model). The cons/prim descriptors carry the names AND the roles (M::conservative_vars()), used by inter-species couplings.
Here is the caller graph for this function:◆ install_program()
| POPS_EXPORT void pops::System::install_program | ( | const std::string & | so_path | ) |
Load a generated problem.so and install its compiled time Program.
dlopens so_path, checks its ABI key against this module (fail-loud on mismatch), and calls its pops_install_program(this), which wraps the System in a ProgramContext and installs the macro-step closure. The .so resolves the seam accessors above via the global scope (same self-promotion as the native loader). Mirrors add_native_block; the .so stays loaded for the process lifetime.
◆ install_program_step()
| POPS_EXPORT void pops::System::install_program_step | ( | std::function< void(double)> | step | ) |
Here is the caller graph for this function:◆ installed_program_hash()
| POPS_EXPORT std::string pops::System::installed_program_hash | ( | ) | const |
IR hash of the installed compiled Program (the string returned by the .so's pops_program_hash), or "" if no program is installed.
Recorded in the checkpoint (sim.checkpoint) so a restart against a DIFFERENT compiled Program is rejected fail-loud (the buffers / cadence would be meaningless).
◆ is_profiling()
| bool pops::System::is_profiling | ( | ) | const |
◆ last_dt_bound()
| std::string pops::System::last_dt_bound | ( | ) | const |
Name of the ACTIVE bound (the one that set dt) of the last step_cfl: "transport:<block>", "source_frequency:<block>", "stability_dt:<block>", "global:<label>", "degenerate" (no evolving block), or "" if no step_cfl has run.
Diagnostic of the step policy.
◆ local_boxes()
| std::vector< std::array< int, 4 > > pops::System::local_boxes | ( | const std::string & | name | ) | const |
(ilo,jlo,ihi,jhi) per local fab
◆ local_state()
| std::vector< double > pops::System::local_state | ( | const std::string & | name, |
| int | li | ||
| ) | const |
U of fab li, flat (ncomp*bny*bnx)
◆ macro_step()
| POPS_EXPORT int pops::System::macro_step | ( | ) | const |
MACRO-STEP counter (0-indexed; incremented by step / step_cfl / step_adaptive).
Necessary for checkpoint/restart: the stride cadence (hold-then-catch-up) depends on macro_step % stride, not only on the time t (audit 2026-06, IO v1). POPS_EXPORT: a scheduled (every(N)/hold) program .so calls it for the cadence decision, so it must be in the loader's flat ABI like the other seam accessors (grid_context / solve_fields_from_state); without it the held-schedule .so fails to dlopen ("symbol not found in flat namespace"), caught by the Spec 3 runtime e2e test.
Here is the caller graph for this function:◆ mass()
| double pops::System::mass | ( | const std::string & | name | ) | const |
◆ n_blocks()
| POPS_EXPORT int pops::System::n_blocks | ( | ) | const |
Number of blocks (species) installed.
Here is the caller graph for this function:◆ n_species()
| int pops::System::n_species | ( | ) | const |
◆ n_vars()
| int pops::System::n_vars | ( | const std::string & | name | ) | const |
◆ newton_report()
| SourceNewtonReport pops::System::newton_report | ( | const std::string & | name | ) | const |
◆ nx()
| int pops::System::nx | ( | ) | const |
◆ ny()
| int pops::System::ny | ( | ) | const |
Extent of the SLOW axis of the field (rows of the (ny, nx) row-major array returned by density / potential / get_state).
Cartesian: ny() == nx() == n (square, UNCHANGED). Polar (ring): ny() == ntheta (slow azimuthal axis) while nx() == nr (fast radial axis) – with nr != ntheta the field has nr*ntheta values, NOT nx()^2: it is this dimension that correctly sizes the numpy array on the bindings side (without it, a (nx, nx) reshape overflows the buffer when nr != ntheta).
◆ operator=()
◆ poisson_solver()
| std::string pops::System::poisson_solver | ( | ) | const |
Configured field (Poisson) solver token, e.g.
"geometric_mg" | "fft" | "fft_spectral" (the solver of the last set_poisson; default "geometric_mg"). Read by install_program for the Spec criterion-24 solver requirement check (a field operator that requires a named solver is rejected at install when the configured solver does not match) and exposed for introspection.
◆ potential()
| std::vector< double > pops::System::potential | ( | ) |
phi, ny*nx row-major (j slow, i fast)
◆ potential_global()
| std::vector< double > pops::System::potential_global | ( | ) |
phi, ny*nx global
◆ profile_report()
| std::string pops::System::profile_report | ( | ) | const |
◆ profiler()
| POPS_EXPORT runtime::program::Profiler & pops::System::profiler | ( | ) |
The System-owned Profiler (a non-owning reference; lives as long as the System).
A compiled time Program reaches it through ProgramContext::profile_node to time each Program node into the SAME table sim.profile_report() renders – so per-node scopes ("node:rhs2", ...) accumulate alongside the coarse "step" / "field_solve" phases. POPS_EXPORT: a generated problem.so resolves it across the dlopen boundary like the other ProgramContext seam accessors (block_state, grid_context).
Here is the caller graph for this function:◆ program_block_map()
| POPS_EXPORT const std::vector< int > & pops::System::program_block_map | ( | ) | const |
The installed program-index -> system-index map (empty = identity). Read by ProgramContext.
Here is the caller graph for this function:◆ program_cache()
| POPS_EXPORT runtime::program::CacheManager & pops::System::program_cache | ( | ) |
Here is the caller graph for this function:◆ program_cache_accumulated_dt()
| POPS_EXPORT double pops::System::program_cache_accumulated_dt | ( | int | node_id | ) | const |
The accumulated skipped dt of slot node_id (accumulate_dt policy). 0 if none.
◆ program_cache_global()
| POPS_EXPORT std::vector< double > pops::System::program_cache_global | ( | int | node_id | ) | const |
GLOBAL (collective, MPI-safe) gather of slot node_id's cached MultiFab into a component-major buffer of size ncomp*ny*nx, EXACTLY like state_global / history_global.
All ranks MUST call it.
- Exceptions
-
if node_idis absent.
◆ program_cache_last_update_step()
| POPS_EXPORT int pops::System::program_cache_last_update_step | ( | int | node_id | ) | const |
The macro step at slot node_id's last recompute.
- Exceptions
-
if absent.
◆ program_cache_name()
| POPS_EXPORT std::string pops::System::program_cache_name | ( | int | node_id | ) | const |
The scheduled node name of slot node_id ("fields_from_state"), or "node_<id>" if it was stored without one (the current nameless codegen).
Names a missing node verbatim at restart.
◆ program_cache_ncomp()
| POPS_EXPORT int pops::System::program_cache_ncomp | ( | int | node_id | ) | const |
The component count of slot node_id's cached value.
- Exceptions
-
if absent.
◆ program_cache_ngrow()
| POPS_EXPORT int pops::System::program_cache_ngrow | ( | int | node_id | ) | const |
The ghost-cell width of slot node_id's cached value (1 for the aux, the block-state width for a held scratch) – serialized so restore rebuilds with the same ngrow.
- Exceptions
-
if absent.
◆ program_cache_nodes()
| POPS_EXPORT std::vector< int > pops::System::program_cache_nodes | ( | ) | const |
◆ program_diagnostic()
| POPS_EXPORT Real pops::System::program_diagnostic | ( | const std::string & | name | ) | const |
The recorded value of diagnostic name.
- Exceptions
-
std::out_of_range if namewas never recorded (a typo / a diagnostic the installed program does not write fails loud, not 0).
◆ program_diagnostics()
| POPS_EXPORT std::map< std::string, Real > pops::System::program_diagnostics | ( | ) | const |
All recorded diagnostics (name -> last recorded value).
Empty when the program records none. Exposed to Python as sim.program_diagnostics() (a dict); program_diagnostic(name) reads one.
◆ program_params()
| POPS_EXPORT RuntimeParams pops::System::program_params | ( | int | prog_block | ) | const |
Block prog_block's CURRENT RuntimeParams (a device-clean by-value copy: trivially copyable, readable in a kernel).
An UNSEEDED block (no runtime param declared) returns a default-constructed RuntimeParams (count 0), so a kernel that reads no param is unaffected. Read by ProgramContext for the lowered per-cell source / linear-source kernels.
Here is the caller graph for this function:◆ read_history()
| POPS_EXPORT MultiFab & pops::System::read_history | ( | const std::string & | name, |
| int | lag | ||
| ) |
The history slot lag macro-steps back (lag 0 = the current slot, lag 1 = the previous step's stored value, ...).
- Exceptions
-
if nameis unknown,lagexceeds the registered depth, or the history has not been stored yet ("history '<name>' with lag=<lag> was requested but not initialized") – a read before the first store is a fail-loud configuration error (spec error 17).
Here is the caller graph for this function:◆ record_program_diagnostic()
| POPS_EXPORT void pops::System::record_program_diagnostic | ( | const std::string & | name, |
| Real | value | ||
| ) |
Here is the caller graph for this function:◆ register_elliptic_field()
| POPS_EXPORT void pops::System::register_elliptic_field | ( | const std::string & | field, |
| int | phi_comp, | ||
| int | gx_comp, | ||
| int | gy_comp | ||
| ) |
Register named field's aux output components (where its solved phi / centered grad land).
Called by the native loader for each m.elliptic_field once the block is installed. gx_comp / gy_comp < 0 => only phi is written (the field declared fewer than 3 aux slots).
◆ register_history()
| POPS_EXPORT MultiFab & pops::System::register_history | ( | const std::string & | name, |
| int | lag | ||
| ) |
Here is the caller graph for this function:◆ reset_profiling()
| void pops::System::reset_profiling | ( | ) |
◆ restore_history()
| POPS_EXPORT void pops::System::restore_history | ( | const std::string & | name, |
| int | slot, | ||
| const std::vector< double > & | values | ||
| ) |
RESTORE (restart) slot slot of history name from a GLOBAL component-major buffer (same layout as history_global / set_state): the owner rank writes its box, the others are no-ops (MPI-safe, all ranks call it).
Registers the ring (depth = max(slot)+1) if name is unknown yet, so the restart rebuilds the rings the program will re-register on its first step.
- Exceptions
-
on a size mismatch.
◆ restore_program_cache()
| POPS_EXPORT void pops::System::restore_program_cache | ( | int | node_id, |
| int | ncomp, | ||
| int | ngrow, | ||
| int | last_update_step, | ||
| double | accumulated_dt, | ||
| const std::string & | name, | ||
| const std::vector< double > & | values | ||
| ) |
RESTORE (restart) slot node_id from a GLOBAL component-major buffer (same layout as program_cache_global / set_state): allocate a value MultiFab co-distributed with block 0 (ncomp components), scatter the buffer into it (owner rank writes, others no-op – MPI-safe, all ranks call it), and re-key the slot with its bookkeeping (name may be empty).
- Exceptions
-
if no block exists yet (the cache value is co-distributed with block 0's storage).
◆ rotate_histories()
| POPS_EXPORT void pops::System::rotate_histories | ( | ) |
Shift every history ring buffer one step (slot k <- slot k-1, for k = depth-1 .
. 1), called ONCE at the end of each macro-step (the generated step body emits ctx.rotate_histories() last). The current slot [0] is recycled (it gets the oldest buffer; the next store overwrites it before any read). O(1) handle swaps – no deep copy. No-op when no history exists.
Here is the caller graph for this function:◆ seed_program_params()
| POPS_EXPORT void pops::System::seed_program_params | ( | int | prog_block, |
| const std::vector< double > & | defaults | ||
| ) |
Seed block prog_block's RuntimeParams to its DECLARATION defaults (count values, the .so pops_program_param_default metadata), establishing the no-set baseline.
Called by install_program once per runtime-param Program block; a later set_program_params overwrites only the supplied values. Idempotent (re-seeding resets to defaults).
◆ set_aux_field_component()
| void pops::System::set_aux_field_component | ( | int | comp, |
| const std::vector< double > & | field | ||
| ) |
Sets a NAMED aux field (ADC-70 phase 1) on the canonical component comp (>= kAuxNamedBase = 5), row-major n*n (cartesian) / nr*ntheta (polar) array.
The System does NOT know the names: the FACADE (pops.System.set_aux_field) resolves name -> comp via the block's table (from CompiledModel.aux_extra_names) and calls this. PERSISTENT STATIC field: stored (re-applied after a channel reallocation) and populated right away if the channel is wide enough.
- Exceptions
-
if comp < kAuxNamedBase (components reserved for phi/grad/B_z/T_e: dedicated paths), if the size does not match the grid, or if no block declares a field at this index (channel too narrow).
◆ set_aux_field_halo_component()
| void pops::System::set_aux_field_halo_component | ( | int | comp, |
| int | bc_type, | ||
| double | value | ||
| ) |
Declares a per-field aux HALO policy (ADC-369) for the NAMED component comp (>= kAuxNamedBase): bc_type is pops::BCType (Foextrap=1 / Dirichlet=2), value the Dirichlet boundary value (ignored for Foextrap).
Applied by solve_fields AFTER the shared aux ghost fill, overriding only this component's PHYSICAL-face ghosts (periodic faces – periodic domain, polar theta – keep their wrap). The FACADE (pops.System.set_aux_field(..., halo=pops.AuxHalo(...))) resolves name -> comp and calls this. No policy declared -> the shared aux BC, bit-identical.
- Exceptions
-
on a reserved/too-narrow component or an unsupported type.
◆ set_block_conversion()
| POPS_EXPORT void pops::System::set_block_conversion | ( | const std::string & | name, |
| CellConvert | prim_to_cons, | ||
| CellConvert | cons_to_prim | ||
| ) |
Installs the pointwise cons <-> prim conversions of a block (after install_block).
Called by the header template add_compiled_model (compiled model); the native path add_block and the dynamic .so path set them directly. POPS_EXPORT: resolved by the native loader through dlopen.
Here is the caller graph for this function:◆ set_block_dt_bounds()
| POPS_EXPORT void pops::System::set_block_dt_bounds | ( | const std::string & | name, |
| std::function< Real(const MultiFab &)> | source_frequency, | ||
| std::function< Real(const MultiFab &)> | stability_dt | ||
| ) |
Installs the optional STEP BOUNDS of a block (after install_block): reduction of the max source frequency (HasSourceFrequency trait, bound dt <= cfl*substeps/(stride*mu)) and of the min admissible step (HasStabilityDt trait, bound dt <= dt_adm*substeps/stride, without cfl).
EMPTY functions = the block imposes no bound (historical step policy, bit-identical). Called by add_block and by the template add_compiled_model (cf. dsl_block.hpp) with the compiled closures of block_builder (make_source_frequency / make_stability_dt). POPS_EXPORT: resolved by the native loader through dlopen.
Here is the caller graph for this function:◆ set_block_elliptic_field()
| POPS_EXPORT void pops::System::set_block_elliptic_field | ( | const std::string & | block_name, |
| const std::string & | field, | ||
| std::function< void(const MultiFab &, MultiFab &)> | rhs | ||
| ) |
Attach named field's RHS closure (+= elliptic_field_rhs(U)) to block block_name.
Called by the native loader (make_poisson_rhs of the per-field brick).
- Exceptions
-
if the block is unknown.
◆ set_block_ghosts()
| POPS_EXPORT void pops::System::set_block_ghosts | ( | const std::string & | name, |
| int | n_ghost | ||
| ) |
Guarantees that the state U of block name carries at least n_ghost ghosts (width of the spatial stencil).
WENO5 reads 3 ghosts, > the 2 allocated by install_block; called by add_compiled_model (header) with block_n_ghost(limiter) AFTER install_block, so the native compiled path (loader .so) accepts weno5 – SAME mechanism as add_block. No-op if U already has enough ghosts (none/minmod/vanleer, <= 2): allocation and data bit-identical to history. POPS_EXPORT: called by the header template add_compiled_model -> must be exported for the loader .so.
Here is the caller graph for this function:◆ set_block_params()
| void pops::System::set_block_params | ( | const std::string & | name, |
| const std::vector< double > & | values | ||
| ) |
Changes the values of the RUNTIME parameters of an AOT block (add_compiled_block) WITHOUT recompiling the .so (P7-b).
values is the COMPLETE block of values (order = sorted order of the names on the DSL side, cf. CompiledModel.runtime_param_names). The block must have been added via add_compiled_block AND declare at least one runtime parameter (dsl.Param(..., kind="runtime")); otherwise explicit error (a silent set_param on a block without a runtime param would mask a bug). Effect on the next step (the block closures read the SHARED block of values).
- Exceptions
-
std::runtime_error if the block is unknown, has no runtime params, or if valuesdoes not have the right length.
◆ set_clock()
| void pops::System::set_clock | ( | double | t, |
| int | macro_step | ||
| ) |
RESTORES the clock (t, macro_step) – reserved for the RESTART (sim.restart).
Restoring macro_step is MANDATORY to resume the stride cadence exactly; a restart that would only restore t would desynchronize the blocks at stride > 1.
- Exceptions
-
if macro_step < 0.
◆ set_density()
| void pops::System::set_density | ( | const std::string & | name, |
| const std::vector< double > & | rho | ||
| ) |
Sets the density of a species (component 0), n*n row-major array.
The other components (momentum, energy) are set to the at-rest equilibrium.
◆ set_disc_domain()
| void pops::System::set_disc_domain | ( | double | cx, |
| double | cy, | ||
| double | R, | ||
| const std::string & | mode = "none" |
||
| ) |
Sets the TRANSPORT DOMAIN as a DISC centered at (cx, cy) with radius R (T2 work, CONTRACT inert by default).
Materializes a 0/1 cell-centered mask (cell active when its center is inside the disc, level set hypot(x-cx, y-cy) - R < 0, SAME convention as the conducting wall of the Poisson). It is the FV counterpart of the elliptic wall: it lets the FV transport act on the true disc instead of the full cartesian square (otherwise the circle lives only in the Poisson wall – the "cartesian ring edges" lock, cf. docs/HOFFART_FIDELITY.md). The mask makes possible a CONSERVATIVE mask-aware transport (zero normal flux at active/inactive faces).
DISC TRANSPORT MODE (T5-PR3 work, mode): dispatches the transport advance of step() to the corresponding disc operator. Default "none" -> full cartesian path (assemble_rhs), BIT- IDENTICAL to history even after set_disc_domain (the mask is materialized but transport ignores it while the mode is "none"). "staircase" -> conservative masked transport (assemble_rhs_ masked, 0/1 face gate, jagged boundary). "cutcell" -> cut-cell / embedded-boundary transport (assemble_rhs_eb, alpha_f apertures + kappa volume fraction, smooth boundary, order 2 interior). The mode is honored under Lie AND Strang (cf. set_time_scheme). A mode != "none" without a transportable cartesian block raises an EXPLICIT error at the step (never a silent full transport). Unknown mode -> error. R > 0 required; cartesian only (polar already bounds the ring by its radial walls -> explicit error).
◆ set_electron_temperature_from()
| void pops::System::set_electron_temperature_from | ( | const std::string & | name | ) |
Designates a COMPRESSIBLE fluid block (4 var) as the source of the electron temperature T_e: the T_e aux channel (next canonical component) is filled with T = p/rho of this block, RECOMPUTED at each solve_fields.
Has effect only if a block declares it reads T_e (n_aux > 4); otherwise stored and inert. It is the second EXTRA aux field (after B_z), populated by DERIVATION from a block (and not supplied by the user as B_z is).
◆ set_epsilon_anisotropic_field()
| void pops::System::set_epsilon_anisotropic_field | ( | const std::vector< double > & | eps_x, |
| const std::vector< double > & | eps_y | ||
| ) |
Sets an ANISOTROPIC permittivity eps_x(x), eps_y(x), two n*n row-major fields (> 0), at the CENTER of the cells.
The system Poisson operator becomes div(diag(eps_x, eps_y) grad phi) = f: faces normal to x carry eps_x, those normal to y carry eps_y (harmonic face coefficients, order 2), CARRIED BY THE OPERATOR without 1/eps scaling of the right-hand side. eps_x == eps_y gives back the isotropic operator div(eps grad phi). Only 'geometric_mg' supports it; requesting it with 'fft' (constant coefficient) raises an error. Call before solve_fields.
◆ set_epsilon_field()
| void pops::System::set_epsilon_field | ( | const std::vector< double > & | eps | ) |
Sets a VARIABLE permittivity eps(x), n*n row-major field (> 0), at the cell CENTER.
The system Poisson operator becomes div(eps grad phi) = f, eps CARRIED BY THE OPERATOR (harmonic face coefficient, order 2) without 1/eps scaling of the right-hand side. Only the 'geometric_mg' solver supports it; requesting it with 'fft' (constant coefficient) raises an error. Takes precedence over the constant permittivity of set_poisson. Call before solve_fields.
◆ set_gauss_policy()
| void pops::System::set_gauss_policy | ( | const std::string & | policy | ) |
Gauss-law policy.
"restart" (default): solve_fields re-solves -Delta phi = f at each step (BIT-IDENTICAL to history). "evolve": after the first solve (phi^0), solve_fields no longer re-solves the Poisson and derives the aux from the CURRENT phi that the Schur source stage evolves in-place -> -Delta phi evolution without restart (Gauss imposed only at t=0). Has effect only with a condensed source stage. Unknown scheme -> explicit error.
◆ set_geometry_mode()
| void pops::System::set_geometry_mode | ( | const std::string & | mode | ) |
Sets ONLY the disc transport mode (without (re)defining the disc): "none" | "staircase" | "cutcell".
Useful to toggle the mode after set_disc_domain, or to reset it to "none" (back to the full cartesian path, bit-identical). Requesting a mode != "none" without a defined disc (set_disc_domain) raises an EXPLICIT error (the mode alone has no geometry to apply).
◆ set_history_initialized()
| POPS_EXPORT void pops::System::set_history_initialized | ( | const std::string & | name, |
| bool | initialized | ||
| ) |
Mark history name initialized (or not) after a restart: a restored, already-stored ring must read at lag without a phantom cold-start re-fill on its first post-restart store.
- Exceptions
-
if nameis unknown (restore its slots first).
◆ set_magnetic_field()
| void pops::System::set_magnetic_field | ( | const std::vector< double > & | bz | ) |
Sets an out-of-plane magnetic field B_z(x, y) SHARED by the blocks, n*n row-major.
Populates the extra aux component (B_z channel) read by the models that declare it (n_aux > 3); inert if no block reads B_z (aux channel stays at base width). B_z is static (external to the elliptic): derive_aux does not touch it. Call after having added the block (or before: the value is kept and applied when the aux channel widens).
◆ set_poisson()
| void pops::System::set_poisson | ( | const std::string & | rhs = "charge_density", |
| const std::string & | solver = "geometric_mg", |
||
| const std::string & | bc = "auto", |
||
| const std::string & | wall = "none", |
||
| double | wall_radius = 0.0, |
||
| double | epsilon = 1.0, |
||
| double | abs_tol = 0.0 |
||
| ) |
Configures the shared Poisson.
- Parameters
-
rhs only mode: "charge_density", f = sum_s elliptic_rhs_s(u_s) solver "geometric_mg" (any case, wall included) | "fft" (periodic, n = 2^k) bc "auto" | "periodic" | "dirichlet" | "neumann" wall "none" | "circle": conducting wall at (L/2, L/2), radius wall_radius epsilon CONSTANT permittivity of the operator div(eps grad phi) = f. eps != 1 solves eps lap phi = f (i.e. lap phi = f/eps). For a VARIABLE permittivity eps(x), cf. set_epsilon_field (variable-coefficient operator, GeometricMG). abs_tol ABSOLUTE floor of the stopping criterion of the GeometricMG V-cycle (same units as the residual). Default 0: purely relative criterion, historical behavior unchanged. Set > 0 (problem scale), it makes solve_fields exit without cycling OUT OF STEP on an already-converged state. No effect on the FFT solver (direct).
◆ set_potential()
| void pops::System::set_potential | ( | const std::vector< double > & | phi | ) |
RESTORES the potential phi (IO v1, reserved for restart): without it the multigrid would restart from a blank phi and the resume would not be bit-identical (warm start lost); in gauss_policy="evolve", phi IS the physical state and its restoration is indispensable.
Field ny*nx row-major (same layout as potential()).
◆ set_primitive_state()
| void pops::System::set_primitive_state | ( | const std::string & | name, |
| const std::vector< double > & | prim | ||
| ) |
Initializes the state of a block from its PRIMITIVE variables (rho, u, v, p ...): prim is a flat ncomp*n*n component-major array in the order of primitive_vars(name).
Each cell is converted to CONSERVATIVE variables by the block's MODEL conversion (M.to_conservative), then written into the state. Ergonomic counterpart of set_density for a model with several primitives (compressible 4 var: p; isothermal 3 var; scalar 1 var: identity). cf. get_primitive_state.
◆ set_program_block_map()
| POPS_EXPORT void pops::System::set_program_block_map | ( | const std::vector< int > & | prog_to_sys | ) |
◆ set_program_cadence()
| POPS_EXPORT void pops::System::set_program_cadence | ( | int | substeps, |
| int | stride | ||
| ) |
Set the compiled-Program macro-step cadence (ADC-411): SYSTEM-level substeps and stride around the installed program closure (cf.
SystemStepper::step). substeps subdivides each effective step into substeps calls program_step_(eff_dt/substeps); stride runs the whole program once per stride macro-steps with eff_dt = stride*dt (GLOBAL hold-then-catch-up, the clock still ticks every macro-step). Both must be >= 1 (throws std::invalid_argument otherwise). Default 1/1 -> byte-identical to a single program_step_(dt) call. Kept SEPARATE from install_program so the generated .so ABI is untouched (the cadence is runtime metadata). NOTE: substeps > 1 is bit-exact vs native substeps ONLY for an UNCOUPLED / transport-only program (program_step_ re-runs the whole program, solve_fields included); stride is GLOBAL (whole-system), equal to native per-block stride only for a single-block system. See SystemStepper::step.
◆ set_program_params()
| POPS_EXPORT void pops::System::set_program_params | ( | int | prog_block, |
| const std::vector< double > & | values | ||
| ) |
◆ set_reaction_field()
| void pops::System::set_reaction_field | ( | const std::vector< double > & | kappa | ) |
Enables a REACTION term kappa(x) >= 0: the system Poisson operator goes from div(eps grad phi) = f to div(eps grad phi) - kappa phi = f (SCREENED Poisson / Helmholtz; kappa = 1/lambda_D^2 for Debye screening).
n*n row-major field, carried by the operator GeometricMG (diagonal kappa, restricted to coarse levels). Only 'geometric_mg' supports it (error with 'fft'). Composable with set_epsilon_field. kappa = 0 everywhere => Poisson unchanged.
◆ set_source_stage()
| void pops::System::set_source_stage | ( | const std::string & | name, |
| const std::string & | kind, | ||
| double | theta, | ||
| double | alpha, | ||
| const SourceStageOptions & | opts = {} |
||
| ) |
Enables a Schur-condensed SOURCE STAGE on block name (EXPLICIT / IMPLICIT splitting, cf.
docs/SCHUR_CONDENSATION_DESIGN.md sections 5-6). It is the OPT-IN of the pops.Split( hyperbolic=Explicit, source=CondensedSchur) policy: at each step, the block does its EXPLICIT hyperbolic transport as today, THEN this source stage replaces the explicit / IMEX source by the condensed stage (CondensedSchurSourceStepper, #126), AUTONOMOUS and solved in C++ (no per-cell Python callback). The default path (without this call) stays BIT-IDENTICAL.
CONTRACT (validated HERE, before any step): the block MUST expose the Density / MomentumX / MomentumY roles (Energy optional) in its conservative descriptor; a missing role raises an EXPLICIT error. The B_z field must be available (set_magnetic_field called): the aux is widened to the B_z channel and an absent B_z raises an error. The block reuses the phi potential of the system Poisson as a warm start (solve_fields runs at the head of step), but the source stage solves its OWN condensed elliptic operator (it does not duplicate solve_fields).
- Parameters
-
kind only "electrostatic_lorentz" for now (ElectrostaticLorentzCondensation). theta theta-scheme in (0,1] (0.5 = Crank-Nicolson, 1 = backward Euler). alpha electrostatic coupling constant of the source sub-system (d_t(-Lap phi) = -alpha div(rho v)). opts settings grouped in a POD (ADC-214; cf. SourceStageOptions): tolerance / budget of the Krylov solve (krylov_tol / krylov_max_iters, <= 0 = historical defaults 1e-10; 400 cartesian, 600 polar), field DESCRIPTORS (density / momentum_x / momentum_y / energy; "" = canonical role, otherwise stable role name or variable name of the block; energy == "none" disables the energy; CARTESIAN only for an override, the polar stepper rejects it), and bz_aux_component (< 0 = canonical B_z channel). Default {} = historical bit-identical behavior. These fields were a long list of four adjacent std::stringinterchangeable at the call site.
◆ set_state()
| void pops::System::set_state | ( | const std::string & | name, |
| const std::vector< double > & | u | ||
| ) |
◆ set_time_scheme()
| void pops::System::set_time_scheme | ( | const std::string & | scheme | ) |
Time SPLITTING POLICY of the macro-step (hyperbolic transport H + source stage S):
- "lie" (default): H(dt); S(dt) once (Godunov, 1st order). BIT-IDENTICAL to history.
- "strang": H(dt/2); S(dt); H(dt/2) (symmetric, 2nd order as soon as H and S are). The Strang scheme RE-SOLVES solve_fields between the stages so that each half-advance reads a phi consistent with the current density (the SINGLE head solve_fields, sufficient for Lie, does not suffice for the 2nd Strang half-advance); cf. docs/HOFFART_STEP_SEQUENCE.md and SystemStepper::step_strang. Reuses the SAME bricks (s.advance, source stage): no new stepper. An unknown scheme raises an EXPLICIT error. Without a call, the path stays BIT-IDENTICAL (Lie).
◆ solve_fields()
| POPS_EXPORT void pops::System::solve_fields | ( | ) |
solves Poisson then derives aux = (phi, grad phi); exported
so a compiled program .so resolves it via ProgramContext (the other seam accessors below are likewise POPS_EXPORT)
Here is the caller graph for this function:◆ solve_fields_from_blocks()
| POPS_EXPORT void pops::System::solve_fields_from_blocks | ( | const std::vector< const MultiFab * > & | U_stages | ) |
Coupled multi-block field solve (Spec 3 criterion 24, ADC-457): SAME elliptic solve + aux derivation as solve_fields(), but the system Poisson RHS is assembled from the SIMULTANEOUS stage states of MULTIPLE blocks at once – every coupled block reads its OWN stage state, not a single- target override.
U_stages is indexed BY BLOCK INDEX (its size must equal n_blocks()); entry b != nullptr -> block b contributes its stage state, entry b == nullptr -> block b contributes its live state. With every entry pointing at the corresponding live state it is bit-identical to solve_fields(). The codegen lowers P.solve_fields_from_blocks([...]) to this – the seam a multi- species field-coupled step uses (the IR commit_many guarantee: no operator observes a partially committed group). POPS_EXPORT: resolved by a compiled program .so (ProgramContext) across the dlopen boundary.
- Exceptions
-
std::invalid_argument if U_stagesis not sized to n_blocks().
Here is the caller graph for this function:◆ solve_fields_from_state() [1/2]
| POPS_EXPORT void pops::System::solve_fields_from_state | ( | const std::string & | field, |
| int | block_idx, | ||
| const MultiFab & | U_stage | ||
| ) |
◆ solve_fields_from_state() [2/2]
| POPS_EXPORT void pops::System::solve_fields_from_state | ( | int | block_idx, |
| const MultiFab & | U_stage | ||
| ) |
Per-stage field solve (ADC-409): SAME elliptic solve + aux derivation as solve_fields(), but block block_idx assembles its Poisson RHS from U_stage instead of its live state (the other blocks keep theirs).
This re-fills the SHARED aux with phi(U_stage) so a field-coupled multi-stage compiled Program can re-solve the fields from each STAGE state – the stages run sequentially, so stage k's RHS (called right after this) reads phi from stage k's own state before the next stage overwrites the aux. With block_idx 0 and U_stage = U^n (the first stage) it is identical to solve_fields(). POPS_EXPORT: resolved by a compiled program .so (ProgramContext) across the dlopen boundary.
- Exceptions
-
std::out_of_range if block_idxis not a valid block.
Here is the caller graph for this function:◆ state_global()
| std::vector< double > pops::System::state_global | ( | const std::string & | name | ) | const |
U, ncomp*ny*nx global.
◆ step()
| void pops::System::step | ( | double | dt | ) |
solve_fields, then advances each block according to its scheme
◆ step_adaptive()
| double pops::System::step_adaptive | ( | double | cfl | ) |
Advances one MULTIRATE macro-step: the slowest block sets the macro-step, each block that is faster is sub-cycled n = ceil(w_block / w_min) times.
- Returns
- the macro-step.
◆ step_cfl()
| double pops::System::step_cfl | ( | double | cfl | ) |
Advances one step at dt = cfl * h / max wave speed of the system.
- Returns
- the dt used.
◆ store_history()
| POPS_EXPORT void pops::System::store_history | ( | const std::string & | name, |
| const MultiFab & | value | ||
| ) |
Copy value (valid cells) into the CURRENT slot [0] of history name and mark it initialized.
On the FIRST store the value is also broadcast into EVERY deeper slot (the cold-start fill: a multistep scheme's step 0 then reads the same value at every lag, degenerating to a one-step method – deterministic and machine-precision reproducible).
- Exceptions
-
if nameis unknown. The caller is responsible for layout compatibility: the ring slots share the block's (ba, dm, ncomp), so a value built from the same block matches (lincomb is a valid-cell copy, no layout check).
Here is the caller graph for this function:◆ time()
| double pops::System::time | ( | ) | const |
◆ variable_names()
| std::vector< std::string > pops::System::variable_names | ( | const std::string & | name, |
| const std::string & | kind = "conservative" |
||
| ) | const |
Variable names of a block (introspection): kind = "conservative" | "primitive".
◆ variable_roles()
| std::vector< std::string > pops::System::variable_roles | ( | const std::string & | name, |
| const std::string & | kind = "conservative" |
||
| ) | const |
PHYSICAL roles of the variables of a block (parallel to variable_names): "density", "momentum_x", "energy", ... or "custom" if the block does not provide its roles.
This is what the inter-species couplings resolve (index_of(role)) instead of a literal index.
The documentation for this class was generated from the following file:
- include/pops/runtime/system.hpp
Generated by