zoomy_core.systemmodel.operations

zoomy_core.systemmodel.operations#

Reusable SystemModel.apply operations (op(sm) callables) that rewrite operator slots: aux registration, 1/h regularization, eigenvalue guards. They run on the way from SystemModel to NumericalSystemModel; sweeping operations cover every slot of OPERATOR_SLOTS unless a slot is explicitly excluded.

Module Contents#

Functions#

map_operator_slots

Apply rewrite(expr) -> expr to every slot of OPERATOR_SLOTS except exclude, freezing the lazy quasilinear_matrix from the clean primaries first.

normalize_face_normal

Operation: impose |n| = 1 on every slot (default scope OPERATOR_SLOTS), which separates the SWE celerity from the 1/h of the momentum terms. It is a NumericalSystemModel default (normalize_normal=True).

kp_hinv

Kurganov-Petrova desingularized inverse depth sqrt(2) h / sqrt(h^4 + max(h, eps)^4), equal to 1/h for h >= eps and tending to 0 as h -> 0. The numerator is bare h (no clamp), so a negative depth gives a negative hinv and the failure stays visible.

register_aux

Operation: add the algebraic aux variable name = formula to aux_state and update_aux_variables (identity on existing rows) and refresh the derived operators. **assumptions go to the new Symbol (default real=True).

regularize_pow

Operation: replace every field**(-n) in the physical, derived and reconstruction operators by aux_name**n; eigenvalues get a floored plain 1/max(1e-14, h) instead. The lazy derived operators are frozen from the clean primaries first, so the exact d(q^2/h)/dh term survives the substitution.

equilibrate_constraint_rows

Operation: multiply each algebraic (zero mass-matrix) row by the minimal power of h that clears its negative powers of h. This changes the conditioning of the elliptic block, not the constraint manifold, wherever ``h

desingularize_hinv

Operation: register the hinv aux with the Kurganov-Petrova inverse depth (eps = wet_dry_eps) and rewrite every h**(-n) to hinv**n; mode is True or "kp", falsy is a no-op. Legacy NSM depth default; the depth-law study uses regularize_depth_direct / regularize_depth_aux.

symbolic_spectrum

Operation: derive the closed-form spectrum of the normal-projected quasilinear matrix into sm.eigenvalues; opt-in because it is expensive for high moment levels.

regularize_depth_direct

Operation: substitute the Kurganov-Petrova reciprocal inline for every field**(-n) in every slot (the DIRECT path); no aux row is added. Mutually exclusive with regularize_depth_aux.

regularize_depth_aux

Operation: register the aux name whose update row is the Kurganov-Petrova reciprocal and rewrite every field**(-n) to name**n in every slot (the AUX path). Mutually exclusive with regularize_depth_direct; takes the same eps / eigenvalues / exclude arguments, and the row defining name is not rewritten.

desingularize_positivity

Operation: replace the base of each negative or fractional power in the source that involves a positive_state symbol by sqrt(base**2 + floor**2) and re-derive the source Jacobians. A no-op if the model declares no positive_state.

guard_eigenvalue_powers

Operation: wrap every fractional power of the depth in sm.eigenvalues in Max(., 0) so wave speeds stay real at a transient h < 0 (no dry zeroing). Half A of the guard/gate split; gate_eigenvalues_dry is half B.

gate_eigenvalues_dry

Operation: publish a timestep-only spectrum conditional(h > eps, e, 0) into sm.eigenvalues_cfl, leaving sm.eigenvalues physical (with its power guard) for the Riemann dissipation. The dry gate carries the power guard because a branchless backend evaluates both arms; without a closed form it gates numerical_eigenvalues (or the model’s published eigenvalues_cfl) and is idempotent.

regularize_fractional_powers

Operation: shift the base of every fractional power by eps**2 (b**r -> (b + eps**2)**r) so the operators stay differentiable at b = 0 under AD. Opt-in and idempotent; eps must be positive, and the value changes by O(eps**2) away from b = 0.

Data#

API#

zoomy_core.systemmodel.operations.OPERATOR_SLOTS#

(‘flux’, ‘hydrostatic_pressure’, ‘nonconservative_matrix’, ‘source’, ‘source_explicit’, ‘mass_matrix…

zoomy_core.systemmodel.operations.KERNEL_SLOTS#

(‘boundary_conditions’, ‘aux_boundary_conditions’, ‘boundary_gradients’)

zoomy_core.systemmodel.operations.map_operator_slots(sm, rewrite, *, exclude=(), skip_rows=None) → dict#

Apply rewrite(expr) -> expr to every slot of OPERATOR_SLOTS except exclude, freezing the lazy quasilinear_matrix from the clean primaries first.

Args

  • exclude: slot names to leave alone; a name outside OPERATOR_SLOTS raises KeyError.

  • skip_rows: {slot: rows} left untouched inside a swept slot.

Returns

{slot: "rewritten" | "unchanged" | "absent" | "excluded"}.

zoomy_core.systemmodel.operations.normalize_face_normal(*, exclude=())#

Operation: impose |n| = 1 on every slot (default scope OPERATOR_SLOTS), which separates the SWE celerity from the 1/h of the momentum terms. It is a NumericalSystemModel default (normalize_normal=True).

zoomy_core.systemmodel.operations.kp_hinv(h, eps)#

Kurganov-Petrova desingularized inverse depth sqrt(2) h / sqrt(h^4 + max(h, eps)^4), equal to 1/h for h >= eps and tending to 0 as h -> 0. The numerator is bare h (no clamp), so a negative depth gives a negative hinv and the failure stays visible.

Args

  • h: depth, sympy expression or numpy/jax array.

  • eps: regularization scale.

References

  • Kurganov, Petrova 2007, https://doi.org/10.4310/CMS.2007.v5.n1.a6

zoomy_core.systemmodel.operations.register_aux(name: str, formula, **assumptions)#

Operation: add the algebraic aux variable name = formula to aux_state and update_aux_variables (identity on existing rows) and refresh the derived operators. **assumptions go to the new Symbol (default real=True).

zoomy_core.systemmodel.operations.regularize_pow(field: Union[str, sympy.Symbol], aux_name: str)#

Operation: replace every field**(-n) in the physical, derived and reconstruction operators by aux_name**n; eigenvalues get a floored plain 1/max(1e-14, h) instead. The lazy derived operators are frozen from the clean primaries first, so the exact d(q^2/h)/dh term survives the substitution.

zoomy_core.systemmodel.operations.equilibrate_constraint_rows(mode=True)#

Operation: multiply each algebraic (zero mass-matrix) row by the minimal power of h that clears its negative powers of h. This changes the conditioning of the elliptic block, not the constraint manifold, wherever ``h

0``.

zoomy_core.systemmodel.operations.desingularize_hinv(mode='kp')#

Operation: register the hinv aux with the Kurganov-Petrova inverse depth (eps = wet_dry_eps) and rewrite every h**(-n) to hinv**n; mode is True or "kp", falsy is a no-op. Legacy NSM depth default; the depth-law study uses regularize_depth_direct / regularize_depth_aux.

References

  • Kurganov, Petrova 2007, https://doi.org/10.4310/CMS.2007.v5.n1.a6

zoomy_core.systemmodel.operations.symbolic_spectrum()#

Operation: derive the closed-form spectrum of the normal-projected quasilinear matrix into sm.eigenvalues; opt-in because it is expensive for high moment levels.

zoomy_core.systemmodel.operations.regularize_depth_direct(eps=None, *, field: str = 'h', eigenvalues: str = 'regularize', exclude=())#

Operation: substitute the Kurganov-Petrova reciprocal inline for every field**(-n) in every slot (the DIRECT path); no aux row is added. Mutually exclusive with regularize_depth_aux.

Args

  • eps: regularization scale (not wet_dry_eps).

  • eigenvalues: "regularize" sweeps the spectrum, "exclude" keeps the exact 1/h.

  • exclude: further slots to leave alone (opt out, never opt in).

References

  • Kurganov, Petrova 2007, https://doi.org/10.4310/CMS.2007.v5.n1.a6

zoomy_core.systemmodel.operations.regularize_depth_aux(eps=None, *, name: str = 'hinv', field: str = 'h', eigenvalues: str = 'regularize', exclude=())#

Operation: register the aux name whose update row is the Kurganov-Petrova reciprocal and rewrite every field**(-n) to name**n in every slot (the AUX path). Mutually exclusive with regularize_depth_direct; takes the same eps / eigenvalues / exclude arguments, and the row defining name is not rewritten.

References

  • Kurganov, Petrova 2007, https://doi.org/10.4310/CMS.2007.v5.n1.a6

zoomy_core.systemmodel.operations.desingularize_positivity(floor)#

Operation: replace the base of each negative or fractional power in the source that involves a positive_state symbol by sqrt(base**2 + floor**2) and re-derive the source Jacobians. A no-op if the model declares no positive_state.

zoomy_core.systemmodel.operations.guard_eigenvalue_powers()#

Operation: wrap every fractional power of the depth in sm.eigenvalues in Max(., 0) so wave speeds stay real at a transient h < 0 (no dry zeroing). Half A of the guard/gate split; gate_eigenvalues_dry is half B.

zoomy_core.systemmodel.operations.gate_eigenvalues_dry(eps=None)#

Operation: publish a timestep-only spectrum conditional(h > eps, e, 0) into sm.eigenvalues_cfl, leaving sm.eigenvalues physical (with its power guard) for the Riemann dissipation. The dry gate carries the power guard because a branchless backend evaluates both arms; without a closed form it gates numerical_eigenvalues (or the model’s published eigenvalues_cfl) and is idempotent.

zoomy_core.systemmodel.operations.regularize_fractional_powers(eps, *, exclude=())#

Operation: shift the base of every fractional power by eps**2 (b**r -> (b + eps**2)**r) so the operators stay differentiable at b = 0 under AD. Opt-in and idempotent; eps must be positive, and the value changes by O(eps**2) away from b = 0.