zoomy_core.model.derivation.closure#
Concrete-level Galerkin resolve, conservative-form folding, simplification and term-tagging operations of the derivation pipeline. They carry a modal ansatz to the K&T SME system form that system_extract classifies into SystemModel slots.
Module Contents#
Classes#
Fold a self-product flux term c·f·∂_x f into ∂_x(c·f²/2) by splitting it in two halves and applying the inverse product rule to one half. Value-preserving (½·I + ½·ProductRule⁻¹); apply it to the self-product terms only. |
|
Divide a projected moment row by the coefficient of its ∂_t term, normalising ∂_t Q_k to unit coefficient (the mass-matrix entry is read off the row). Apply it after the stray ∂_t h has been removed so the row carries a single diagonal ∂_t term. |
|
Tracked op form of fold_to_conservative_form: conservative diffusion atoms ∂_x(D·∂_x q) are
left untouched and the hyperbolic remainder is folded. Arguments mirror
fold_to_conservative_form ( |
|
Concrete-level Galerkin project-and-resolve: multiply the row by the test weight c(ζ)·φ(l, ζ), substitute the concrete basis polynomials, and integrate over ζ with the ∂_x / ∂_t derivatives kept inside the integral. Apply after the truncation bound is concrete and the Sum ansatz is expanded; modal coefficients and h, b factors are left untouched. |
|
Resolve the opaque ∫…dζ atoms left by an abstract projection, each by a selectable method:
“auto” (classified per term by shape), “basis”, “ftc” or “numerical”. |
|
Op form of consolidate with fold_self and fold_sums enabled. |
|
Replace every definite Integral atom with numeric bounds by an order-point Gauss-Legendre sum Σ_g w_g f(ζ_g), for integrands without a closed form (e.g. non-polynomial material closures). Exact for polynomials up to degree 2·order − 1; apply after the basis is resolved. |
|
Hide every surviving moment-bearing ∫_0^1 f dζ as an opaque NumQuad(f) atom, so non-analytic rational closure integrals ride the rest of the derivation unexpanded. Apply before ResolveBasis; pure-ζ Galerkin brackets are left for analytic closure. |
|
Resolve every opaque NumQuad(f) to its order-point Gauss-Legendre sum over ζ in [0, 1]; run
it as the last derivation step. |
|
Canonicalisation that keeps fluxes grouped while exposing cancellations: pull_consts followed
by consolidate. On an Equation it also runs Sort as a final step ( |
|
Tag each untagged additive term with its physics category, leaving existing tags alone. Pairs with SortByTag; Sort runs both. |
|
Reorder an equation’s terms into TAG_ORDER, stable within each group. |
|
AutoTag then SortByTag; apply it last, because a later expression-level op rebuilds the term list and drops the ordering. |
Functions#
Fold the spatial part of a momentum/continuity residual into conservative flux and hydrostatic-pressure bundles, leaving bed and cross-mode couplings as explicit nonconservative terms. Reproduces the production K&T decomposition, e.g. g·h·∂_x b + ∂_x(g·h²/2) + ∂_x(q_0²/h + q_1²/3h) + ∂_t q_0. |
|
True iff |
|
Galerkin-project a viscous second-order momentum block so the diffusive flux stays in conservative ∂_x(F^d) form (typed as diffusion_matrix), with the σ-metric residual integrated in place. Built from Multiply(test weight), the inverse ProductRule, abstract Integrate and ResolveIntegral (basis); the sum equals the in-place Resolve. |
|
∂_v(c·f) -> c·∂_v f for every factor c independent of the derivative variables, to a fixpoint and through nested derivatives. Gentle: it never applies the product rule to a v-dependent product, so ∂_x(g h²/2) -> (g/2)∂_x(h²) keeps h² grouped. |
|
Recover conservative groupings g·∂_v f + f·∂_v g -> ∂_v(f·g) by a pairwise fold applied only when it is exact. Terms with Integral atoms and genuine nonconservative couplings are left untouched. |
|
Pairwise Leibniz fold with a shared passive factor M: M·a·∂_v b + M·b·∂_v a -> M·∂_v(a·b). Accepted only when exact and M carries no derivative or integral. |
|
Fold a self-product M·f·∂_v f into M·∂_v(f²/2) (M moved inside when v-independent), e.g. g·h·∂_x h -> ∂_x(g h²/2). Fires only when M does not reintroduce f, which makes the fold idempotent. |
|
Push every coordinate-free factor back into a bare derivative (inverse of pull_consts), e.g. (g/2)·∂_x(h²)·⟨c,φ_k⟩ -> ∂_x((g/2)·h²·⟨c,φ_k⟩). Fires only on a term with one derivative factor whose other factors are all coordinate-free; also applied inside every Sum. |
|
Merge Sum(A, lims) + Sum(B, lims) -> Sum(A + B, lims) for identical limits, pulling index-independent coefficients inside first. The Sum analogue of merge_integrals_over_add. |
|
Unify terms under a common outer derivative: leibniz_fold on the top-level terms and, after merging same-limit integrals, recursively on every integrand and Sum summand. |
Data#
API#
- class zoomy_core.model.derivation.closure.Split(variables=None, name='split')#
Bases:
zoomy_core.model.operations.OperationFold a self-product flux term c·f·∂_x f into ∂_x(c·f²/2) by splitting it in two halves and applying the inverse product rule to one half. Value-preserving (½·I + ½·ProductRule⁻¹); apply it to the self-product terms only.
Initialization
variablesrestricts the fold to these fields (None: all).- whole_leaf_op#
True
- class zoomy_core.model.derivation.closure.InvertMassMatrix(time=None, name='invert_mass_matrix')#
Bases:
zoomy_core.model.operations.OperationDivide a projected moment row by the coefficient of its ∂_t term, normalising ∂_t Q_k to unit coefficient (the mass-matrix entry is read off the row). Apply it after the stray ∂_t h has been removed so the row carries a single diagonal ∂_t term.
Initialization
timeis the time coordinate (default: the globalt).- whole_leaf_op#
True
- zoomy_core.model.derivation.closure.fold_to_conservative_form(expr, flux_fields, *, h, b, x, gravity_param)#
Fold the spatial part of a momentum/continuity residual into conservative flux and hydrostatic-pressure bundles, leaving bed and cross-mode couplings as explicit nonconservative terms. Reproduces the production K&T decomposition, e.g. g·h·∂_x b + ∂_x(g·h²/2) + ∂_x(q_0²/h + q_1²/3h) + ∂_t q_0.
Args
expr: residual of one row, in Function form.flux_fields: conserved modal fields q(k, t, x).h,b: depth and bed (applied Functions).x: spatial coordinate.
Returns
The re-bundled residual.
References
Kowalski, Torrilhon 2019, https://doi.org/10.4208/cicp.OA-2017-0263
- class zoomy_core.model.derivation.closure.FoldConservative(flux_fields, *, h, b, x, gravity=None, name='fold_conservative')#
Bases:
zoomy_core.model.operations.OperationTracked op form of fold_to_conservative_form: conservative diffusion atoms ∂_x(D·∂_x q) are left untouched and the hyperbolic remainder is folded. Arguments mirror fold_to_conservative_form (
gravityis itsgravity_param).Initialization
Store the fold arguments.
- whole_leaf_op#
True
- class zoomy_core.model.derivation.closure.Resolve(test_function, basis_cls, level, *, var=None, bounds=(0, 1), name='resolve')#
Bases:
zoomy_core.model.operations.OperationConcrete-level Galerkin project-and-resolve: multiply the row by the test weight c(ζ)·φ(l, ζ), substitute the concrete basis polynomials, and integrate over ζ with the ∂_x / ∂_t derivatives kept inside the integral. Apply after the truncation bound is concrete and the Sum ansatz is expanded; modal coefficients and h, b factors are left untouched.
Args
test_function: Galerkin test weight, typically c(ζ)·φ(l, ζ) with l bound to the row’s mode.basis_cls: concrete basis class providingeval(k, ζ)andweight.level: concrete basis level.var: ζ integration variable (defaultzeta).bounds: integration interval (default (0, 1)).
References
Kowalski, Torrilhon 2019, https://doi.org/10.4208/cicp.OA-2017-0263
Initialization
Build the test-weight multiplier and the concrete basis.
- whole_leaf_op#
True
- class zoomy_core.model.derivation.closure.ResolveIntegral(basis_cls=None, *, var=None, method='auto', level=None, weight=None, classify=None, bounds=(0, 1), name='resolve_integral')#
Bases:
zoomy_core.model.operations.OperationResolve the opaque ∫…dζ atoms left by an abstract projection, each by a selectable method: “auto” (classified per term by shape), “basis”, “ftc” or “numerical”.
classify(Integral -> method or None) overrides the defaultmethodper integral.Args
basis_cls: concrete basis used by the “basis” and “auto” methods.method: default resolution method.weight: basis weight when nobasis_clsis given.classify: optional per-integral method override.
Initialization
Configure the default method, basis, and bounds.
- whole_leaf_op#
True
- zoomy_core.model.derivation.closure.is_conservative_diffusion(term, x)#
True iff
termis coeff·∂_x(F) with a single first-order ∂_x factor whose flux F itself contains a ∂_x, the ∂_x(D·∂_x q) shape that system_extract types as diffusion_matrix. Advective fluxes ∂_x(q²/h) and first-order couplings coeff·∂_x q do not match.
- zoomy_core.model.derivation.closure.project_conservative_diffusion(visc_expr, test_weight, *, basis_cls, level, var, x)#
Galerkin-project a viscous second-order momentum block so the diffusive flux stays in conservative ∂_x(F^d) form (typed as diffusion_matrix), with the σ-metric residual integrated in place. Built from Multiply(test weight), the inverse ProductRule, abstract Integrate and ResolveIntegral (basis); the sum equals the in-place Resolve.
Args
visc_expr: viscous term of the momentum row.test_weight: Galerkin test weight c·φ_k.basis_cls,level: concrete basis and its level.var: ζ integration variable.x: horizontal coordinate.
Returns
The resolved viscous moment (conservative flux plus metric residual).
- zoomy_core.model.derivation.closure.pull_consts(expr)#
∂_v(c·f) -> c·∂_v f for every factor c independent of the derivative variables, to a fixpoint and through nested derivatives. Gentle: it never applies the product rule to a v-dependent product, so ∂_x(g h²/2) -> (g/2)∂_x(h²) keeps h² grouped.
- zoomy_core.model.derivation.closure.fold_conservatives(expr)#
Recover conservative groupings g·∂_v f + f·∂_v g -> ∂_v(f·g) by a pairwise fold applied only when it is exact. Terms with Integral atoms and genuine nonconservative couplings are left untouched.
- zoomy_core.model.derivation.closure.leibniz_fold(expr)#
Pairwise Leibniz fold with a shared passive factor M: M·a·∂_v b + M·b·∂_v a -> M·∂_v(a·b). Accepted only when exact and M carries no derivative or integral.
- zoomy_core.model.derivation.closure.fold_self_products(expr)#
Fold a self-product M·f·∂_v f into M·∂_v(f²/2) (M moved inside when v-independent), e.g. g·h·∂_x h -> ∂_x(g h²/2). Fires only when M does not reintroduce f, which makes the fold idempotent.
- zoomy_core.model.derivation.closure.push_consts_into_flux(expr, coords=None)#
Push every coordinate-free factor back into a bare derivative (inverse of pull_consts), e.g. (g/2)·∂_x(h²)·⟨c,φ_k⟩ -> ∂_x((g/2)·h²·⟨c,φ_k⟩). Fires only on a term with one derivative factor whose other factors are all coordinate-free; also applied inside every Sum.
- zoomy_core.model.derivation.closure.merge_sums_over_add(expr)#
Merge Sum(A, lims) + Sum(B, lims) -> Sum(A + B, lims) for identical limits, pulling index-independent coefficients inside first. The Sum analogue of merge_integrals_over_add.
- zoomy_core.model.derivation.closure.consolidate(expr, fold_self=False, fold_sums=False)#
Unify terms under a common outer derivative: leibniz_fold on the top-level terms and, after merging same-limit integrals, recursively on every integrand and Sum summand.
Args
expr: expression to fold.fold_self: also fold self-products (fold_self_products) and push constants back into fluxes.fold_sums: hide Galerkin brackets and merge same-index Sums first.
- class zoomy_core.model.derivation.closure.Consolidate(name='consolidate')#
Bases:
zoomy_core.model.operations.OperationOp form of consolidate with fold_self and fold_sums enabled.
Initialization
Create the op.
- whole_leaf_op#
True
- log_level#
5
- class zoomy_core.model.derivation.closure.GaussQuadrature(var=None, order=8, name=None)#
Bases:
zoomy_core.model.operations.OperationReplace every definite Integral atom with numeric bounds by an order-point Gauss-Legendre sum Σ_g w_g f(ζ_g), for integrands without a closed form (e.g. non-polynomial material closures). Exact for polynomials up to degree 2·order − 1; apply after the basis is resolved.
Args
var: integration variable (defaultzeta).order: number of Gauss nodes (>= 1).
References
Golub, Welsch 1969, https://doi.org/10.1090/s0025-5718-69-99647-1
Initialization
Store the node count and validate it.
- whole_leaf_op#
True
- log_level#
1
- zoomy_core.model.derivation.closure.NumQuad#
‘type(…)’
- class zoomy_core.model.derivation.closure.DeferQuadrature(var=None, name='defer_quadrature')#
Bases:
zoomy_core.model.operations.OperationHide every surviving moment-bearing ∫_0^1 f dζ as an opaque NumQuad(f) atom, so non-analytic rational closure integrals ride the rest of the derivation unexpanded. Apply before ResolveBasis; pure-ζ Galerkin brackets are left for analytic closure.
Initialization
Set the integration variable (default
zeta).- whole_leaf_op#
True
- log_level#
1
- class zoomy_core.model.derivation.closure.ResolveNumQuad(var=None, order=8, basis=None, name=None)#
Bases:
zoomy_core.model.operations.OperationResolve every opaque NumQuad(f) to its order-point Gauss-Legendre sum over ζ in [0, 1]; run it as the last derivation step.
basis(optional) resolves opaque φ(k, ζ) still present in a bracket body at the nodes.References
Golub, Welsch 1969, https://doi.org/10.1090/s0025-5718-69-99647-1
Initialization
Store the node count and the optional basis.
- whole_leaf_op#
True
- log_level#
1
- class zoomy_core.model.derivation.closure.Simplify(name='simplify', sort=True)#
Bases:
zoomy_core.model.operations.OperationCanonicalisation that keeps fluxes grouped while exposing cancellations: pull_consts followed by consolidate. On an Equation it also runs Sort as a final step (
sort=Falseskips it).Initialization
sorttoggles the final Sort pass on Equations.- whole_leaf_op#
True
- log_level#
5
- apply_to_equation(eq)#
Canonicalise the residual, then tag and sort its terms when
sortis on.
- zoomy_core.model.derivation.closure.TAG_ORDER#
[‘time_derivative’, ‘flux’, ‘pressure_flux’, ‘diffusion’, ‘nonconservative_flux’, ‘source’, ‘untagge…
- class zoomy_core.model.derivation.closure.AutoTag(name='auto_tag', detect_ncp=True)#
Bases:
zoomy_core.model.operations.OperationTag each untagged additive term with its physics category, leaving existing tags alone. Pairs with SortByTag; Sort runs both.
Initialization
detect_ncptoggles detection of nonconservative products (off: they become source).- log_level#
5
- apply_to_equation(eq)#
Tag the untagged terms of
eq.
- class zoomy_core.model.derivation.closure.SortByTag(name='sort_by_tag')#
Bases:
zoomy_core.model.operations.OperationReorder an equation’s terms into TAG_ORDER, stable within each group.
Initialization
Create the op.
- log_level#
5
- apply_to_equation(eq)#
Sort
eq’s terms by tag rank.
- class zoomy_core.model.derivation.closure.Sort(name='sort', detect_ncp=True)#
Bases:
zoomy_core.model.operations.OperationAutoTag then SortByTag; apply it last, because a later expression-level op rebuilds the term list and drops the ordering.
Initialization
detect_ncpis passed to AutoTag.- log_level#
5
- apply_to_equation(eq)#
Auto-tag, then sort
eq’s terms.