zoomy_core.model.splitter

zoomy_core.model.splitter#

Predictor / pressure / corrector splitter for non-hydrostatic SystemModels.

=========================================================================

CANONICAL SPLITTER: split_for_pressure_structural

This is the ONE generic, supported pressure splitter — use it for every non-hydrostatic model (VAM, ML-VAM, …). It is what model.chorin_split(dt) calls (see VAM.chorin_split / MLVAM.chorin_split). It is “structural”: it discovers the row roles purely from the OPERATORS — the divergence constraints are exactly the rows with an identically-zero mass-matrix row, the rest are evolution rows — so it needs NO equation_names convention and works for any level / dimension / basis straight out of a declarative derivation.

>>> split = VAM(level=1).chorin_split(dt)      # (SM_pred, SM_press, SM_corr)
>>> split = split_for_pressure_structural(sm, P_syms, dt)   # explicit form

DEPRECATED — do NOT use for new work (each raises a DeprecationWarning):

  • split_for_pressure — old chain-DAE splitter; dispatches rows by the fragile equation_names convention (mass / xmom_j* / zmom_j* / cont_j*). A declarative SystemModel carries no such names.

  • split_simple — minimal hand-rolled splitter; used only by the hand-built escalante_vam_bump/run.py oracle (also name-based).

  • build_pressure_elliptic_block — internal helper for the two deprecated splitters; the structural splitter does NOT use it.

The deprecated three are kept only so the legacy hand-built reference cases keep running; new models and agents must go through chorin_split / split_for_pressure_structural.

Module Contents#

Classes#

PressureLinearOperator

Explicit coefficient fields (A0, first_derivative, second_derivative, RHS) of the affine pressure operator SM_press.source, as printable sympy expressions.

Stage

One stage of a split solver’s march (REQ-173).

SplitForPressureResult

Three sub-SystemModels produced by split_for_pressure.

Functions#

build_pressure_elliptic_block

Build the pressure-stage elliptic block for SM_press.

verify_p_linearity

Verify each row is linear in (P_l, ∂_x P_l, ∂_xx P_l).

build_pressure_linear_operator

Extract the explicit affine pressure operator from a Chorin SM_press (see PressureLinearOperator).

split_for_pressure

DEPRECATED — use split_for_pressure_structural (via model.chorin_split(dt)).

split_for_pressure_structural

Structural predictor / pressure / corrector splitter — no equation-name conventions.

split_simple

DEPRECATED — use split_for_pressure_structural (via model.chorin_split(dt)).

API#

zoomy_core.model.splitter.build_pressure_elliptic_block(sm, pressure_vars: Sequence, dt: sympy.Symbol, *, bottom=None)#

Build the pressure-stage elliptic block for SM_press.

Parameters

  • sm (SystemModel): Chain-DAE SystemModel (e.g. SystemModel.from_model( VAMModelGalerkin(level=1))). Must carry equation_names with the canonical mass / xmom_j / zmom_j / cont_j layout.

  • pressure_vars (Sequence): Pressure mode state Symbols (e.g. [P_0, P_1] for VAM(1,2,2)). Internally converted to Function form to match the residual representation produced by SystemModel.reconstruct_residuals.

  • dt (sp.Symbol): Symbolic time-step.

  • bottom (optional): Bottom-topography Function b(x). If None, located by scanning equation residuals for a Function named b.

Returns

  • dict with keys rows, U_tilde, W_tilde, T_u, T_w,

  • U_corr, W_corr, pressure_vars, h, bottom, t,

  • x. All entries are in Function form (coordinate-dependent).

zoomy_core.model.splitter.verify_p_linearity(rows, pressure_vars, x)#

Verify each row is linear in (P_l, ∂_x P_l, ∂_xx P_l).

class zoomy_core.model.splitter.PressureLinearOperator#

Explicit coefficient fields (A0, first_derivative, second_derivative, RHS) of the affine pressure operator SM_press.source, as printable sympy expressions.

Produced lazily by SystemModel.pressure_operator by differentiating the already-split source (no re-derivation); Ax/Ay/Az and Axx/Ayy/Azz/Axy/Axz/Ayz are accessors (None when the axis is absent).

P_modes: list#

None

A0: list#

None

first_derivative: dict#

None

second_derivative: dict#

None

RHS: list#

None

property Ax#
property Ay#
property Az#
property Axx#
property Ayy#
property Azz#
property Axy#
property Axz#
property Ayz#
zoomy_core.model.splitter.build_pressure_linear_operator(sm_press)#

Extract the explicit affine pressure operator from a Chorin SM_press (see PressureLinearOperator).

Mechanical — no re-derivation. SM_press.source is differentiated w.r.t. the bare pressure symbols (→ A0) and w.r.t. the pressure-derivative aux symbols, which the existing aux metadata (aux_registry / aux_input_registry entries whose state_index is a pressure slot) sorts into first / second-derivative coefficient matrices by their multi_index. Asserts source is affine in P and its pressure derivatives (raises clearly otherwise).

General over any chorin-split model (VAM any level, ML_VAM), any number of pressure modes NP, and any dimension (2-D / 3-D).

class zoomy_core.model.splitter.Stage#

Bases: typing.NamedTuple

One stage of a split solver’s march (REQ-173).

A split solver is supplied a list of stages and marches over it; a backend supplies one executor per stage kind.

  • label — a STABLE, deterministic per-stage identifier ("predictor" / "pressure" / "corrector" for the Chorin split). Code-generating backends key generated names off it (amrex emits Model_<label>.H); it MUST NOT be derived from the list position, or any reordering silently renames every header and invalidates every build cache.

  • kind — the executor kind: "hyperbolic" | "elliptic" | "pointwise" (extensible). It selects which per-kind executor the backend runs for this stage.

  • sm — the SystemModel for the stage.

label: str#

None

kind: str#

None

sm: object#

None

class zoomy_core.model.splitter.SplitForPressureResult#

Three sub-SystemModels produced by split_for_pressure.

For VAM(1, 2, 2) on Q = [h, U_0, U_1, W_0, W_1, P_0, P_1]:

  • SM_pred — predictor: 5 evolution rows (mass + 2 xmom + 2 zmom) updating Q[0..4] = h, U_k, W_k with P_k frozen at P_k^n.

  • SM_press — pressure stage: 2 algebraic rows (the elliptic block in (P_0, P_1)) updating Q[5..6]. Mass matrix all-zero.

  • SM_corr — corrector: 4 algebraic update rows.

SM_pred: object#

None

SM_press: object#

None

SM_corr: object#

None

property stages#

The split as a canonical stage list (REQ-173).

A split solver marches over this list; a backend supplies one executor per Stage kind. The Chorin split is exactly this data — predictor (hyperbolic) → pressure (elliptic) → corrector (pointwise). .SM_pred / .SM_press / .SM_corr remain the primary attributes; this is a view of the same three objects with their stable labels and kinds attached.

zoomy_core.model.splitter.split_for_pressure(sm, pressure_vars, dt, *, bottom=None)#

DEPRECATED — use split_for_pressure_structural (via model.chorin_split(dt)).

Old chain-DAE splitter that dispatches rows by the equation_names convention (mass / xmom_j* / zmom_j* / cont_j*); a declarative SystemModel carries no such names. The structural splitter discovers the same roles from the operators (zero mass-matrix rows ⇒ constraints) and is the only supported path for new models.

zoomy_core.model.splitter.split_for_pressure_structural(sm, pressure_vars, dt)#

Structural predictor / pressure / corrector splitter — no equation-name conventions.

The chain-DAE split_for_pressure dispatches rows by the legacy naming scheme (xmom_j*, cont_j*, U_k/q_Uk). SystemModels extracted from a DECLARATIVE derivation carry no such names, but the row roles are fully visible in the operators:

  • constraint rows — identically-zero mass-matrix row (no ∂_t): these are the divergence/incompressibility projections → the elliptic block determining the pressure modes;

  • evolution rows — everything else; rows whose residual carries a pressure variable get the corrector update Q[s] ← Q[s] − dt·(pressure part)/M[row, s];

  • equation_to_state_index supplies the row→slot map (identity for a square from_model extraction).

The predictor is the PRESSURE-FREE hydrostatic system (the evolution residuals with every pressure mode set to zero) — Escalante et al. 2024 eq (12a): the hyperbolic step carries no non-hydrostatic terms; the FULL pressure impulse −dt·T(P^{n+1}) is applied by the corrector. (Keeping the pressure force at P^n in the predictor double-applies it and turns the pressure into an undamped alternating iteration P^{n+1} + P^n ≈ P_phys that diverges at shocks — the historic VAM dam-break NaN.) The elliptic rows are the constraint residuals with every corrected velocity substituted; the corrector applies the closed-form update via update_variables.

Parameters

  • sm (SystemModel): Square (or rectangular) extraction with equation_to_state_index.

  • pressure_vars (Sequence): Pressure-mode state Symbols (must be entries of sm.state).

  • dt (sp.Symbol): Symbolic time-step.

zoomy_core.model.splitter.split_simple(sm, pressure_vars, dt, *, bottom=None)#

DEPRECATED — use split_for_pressure_structural (via model.chorin_split(dt)).

Minimal hand-rolled splitter retained ONLY for the hand-built escalante_vam_bump/run.py reference oracle; it dispatches rows by the equation_names convention (cont_j* ⇒ constraint). New models / agents must use the structural splitter, which discovers the same roles from the operators (zero mass-matrix rows ⇒ constraints).

Copies the SystemModel, then deletes rows and terms.

Algorithm:

  1. Copy the parent SystemModel three times → (SM_pred, SM_press, SM_corr).

  2. SM_pred: keep only the rows whose equation_name does NOT start with cont_j (evolution rows including any trivial ones like b_eq for bathymetry-as-state). Then ZERO out every pressure-state-dependent term: F.xreplace({P_k: 0}), same for P, B, S. The predictor thus advances with kinetic + gravity + NCP and ANY non-pressure source — exactly like the OLD PredictorCorrectorSolver did with its flux-only step.

  3. SM_press: keep only the cont_j* rows, with the dt-baked U_corr substitution applied (reuses build_pressure_elliptic_block for the substitution machinery). This is the elliptic block in (P_0, P_1) that the implicit Newton-Krylov solver targets.

  4. SM_corr: the closed-form update_variables formula Q[k] ← Q[k] − (dt/h)·T_u_k(P) (also from build_pressure_elliptic_block). This is the projection-corrector that applies the freshly-solved pressure to the momentum modes.

Parameters

  • sm (SystemModel): Parent system. Must carry equation_names whose values flag algebraic rows by the cont_j prefix.

  • pressure_vars (Sequence[sp.Symbol]): Pressure state symbols (e.g. [P_0, P_1]).

  • dt (sp.Symbol): Symbolic time-step (gets baked into SM_press / SM_corr operators).

  • bottom (optional): Forwarded to build_pressure_elliptic_block.

Returns

  • SplitForPressureResult