zoomy_core.model.models.closures

zoomy_core.model.models.closures#

Composable stress closures: each Closure is one constitutive relation for a stress component (bulk, bed trace, surface trace, horizontal) over a ClosureState. Models list them in closures=[...] and perform the substitution when the Model is derived, before the lift to a SystemModel.

Module Contents#

Classes#

Closure

Base class of a one-component stress closure; subclasses set the immutable closes / requires and implement expression.

Newtonian

Incompressible Newtonian fluid, tau = rho nu (grad u + grad u^T). expression is the vertical shear rho nu d_z u (all Stress.shallow keeps); with Stress.full the model takes the whole tensor (tensor before the sigma map, stress / normal_stress / shear at the traces), so the depth-averaged in-plane term is div(h (tau - tau_zz I)), i.e. div(nu h (grad u + grad u^T)) + 2 nu grad(h div u) at level 0.

KEpsilonViscosity

Bulk stress tau = rho nu_t d_z u with the k-epsilon eddy viscosity nu_t = C_mu k^2/epsilon; needs k and varepsilon fields and quadrature_order > 0.

QRViscosity

Bulk stress for the positivity-preserving q-r k-epsilon model, with nu_t = C_mu sk^4/se^2 for the transported sk = sqrt(k), se = sqrt(epsilon); needs quadrature_order > 0.

Bingham

Regularized Bingham bulk stress tau = (rho nu + tau_y/sqrt((d_z u)^2 + eps^2)) d_z u, rigid below the yield stress tau_y as eps -> 0. Not analytically integrable, so build the model with quadrature_order > 0.

ElderViscosity

Algebraic Elder eddy viscosity nu_t = kappa u_star h zeta (1 - zeta), polynomial in zeta so the Galerkin projection closes analytically.

NavierSlip

Linear Navier slip at the bed, tau_b = lambda_s u_b.

RoughWall

Turbulent rough-wall bed drag tau_b = rho C_f u_b |u_b| with C_f = (kappa/ln(z_p/z_0))^2 and z_0 = k_s/30. Needs k_s (Nikuradse roughness), z_p and kappa.

WallFunctionBed

k-epsilon rough-wall-function bed stress tau_b = rho C_f |U_p| U_p with the log-law C_f of RoughWall but the velocity U_p read at the near-wall height zeta_p = z_p/h rather than at the bed trace. Needs k_s, z_p and kappa.

ManningFriction

Manning bed friction as a bottom closure for depth-averaged SWE; expression is the rate -g n^2 |u| / max(h, h_floor)^(1/3) multiplying each velocity component.

EddyViscosity

Horizontal eddy viscosity closure for the depth-averaged mixing div(nu h grad u); expression returns the isotropic nu and the SWE model builds the diffusion tensor from it.

StressFree

Stress-free free surface, tau(1) = 0.

InterfaceQuantity

The interface value an InterfaceFlux scheme supplies.

InterfaceFlux

Base of the multilayer interface schemes; expression(below, above, G) takes the two one-sided interface traces and the interface mass flux G.

MeanInterface

Central interface velocity u* = (u_below + u_above)/2.

UpwindInterface

Donor interface velocity chosen by the sign of the interface flux, u* = u_below if G >= 0 else u_above.

Functions#

full_tensor_closures

The bulk closures that supply the full stress tensor when the model keeps it (stress is Stress.full; a model’s choice is m.stress, set by moment_scaling), else [].

inplane_stress

In-plane stress relative to the vertical normal stress, tau_de - delta_de tau_zz, summed over the full-tensor closures at the trace of s (nh x nh nested list).

apply_stress_closures

Inject the bulk, bed and surface stress closures into the projected momentum equations of SME/VAM, frame-aware and dimension-agnostic. With the full tensor (Stress.full) the bed and surface tractions carry the in-plane stress too (_inplane_traction) and the bulk shear its rho nu d_d w part.

apply_full_tensor

With Stress.full, substitute the full-tensor closure’s stress (physical, fixed-z coordinates, before the sigma map) into the blueprint momentum: tau_de and tau_zz in the horizontal rows, tau_ez and tau_zz in the vertical row (its divergence evaluated). The horizontal shear tau_dz stays a closure variable, closed after the projection (apply_stress_closures: bed / surface traction, bulk). In the vertical row the rho nu d_e d_e w term is dropped (Marche 2007, (3.2) -> (3.5)).

apply_layer_stress_closures

Per-layer variant of apply_stress_closures for the multilayer models: the surface closure acts only on the top layer and the bed closure only on the bottom layer.

interface_closure

Return the InterfaceFlux closure for quantity in closures, or None.

interface_traction

Shared, single-valued part of the traction at an internal layer interface z_l, per axis, sum_e sigma*_de d_e z_l - tau*_dz, or None without the full tensor (stress).

swe_closure_state

Build a ClosureState over a depth-averaged SWE model, exposing s.u, s.w, s.h, s.speed (floored horizontal magnitude) and s.par, with velocity desingularised through the hinv aux.

API#

zoomy_core.model.models.closures.full_tensor_closures(closures, stress)#

The bulk closures that supply the full stress tensor when the model keeps it (stress is Stress.full; a model’s choice is m.stress, set by moment_scaling), else [].

Raises

  • ValueError: Stress.full with no bulk closure, or with one that closes only the vertical shear (only Newtonian provides the tensor).

zoomy_core.model.models.closures.inplane_stress(full, s, nh)#

In-plane stress relative to the vertical normal stress, tau_de - delta_de tau_zz, summed over the full-tensor closures at the trace of s (nh x nh nested list).

zoomy_core.model.models.closures.apply_stress_closures(closures, m, axes, state, horiz)#

Inject the bulk, bed and surface stress closures into the projected momentum equations of SME/VAM, frame-aware and dimension-agnostic. With the full tensor (Stress.full) the bed and surface tractions carry the in-plane stress too (_inplane_traction) and the bulk shear its rho nu d_d w part.

Args

  • closures: list of Closure objects.

  • m: derivation model.

  • axes: one {"mx": eq, "tau": FieldHandle, "velname": "u"|"v"} per horizontal momentum component.

  • state: callable state(at, *, alias=None, btag=None) -> ClosureState.

  • horiz: horizontal coordinates.

Returns

True iff a bulk closure was applied.

zoomy_core.model.models.closures.apply_full_tensor(closures, m, momentum)#

With Stress.full, substitute the full-tensor closure’s stress (physical, fixed-z coordinates, before the sigma map) into the blueprint momentum: tau_de and tau_zz in the horizontal rows, tau_ez and tau_zz in the vertical row (its divergence evaluated). The horizontal shear tau_dz stays a closure variable, closed after the projection (apply_stress_closures: bed / surface traction, bulk). In the vertical row the rho nu d_e d_e w term is dropped (Marche 2007, (3.2) -> (3.5)).

Returns

The tensor {key: tau}, or None with Stress.shallow (no-op).

class zoomy_core.model.models.closures.Closure(name=None)#

Bases: zoomy_core.model.operations.Operation

Base class of a one-component stress closure; subclasses set the immutable closes / requires and implement expression.

Initialization

Name the closure after its class and describe it by the component it closes.

closes#

None

requires#

()

register(model)#

Register-or-query the model parameters this closure needs (override).

check(model)#

Assert every field in requires is present in the model.

abstract expression(s)#

Return the symbolic relation for this stress component over the ClosureState s (override).

friction_velocity(s)#

Bed friction velocity u_star this closure defines, or None; bed drag laws override it so an eddy viscosity can consume it.

traction(s)#

Boundary traction in the local frame {n, t_a}; the default wraps the scalar expression as one tangential component.

Returns

{"normal": n.sigma.n or None, "tangent": [t_a.sigma.n, ...]}, one tangent entry per horizontal direction.

class zoomy_core.model.models.closures.Newtonian(name=None)#

Bases: zoomy_core.model.models.closures.Closure

Incompressible Newtonian fluid, tau = rho nu (grad u + grad u^T). expression is the vertical shear rho nu d_z u (all Stress.shallow keeps); with Stress.full the model takes the whole tensor (tensor before the sigma map, stress / normal_stress / shear at the traces), so the depth-averaged in-plane term is div(h (tau - tau_zz I)), i.e. div(nu h (grad u + grad u^T)) + 2 nu grad(h div u) at level 0.

References

  • Marche 2007, eqs. (3.2), (3.5), (5.19), https://doi.org/10.1016/j.euromechflu.2006.04.007

Initialization

Name the closure after its class and describe it by the component it closes.

closes#

‘bulk’

requires#

(‘u’,)

register(m)#

Register the kinematic viscosity nu.

expression(s)#

Return rho nu d_z u.

tensor(m, uvel, w, horiz)#

The tensor in physical (fixed-z) coordinates, before the sigma map, {key: tau} for the blueprint keys ("xx", "xy", "yy", "xz", "yz", "zz"): tau_de = rho nu (d_d u_e + d_e u_d), tau_dz = rho nu (d_z u_d + d_d w), tau_zz = 2 rho nu d_z w = -2 rho nu div_h u (incompressible).

stress(s)#

In-plane tensor tau_de = rho nu (d_d u_e + d_e u_d) at the trace of s, with the physical (fixed-z) derivatives of the sigma map; it enters the boundary traction (apply_stress_closures).

normal_stress(s)#

Viscous normal stress tau_zz = 2 rho nu d_z w = -2 rho nu div_h u at the trace of s (physical derivatives).

shear(s)#

The rho nu d_d w part of the shear tau_dz at the trace of s (physical derivative), per axis; expression is the other part.

class zoomy_core.model.models.closures.KEpsilonViscosity(name=None, wall_floor=True)#

Bases: zoomy_core.model.models.closures.Closure

Bulk stress tau = rho nu_t d_z u with the k-epsilon eddy viscosity nu_t = C_mu k^2/epsilon; needs k and varepsilon fields and quadrature_order > 0.

Args

  • wall_floor: add the near-wall mixing-length viscosity C_mu^(1/4) sqrt(k) kappa z_p so the stress still damps the velocity moments at the wall.

References

  • Launder, Spalding 1974, https://doi.org/10.1016/0045-7825(74)90029-2

Initialization

Store the wall_floor switch.

closes#

‘bulk’

requires#

(‘u’, ‘k’, ‘varepsilon’)

register(m)#

Register C_mu, nu and, with wall_floor, kappa and z_p.

expression(s)#

Return rho (nu + nu_t) d_z u with nu_t = C_mu k^2/epsilon plus the optional wall floor.

class zoomy_core.model.models.closures.QRViscosity(name=None, wall_floor=True)#

Bases: zoomy_core.model.models.closures.Closure

Bulk stress for the positivity-preserving q-r k-epsilon model, with nu_t = C_mu sk^4/se^2 for the transported sk = sqrt(k), se = sqrt(epsilon); needs quadrature_order > 0.

Args

  • wall_floor: add the near-wall mixing-length viscosity, as in KEpsilonViscosity.

References

  • Fe et al. 2009, https://doi.org/10.1002/fld.1880

Initialization

Store the wall_floor switch.

closes#

‘bulk’

requires#

(‘u’, ‘sk’, ‘se’)

register(m)#

Register C_mu, nu, the floor eps_min and, with wall_floor, kappa and z_p.

expression(s)#

Return rho (nu + nu_t) d_z u with nu_t = C_mu sk^4/(se^2 + eps_min) plus the optional wall floor.

class zoomy_core.model.models.closures.Bingham(name=None)#

Bases: zoomy_core.model.models.closures.Closure

Regularized Bingham bulk stress tau = (rho nu + tau_y/sqrt((d_z u)^2 + eps^2)) d_z u, rigid below the yield stress tau_y as eps -> 0. Not analytically integrable, so build the model with quadrature_order > 0.

Initialization

Name the closure after its class and describe it by the component it closes.

closes#

‘bulk’

requires#

(‘u’,)

register(m)#

Register nu, the yield stress tau_y and the regularization scale eps_reg.

expression(s)#

Return the regularized Bingham stress.

class zoomy_core.model.models.closures.ElderViscosity(name=None, friction=None)#

Bases: zoomy_core.model.models.closures.Closure

Algebraic Elder eddy viscosity nu_t = kappa u_star h zeta (1 - zeta), polynomial in zeta so the Galerkin projection closes analytically.

Args

  • friction: optional bed closure whose live friction_velocity replaces the u_star parameter.

References

  • Elder 1959, https://doi.org/10.1017/s0022112059000374

Initialization

Store the optional bed closure supplying u_star.

closes#

‘bulk’

requires#

(‘u’,)

register(m)#

Register kappa, u_star, nu and the parameters of the bed closure, if any.

expression(s)#

Return rho (nu + nu_t) d_z u with the Elder nu_t.

class zoomy_core.model.models.closures.NavierSlip(name=None)#

Bases: zoomy_core.model.models.closures.Closure

Linear Navier slip at the bed, tau_b = lambda_s u_b.

Initialization

Name the closure after its class and describe it by the component it closes.

closes#

‘bottom’

requires#

(‘u’,)

register(m)#

Register the slip coefficient lambda_s.

expression(s)#

Return lambda_s u.

traction(s)#

Tangential traction lambda_s u_t per axis (reduces to lambda_s u_i under small-slope scaling).

class zoomy_core.model.models.closures.RoughWall(name=None)#

Bases: zoomy_core.model.models.closures.Closure

Turbulent rough-wall bed drag tau_b = rho C_f u_b |u_b| with C_f = (kappa/ln(z_p/z_0))^2 and z_0 = k_s/30. Needs k_s (Nikuradse roughness), z_p and kappa.

Initialization

Name the closure after its class and describe it by the component it closes.

closes#

‘bottom’

requires#

(‘u’,)

register(m)#

Register kappa, k_s and z_p.

expression(s)#

Return rho C_f u |u|.

friction_velocity(s)#

Recovered bed friction velocity sqrt(C_f) |u_b| of the horizontal bed trace, consumed by ElderViscosity.

traction(s)#

Quadratic bed drag as a vector traction rho C_f |U_t| u_t opposing the slip velocity, coupling the x/y components through |U_t|.

class zoomy_core.model.models.closures.WallFunctionBed(name=None)#

Bases: zoomy_core.model.models.closures.Closure

k-epsilon rough-wall-function bed stress tau_b = rho C_f |U_p| U_p with the log-law C_f of RoughWall but the velocity U_p read at the near-wall height zeta_p = z_p/h rather than at the bed trace. Needs k_s, z_p and kappa.

Initialization

Name the closure after its class and describe it by the component it closes.

closes#

‘bottom’

requires#

(‘u’,)

register(m)#

Register kappa, k_s and z_p.

expression(s)#

Scalar 1-D drag rho C_f U_p |U_p| from the near-wall velocity.

traction(s)#

Vector bed traction rho C_f |U_p| U_p from the tangential velocity at zeta_p = z_p/h.

class zoomy_core.model.models.closures.ManningFriction(name=None, h_floor=0.0)#

Bases: zoomy_core.model.models.closures.Closure

Manning bed friction as a bottom closure for depth-averaged SWE; expression is the rate -g n^2 |u| / max(h, h_floor)^(1/3) multiplying each velocity component.

Args

  • h_floor: depth floor that keeps friction finite at the wet/dry interface.

Initialization

Store the depth floor h_floor.

closes#

‘bottom’

requires#

(‘u’, ‘h’)

register(m)#

Register g and the Manning coefficient n.

expression(s)#

Return the friction rate (per unit velocity) for the depth-averaged SWE.

traction(s)#

Bed shear stress vector rho g n^2 |U_t| u_t / h^(1/3) for the moment hierarchy, the Manning analogue of RoughWall.traction.

class zoomy_core.model.models.closures.EddyViscosity(name=None)#

Bases: zoomy_core.model.models.closures.Closure

Horizontal eddy viscosity closure for the depth-averaged mixing div(nu h grad u); expression returns the isotropic nu and the SWE model builds the diffusion tensor from it.

Initialization

Name the closure after its class and describe it by the component it closes.

closes#

‘horizontal’

requires#

(‘u’,)

register(m)#

Register the kinematic viscosity nu.

expression(s)#

Return nu.

class zoomy_core.model.models.closures.StressFree(name=None)#

Bases: zoomy_core.model.models.closures.Closure

Stress-free free surface, tau(1) = 0.

Initialization

Name the closure after its class and describe it by the component it closes.

closes#

‘surface’

requires#

()

expression(s)#

Return zero.

traction(s)#

Zero tangential traction on every axis; the free-surface pressure is left to the hydrostatic step.

class zoomy_core.model.models.closures.InterfaceQuantity#

Bases: zoomy_core.misc.options.Option

The interface value an InterfaceFlux scheme supplies.

Initialization

Initialize self. See help(type(self)) for accurate signature.

velocity#

‘velocity’

stress#

‘stress’

class zoomy_core.model.models.closures.InterfaceFlux(name=None, quantity=None)#

Bases: zoomy_core.model.models.closures.Closure

Base of the multilayer interface schemes; expression(below, above, G) takes the two one-sided interface traces and the interface mass flux G.

Args

  • quantity: the InterfaceQuantity it supplies.

Initialization

Store the quantity this scheme supplies (default: the transfer velocity).

closes#

‘interface’

requires#

()

abstract expression(below, above, G)#

Return the shared interface velocity u* (override).

class zoomy_core.model.models.closures.MeanInterface(name=None, quantity=None)#

Bases: zoomy_core.model.models.closures.InterfaceFlux

Central interface velocity u* = (u_below + u_above)/2.

Initialization

Store the quantity this scheme supplies (default: the transfer velocity).

expression(below, above, G)#

Return the arithmetic mean of the two traces.

class zoomy_core.model.models.closures.UpwindInterface(name=None, quantity=None)#

Bases: zoomy_core.model.models.closures.InterfaceFlux

Donor interface velocity chosen by the sign of the interface flux, u* = u_below if G >= 0 else u_above.

References

  • Audusse et al. 2011, https://doi.org/10.1051/m2an/2010036

Initialization

Store the quantity this scheme supplies (default: the transfer velocity).

expression(below, above, G)#

Return the upwind trace selected by the sign of G.

zoomy_core.model.models.closures.apply_layer_stress_closures(closures, m, axes, state, *, is_top, is_bottom)#

Per-layer variant of apply_stress_closures for the multilayer models: the surface closure acts only on the top layer and the bed closure only on the bottom layer.

With the full tensor (Stress.full) every trace carries the layer’s own in-plane traction (_inplane_traction), so the layer’s in-plane term is d_e(h_l <tau_de - delta_de tau_zz>), and the bulk shear its rho nu d_d w part; at an internal interface the trace is that own part only, and the model adds the shared, single-valued rest of the traction (interface_traction).

Args

  • is_top: this layer carries the free surface.

  • is_bottom: this layer sits on the bed.

Returns

True iff a bulk closure was applied.

zoomy_core.model.models.closures.interface_closure(closures, quantity=InterfaceQuantity.velocity)#

Return the InterfaceFlux closure for quantity in closures, or None.

zoomy_core.model.models.closures.interface_traction(closures, stress, below, above, G)#

Shared, single-valued part of the traction at an internal layer interface z_l, per axis, sum_e sigma*_de d_e z_l - tau*_dz, or None without the full tensor (stress).

sigma* (of tau_de - delta_de tau_zz) and tau* (of the shear tau_dz) are the interface values of the two one-sided traces (below: top trace of the lower layer, above: bottom trace of the upper layer, both ClosureState) by the InterfaceFlux closure with quantity=InterfaceQuantity.stress, by default their mean. Each layer’s trace carries only its own in-plane part (apply_layer_stress_closures); the model adds this shared part like the transfer term (+ at the top of the layer below, - at the bottom of the layer above), so the interface traction tau*_dz - sigma*_de d_e z_l is single-valued and the layer terms telescope.

zoomy_core.model.models.closures.swe_closure_state(model)#

Build a ClosureState over a depth-averaged SWE model, exposing s.u, s.w, s.h, s.speed (floored horizontal magnitude) and s.par, with velocity desingularised through the hinv aux.