zoomy_core.fvm.riemann_solvers module#
Symbolic Riemann solvers: Rusanov, positive, nonconservative variants.
Every index-dependent class (PositiveRusanov, NonconservativeRusanov,
PositiveNonconservativeRusanov, …) auto-locates the named fields
h and b (and hinv when present) in model.state /
model.aux_state via FieldHandle so the same Riemann
code path works whether bathymetry is part of the conservative state
(legacy SWE) or lives in Qaux (chain-DAE convention).
- class zoomy_core.fvm.riemann_solvers.FieldHandle(name, container, index, minus, plus, state)#
Bases:
objectA named field resolved to either
Q(state) orQaux.Looks up the location at construction time via
Numerics.find_field(), then exposes the symbolic Symbol on the minus / plus / state side of a face so callers can write reconstruction code without knowing where the field lives.- name#
Field name (e.g.
"h","b","hinv").- Type:
str
- container#
Which array carries this field.
- Type:
{“q”, “qaux”}
- index#
Index into that array.
- Type:
int
- minus, plus, state
Direct references —
minusandplusare the per-face symbolic state on the L / R side of a Rusanov face;stateis the cell-centre reference. Using these in symbolic code produces the same lambdified output asQ[index]/Qaux[index]— a true placeholder.- Type:
sympy.Symbol
- name#
- container#
- index#
- minus#
- plus#
- state#
- access(q_array, qaux_array)#
Return
q_array[index]orqaux_array[index]depending on container. Works for symbolicZArrayand numeric numpy arrays alike.
- assign(q_array, qaux_array, value)#
In-place write into the appropriate container.
- class zoomy_core.fvm.riemann_solvers.Numerics(model, **params)#
Bases:
Parameterized,SymbolicRegistrarSymbolic numerics over a
SystemModel.The numerics consumes a
SystemModel— the frozen operator-form snapshot of a derivation. AModelpassed to the constructor is normalised once viaSystemModel.from_model(); everything internal reads the SystemModel’s stored operators (flux,hydrostatic_pressure,eigenvalues,nonconservative_matrix,quasilinear_matrix) and itsstate/aux_state/parameters/normal.Subclasses that depend on the location of named fields (
h,b,hinv, …) callfind_field()to obtain aFieldHandle— the search walks state then aux automatically, so the same numerics works whether bathymetry is in the state or inaux_state.- name = 'NumericsV2'#
- model = None#
- scaled_q_indices = None#
- find_field(name, *, required=True)#
Return a
FieldHandlefor the named field.Searches
self.model.state(Q) first, thenself.model.aux_state(Qaux). Caches the result inself._field_handles[name]so repeat lookups are free.- Parameters:
name (str) –
required (bool) – If True (default), raise
KeyErrorwhen the field is in neither container. Otherwise returnNone.
- has_field(name)#
- local_max_eigenvalue_definition()#
Returns the opaque max_wavespeed function. The actual implementation is provided by the backend at runtime.
- local_max_abs_eigenvalue(Q=None, Qaux=None, p=None, n=None)#
Called during symbolic Rusanov construction. Returns opaque max_wavespeed with the given state.
- to_runtime(backend='numpy')#
To runtime.
- to_runtime_numpy()#
To runtime numpy.
- to_runtime_ufl()#
Lambdify all registered symbolic functions through the UFL module dict — for use by Firedrake-based backends.
- numerical_flux()#
Numerical flux.
- numerical_fluctuations()#
Numerical fluctuations.
- class zoomy_core.fvm.riemann_solvers.Rusanov(model, **params)#
Bases:
NumericsRusanov. (class).
- name = 'RusanovV2'#
- get_viscosity_identity_flux()#
Get viscosity identity flux.
- get_viscosity_identity_fluctuations()#
Get viscosity identity fluctuations.
- numerical_flux()#
Numerical flux.
- class zoomy_core.fvm.riemann_solvers.HLL(model, **params)#
Bases:
NumericsHLL (Harten-Lax-van-Leer) approximate Riemann solver.
Model-agnostic: needs only the SystemModel’s
flux,hydrostatic_pressureandeigenvaluesoperators. When the SystemModel carries a symbolic spectrum the wave-speed bounds are the Davis estimates — min / max over the eigenvalues of both face states. WheneigenvaluesisNone(the model skipped the spectral derivation) it falls back to± local_max_abs_eigenvalue, i.e. HLL collapses to local Lax-Friedrichs (a valid, more diffusive HLL).The numerical flux is a single closed-form (branch-free) SymPy expression — clamping the wave speeds with
Min(s_L, 0)/Max(s_R, 0)recovers the upwind branches withoutPiecewise, so it codegens cleanly to every backend.- name = 'HLLV2'#
- numerical_flux()#
Numerical flux.
- wave_speed_bounds(qL, qR, auxL, auxR, p, n)#
Return
(s_L, s_R)— slowest / fastest signal speeds at the face.
- class zoomy_core.fvm.riemann_solvers.HLLC(model, **params)#
Bases:
HLLHLLC approximate Riemann solver for the free-surface family.
Restores the contact / shear wave that HLL smears. Requires a depth field
h(resolved viaNumerics.find_field()); the momentum block is the firstmodel.dimensiondepth-scaled Q rows. Any further depth-scaled rows (higher moments) are advected by the contact wave; non-scaled rows (e.g. bed in conservative state) pass through unchanged. Models without anhfield should useHLLinstead.Region selection (
F_L | F_L* | F_R* | F_R) uses the opaqueconditionalprimitive, so it codegens tonp.where/ ternary expressions on every backend.- name = 'HLLCV2'#
- property h_field#
Depth
FieldHandle— raisesKeyErrorif the model has nohfield (such models should useHLL).
- class zoomy_core.fvm.riemann_solvers.PositiveRusanov(model, **params)#
Bases:
RusanovPositiveRusanov. (class).
Hydrostatic reconstruction follows Audusse-Bristeau-Klein:
h_L* = max(0, h_L + b_L − b*),b* = max(b_L, b_R). Theh,band (optional)hinvfields are resolved viaNumerics.find_field()so the same logic works whether bathymetry is part of conservative state or lives inQaux.- name = 'PositiveRusanovV2'#
- property h_field#
Depth
FieldHandle.
- property b_field#
Bathymetry
FieldHandle.
- property hinv_field#
Optional
1/hFieldHandle(Noneif absent).
- hydrostatic_reconstruction(qL, qR, auxL, auxR)#
Hydrostatic reconstruction.
- numerical_flux()#
Numerical flux.
- numerical_fluctuations()#
Numerical fluctuations.
- class zoomy_core.fvm.riemann_solvers.NonconservativeRusanov(model, **params)#
Bases:
RusanovNonconservativeRusanov. (class).
- name = 'NonconservativeRusanovV2'#
- integration_order = 3#
- get_path_integral_states()#
Get path integral states.
- get_viscosity_identity_flux()#
Get viscosity identity flux.
- get_viscosity_identity_fluctuations()#
Get viscosity identity fluctuations.
- numerical_fluctuations()#
Numerical fluctuations.
- class zoomy_core.fvm.riemann_solvers.WellBalancedNonconservativeRusanov(model, **params)#
Bases:
NonconservativeRusanovPath-conservative Rusanov with equilibrium-variable fluctuation viscosity for free-surface (lake-at-rest) well-balancing.
Both the bed-slope
g·h·∂_x band the hydrostatic pressureg·h·∂_x hlive in the nonconservative product (the “Malaga” formulation;hydrostatic_pressureis empty,bis a trivial conserved state with∂_t b = 0). The baseNonconservativeRusanov.get_viscosity_identity_fluctuations()already zeros the stationary-bed row. This subclass additionally couples the depth-continuity row to the bed column so the Rusanov dissipation acts on the free-surface jumpΔη = Δh + Δbinstead ofΔh. At lake-at-restΔη = 0whileΔh = −Δb ≠ 0, so only the coupled form vanishes — giving exact well-balancing for both Rusanov and HLL-flavoured path integrals.For
Q = [b, h, hu]the fluctuation viscosity becomes:[[0, 0, 0], [1, 1, 0], # continuity dissipates on Δη = Δh + Δb [0, 0, 1]]
Model-derived — the coupling is added only when both
handbresolve to conservative-state fields viaNumerics.find_field(). A plain SWE model with no bed gets the unmodified identity (no bed-indexed term at all), as required.- name = 'WellBalancedNonconservativeRusanovV2'#
- get_viscosity_identity_fluctuations()#
Get viscosity identity fluctuations (equilibrium-coupled).
- class zoomy_core.fvm.riemann_solvers.PositiveHLL(model, **params)#
Bases:
HLLHLL with Audusse-Bristeau-Klein hydrostatic reconstruction.
Mirrors
PositiveRusanovbut uses the sharper HLL two-wave numerical flux underneath instead of LF/Rusanov dissipation. Recommended for free-surface dam-break / wet-dry simulations: the hydrostatic reconstruction enforcesh_face ≥ 0(positivity), and HLL captures the rarefaction / shock fronts more accurately than Rusanov on the same mesh.Reconstruction (same as PositiveRusanov, Audusse-Bristeau-Klein):
b* = max(b_L, b_R),h_L* = max(0, h_L + b_L − b*), momentum scaled byh_L* / max(h_L, eps).The depth field
h, bathymetryband (optional)1/hinverse are resolved throughNumerics.find_field()— the same flux code works whether bathymetry is part of the conservative state or carried inQaux.- name = 'PositiveHLLV2'#
- property h_field#
- property b_field#
- property hinv_field#
- hydrostatic_reconstruction(qL, qR, auxL, auxR)#
Hydrostatic reconstruction.
- numerical_flux()#
HLL flux evaluated on the hydrostatically-reconstructed face states — positivity-preserving and well-balanced under the lake-at-rest steady state.
The bed row is exactly zero by construction: HLL’s
_state_jumpzerosdq[b]whenblives in the conservative state, and a well-posed SWE hasF[b, :] = 0symbolically.
- numerical_fluctuations()#
Audusse 2004 well-balancing consistency source
S̃.Audusse-Bouchut-Bristeau-Klein-Perthame 2004, SIAM J. Sci. Comput. 25(6):2050-2065, eq. (2.17)-(2.18):
F_{i+1/2,L} = F_num(U*_L, U*_R) - S̃_L F_{i+1/2,R} = F_num(U*_L, U*_R) + S̃_R
S̃_L = (0, ½g h_L² - ½g h*_L²)^T = (0, P_raw_L - P*_L)^T S̃_R = (0, ½g h*_R² - ½g h_R²)^T = (0, P*_R - P_raw_R)^T
The HR’d numerical flux alone is not well-balanced; the per- cell-per-face S̃ correction restores consistency with the original SWE (paper, Theorem 2.5). S̃ depends only on
hydrostatic_pressureand is independent of the underlying Riemann solver — same formula already inPositiveRusanov.numerical_fluctuations().super().numerical_fluctuations()chains the next entry on the MRO. For plainPositiveHLLit lands atNumerics.numerical_fluctuations()(returns zero). ForPositiveNonconservativeHLLit lands atNonconservativeRusanov.numerical_fluctuations()(the DLM path-integral over the NCP) — so the same override gives well-balanced HLL whether the model carries an NCP or not.
- class zoomy_core.fvm.riemann_solvers.PositiveNonconservativeRusanov(model, **params)#
Bases:
PositiveRusanov,NonconservativeRusanovPositiveNonconservativeRusanov. (class).
- name = 'PositiveNonconservativeRusanovV2'#
- get_path_integral_states()#
Get path integral states.
- class zoomy_core.fvm.riemann_solvers.PositiveNonconservativeHLL(model, **params)#
Bases:
PositiveHLL,NonconservativeRusanovHLL conservative flux + Audusse-Bristeau-Klein hydrostatic reconstruction + path-integral NCP fluctuations.
Combines the sharper HLL two-wave numerical flux (vs. Rusanov / LF) with the same well-balanced reconstruction and path-integral NCP fluctuations as
PositiveNonconservativeRusanov. The bed row is automatically excluded from the LF-style fluctuation dissipation via the bed mask inNonconservativeRusanov.get_viscosity_identity_fluctuations().The Python MRO
(PositiveNonconservativeHLL → PositiveHLL → HLL → NonconservativeRusanov → Rusanov → Numerics)resolvesnumerical_fluxtoPositiveHLL.numerical_flux(hydrostatic reconstruction → HLL combine → bed-row mask) andnumerical_fluctuationstoNonconservativeRusanov. numerical_fluctuations(path-integral NCP + LF identity, bed masked) — exactly the split we want.- name = 'PositiveNonconservativeHLLV2'#
- get_path_integral_states()#
NCP path-integral evaluated on the hydrostatically- reconstructed face states.
- class zoomy_core.fvm.riemann_solvers.QuasilinearRusanov(model, **params)#
Bases:
NonconservativeRusanovQuasilinearRusanov. (class).
- name = 'QuasilinearRusanovV2'#
- numerical_flux()#
Numerical flux.
- class zoomy_core.fvm.riemann_solvers.PositiveQuasilinearRusanov(model, **params)#
Bases:
PositiveRusanov,QuasilinearRusanovPositiveQuasilinearRusanov. (class).
- name = 'PositiveQuasilinearRusanovV2'#
- numerical_flux()#
Numerical flux.
- get_path_integral_states()#
Get path integral states.