Version: dev

HoltslagBovilleSurfaceFlux

Parameterized atmospheric-boundary-layer surface fluxes following Holtslag & Boville (1993), 'Local Versus Nonlocal Boundary-Layer Diffusion in a Global Climate Model', J. Climate 6, 1825-1842. A diagnostic (purely algebraic) bulk-aerodynamic scheme: from the lowest-model-level wind (u₁, v₁), the surface and lowest-level potential and virtual potential temperatures (θ₀/θ₁, θᵥ₀/θᵥ₁), and specific humidities (q₀, q₁), it computes the kinematic surface fluxes of momentum (wu₀, wv₀; Eqs. 2.1-2.2), heat (wθ₀; Eq. 2.3) and moisture (wq₀; Eq. 2.4). The stability-dependent exchange coefficients Cₘ and Cₕ (Eqs. 2.5-2.6) are the neutral coefficient Cₙ (Eq. 2.7, a logarithmic function of the roughness ratio) modulated by the stability functions fₘ and fₕ, which switch on the sign of the surface bulk Richardson number Ri₀ (Eq. 2.8): the unstable branch uses the Louis et al. (1982) forms (Eqs. 2.9-2.10) and the stable branch the Holtslag & Beljaars (1989) form (Eq. 2.11). The model has 11 algebraic variables (no time integration), 11 equations, and 15 parameters (4 physical constants + 11 inputs). All quantities are SI. Migrated from AtmosphericDynamics.jl@816cc391 src/holtslag_boville_1993.jl:35 (`HoltslagBovilleSurfaceFlux` @component).

Domain
atmospheric_dynamics
Subdomain
holtslag_boville
Type
model
.esm schema
1.0.0
Source
components/atmospheric_dynamics/holtslag_boville/surface_flux.esm
Tags
#atmospheric_dynamics #holtslag_boville #model #boundary-layer #surface-flux #holtslag-boville-1993 #bulk-aerodynamic #algebraic-system

Description

Parameterized atmospheric-boundary-layer surface fluxes following Holtslag & Boville (1993), ‘Local Versus Nonlocal Boundary-Layer Diffusion in a Global Climate Model’, J. Climate 6, 1825-1842. A diagnostic (purely algebraic) bulk-aerodynamic scheme: from the lowest-model-level wind (u₁, v₁), the surface and lowest-level potential and virtual potential temperatures (θ₀/θ₁, θᵥ₀/θᵥ₁), and specific humidities (q₀, q₁), it computes the kinematic surface fluxes of momentum (wu₀, wv₀; Eqs. 2.1-2.2), heat (wθ₀; Eq. 2.3) and moisture (wq₀; Eq. 2.4). The stability-dependent exchange coefficients Cₘ and Cₕ (Eqs. 2.5-2.6) are the neutral coefficient Cₙ (Eq. 2.7, a logarithmic function of the roughness ratio) modulated by the stability functions fₘ and fₕ, which switch on the sign of the surface bulk Richardson number Ri₀ (Eq. 2.8): the unstable branch uses the Louis et al. (1982) forms (Eqs. 2.9-2.10) and the stable branch the Holtslag & Beljaars (1989) form (Eq. 2.11). The model has 11 algebraic variables (no time integration), 11 equations, and 15 parameters (4 physical constants + 11 inputs). All quantities are SI. Migrated from AtmosphericDynamics.jl@816cc391 src/holtslag_boville_1993.jl:35 (HoltslagBovilleSurfaceFlux @component).

Reference

Holtslag, A. A. M., & Boville, B. A. (1993). Local Versus Nonlocal Boundary-Layer Diffusion in a Global Climate Model. Journal of Climate, 6(10), 1825-1842, Section 2 (Eqs. 2.1-2.11).

_source_ref: AtmosphericDynamics.jl@816cc3918e1eef2c2ee6d8fa8dd73c8f13a85a47:src/holtslag_boville_1993.jl:35

Upstream HoltslagBovilleSurfaceFlux is an MTK @component returning System(eqs, t; name) with 4 @constants (g, κ, V_min_sq, zero_Ri), 11 @parameters (z₁, z₀ₘ, u₁, v₁, θ₀, θ₁, θᵥ₀, θᵥ₁, q₀, q₁, Dw), 11 @variables (V₁, Ri₀, Cₙ, fₘ, fₕ, Cₘ, Cₕ, wu₀, wv₀, wθ₀, wq₀), and 11 equations. Every equation is algebraic — there is no D(...) time derivative anywhere — so the system is a diagnostic surface-flux scheme: given the lowest-model-level state it returns the surface fluxes by direct substitution.

Structural shape (matches upstream test SurfaceFlux structure, test/holtslag_boville_1993_test.jl:14-43): length(equations()) == 11, length(unknowns()) == 11, length(parameters()) == 15. The .esm preserves this exactly. All 11 @variables are encoded as type: state and carry no default: each is the LHS of one non-differential equation — an algebraic state determined by its equation, not initial-valued. After mtkcompile the upstream system moves all 11 to its observed set; the canonical Python runner resolves them through its topological algebraic-state substitution loop (the equation set is a DAG: V₁ and Cₙ depend only on parameters, Ri₀ on V₁, fₘ/fₕ on Ri₀ and Cₙ, Cₘ/Cₕ on Cₙ and fₘ/fₕ, and the four fluxes on Cₘ/Cₕ and V₁). The 4 MTK @constants and the 11 @parameters together appear in ModelingToolkit.parameters(sys), so all 15 are encoded as type: parameter; the .esm schema has no dedicated constant variable type, and physical constants are carried as parameters — the same treatment used in the sibling general_circulation.esm migration (Omega, R_earth, g).

Local intermediate (not a variable): the upstream component defines a plain Julia local z_ratio = (z₁ + z₀ₘ) / z₀ₘ — the dimensionless roughness ratio (z₁+z₀ₘ)/z₀ₘ. It is neither a @variable nor a @parameter, so it is NOT carried as a 12th variable (that would break the length(equations()) == 11 / length(unknowns()) == 11 structural invariant). It is inlined verbatim as the sub-expression (z₁ + z₀ₘ) / z₀ₘ everywhere it appears — in the Cₙ equation and in the unstable branch of both the fₘ and fₕ equations.

Equation set (matches src/holtslag_boville_1993.jl:110-150 verbatim): V₁ = sqrt(u₁² + v₁² + V_min_sq) — horizontal wind speed with the V_min_sq = 1 m²/s² floor that keeps the Ri₀ and flux denominators away from 0 in calm conditions. Cₙ = κ² / (log((z₁+z₀ₘ)/z₀ₘ))² — Eq. 2.7, neutral exchange coefficient. Ri₀ = g·z₁·(θᵥ₁ − θᵥ₀) / (θ₁·V₁²) — Eq. 2.8, surface bulk Richardson number; negative for unstable (θᵥ₁ < θᵥ₀), positive for stable. fₘ = ifelse(Ri₀ < zero_Ri, 1 − 10·Ri₀/(1 + 75·Cₙ·sqrt((z₁+z₀ₘ)/z₀ₘ·|Ri₀|)), 1/(1 + 10·Ri₀·(1 + 8·Ri₀))) — Eq. 2.9 (unstable, Louis et al. 1982) / Eq. 2.11 (stable, Holtslag & Beljaars 1989). fₕ = ifelse(Ri₀ < zero_Ri, 1 − 15·Ri₀/(1 + 75·Cₙ·sqrt((z₁+z₀ₘ)/z₀ₘ·|Ri₀|)), 1/(1 + 10·Ri₀·(1 + 8·Ri₀))) — Eq. 2.10 (unstable) / Eq. 2.11 (stable); the only difference from fₘ is the numerator coefficient 15 vs 10, so heat mixing is more strongly enhanced than momentum in unstable air. Cₘ = Cₙ·fₘ — Eq. 2.5, momentum exchange coefficient. Cₕ = Cₙ·fₕ — Eq. 2.6, heat exchange coefficient. wu₀ = −Cₘ·V₁·u₁ — Eq. 2.1, surface momentum flux (u-component); opposes the wind. wv₀ = −Cₘ·V₁·v₁ — Eq. 2.2, surface momentum flux (v-component); opposes the wind. wθ₀ = Cₕ·V₁·(θ₀ − θ₁) — Eq. 2.3, surface heat flux; up (positive) when the surface is warmer than the air. wq₀ = Dw·Cₕ·V₁·(q₀ − q₁) — Eq. 2.4, surface moisture flux, scaled by the water-availability factor Dw. The ifelse, sqrt, abs, log, <, ^ ops are all first-class ESS expression ops (esm-schema.json ExpressionNode op enum). zero_Ri (= 0) is the upstream @constants comparison threshold; the strict Ri₀ < zero_Ri test sends exactly-neutral conditions (Ri₀ = 0) into the stable branch, where 1/(1 + 0) = 1, so fₘ = fₕ = 1 — the correct neutral limit.

Well-posedness (no singularities): V₁ ≥ sqrt(V_min_sq) = 1 m/s always, so the V₁² denominator of Ri₀ is bounded away from 0; z₁ > 0 and z₀ₘ > 0 give (z₁+z₀ₘ)/z₀ₘ > 1, so log(·) > 0 and Cₙ is finite and positive; the unstable-branch denominator 1 + 75·Cₙ·sqrt(·) ≥ 1; the stable-branch denominator 1 + 10·Ri₀·(1 + 8·Ri₀) = 1 + 10·Ri₀ + 80·Ri₀² has discriminant 100 − 320 < 0 and is therefore strictly positive for every real Ri₀. Every variable is a finite closed-form function of the parameters at every test point, so all inline tests are simulated directly (unlike general_circulation.esm, this model has no equatorial-style 0/0 point to avoid).

Tolerance rationale (model-level rel=1e-6, abs=1e-9): a diagnostic algebraic system with no ODE integration — the assertion values are the closed-form evaluation of the upstream equations in IEEE-754 double precision. rel=1e-6 covers float64 round-off in the trig-free closed-form expressions (log, sqrt, ^, divisions) and any sub-ULP libm difference between the Julia walker and the Python gate-of-record; abs=1e-9 covers the intended exact zeros — Ri₀ = 0 and wθ₀ = 0 under exactly-neutral stratification (θᵥ₁ = θᵥ₀, θ₀ = θ₁), and wv₀ = 0 when the meridional wind v₁ = 0 — for which a pure relative bound is undefined. This rel/abs pair is the same one used by the sibling algebraic migration general_circulation.esm.

Upstream test reproduction (test/holtslag_boville_1993_test.jl). The 9 inline tests pin all 11 variables for each distinct parameter configuration exercised by the upstream @testitems; assertion values are the closed-form evaluation of the upstream equations:

  • SurfaceFlux structure (lines 14-43) — reproduced by the structural shape: 11 type: state variables, 15 type: parameter variables, 11 entries in equations.
  • SurfaceFlux neutral conditions (lines 105-143) — reproduced by neutral_conditions: θᵥ₁ = θᵥ₀ gives Ri₀ = 0, fₘ = fₕ = 1, Cₘ = Cₕ = Cₙ; the pinned Cₙ = 0.0075119… matches the upstream hand-computed κ²/(log(101))² check.
  • SurfaceFlux stable conditions (lines 145-180) and SurfaceFlux stable stability function numerical Eq. 2.11 (lines 1057-1082) — reproduced by stable_conditions and stable_stability_function: Ri₀ > 0, fₘ = fₕ < 1, wθ₀ < 0 (downward heat flux).
  • SurfaceFlux unstable conditions (lines 182-215) and SurfaceFlux unstable stability function numerical Eq. 2.9-2.10 (lines 1021-1055) — reproduced by unstable_conditions: Ri₀ < 0, fₘ > 1, fₕ > 1, fₕ > fₘ, wθ₀ > 0 (upward heat flux).
  • SurfaceFlux momentum flux direction (lines 217-242) — reproduced by momentum_flux_direction: with u₁ = 5, v₁ = 3 both wu₀ < 0 and wv₀ < 0, the flux opposing the wind.
  • SurfaceFlux Ri₀ numerical verification Eq. 2.8 (lines 244-270) — reproduced by richardson_number: u₁ = 3, v₁ = 4, z₁ = 20, z₀ₘ = 0.01 give the pinned Ri₀ = 0.12576…, matching g·z₁·(θᵥ₁−θᵥ₀)/(θ₁·V₁²) with V₁ = sqrt(26).
  • SurfaceFlux moisture flux Eq. 2.4 (lines 272-304) — reproduced by moisture_flux: q₀ = 0.015, q₁ = 0.01, Dw = 0.8 give wq₀ = Dw·Cₕ·V₁·(q₀−q₁) > 0 (evaporation).
  • SurfaceFlux numerical flux values Eq. 2.1-2.3 (lines 306-337) — reproduced by numerical_flux_values: neutral wind/virtual-temperature setup with θ₀ = 305 > θ₁ = 300 gives wu₀ = −Cₘ·V₁·u₁ and wθ₀ = Cₕ·V₁·5. The default_parameters test additionally pins all 11 variables at the declared defaults (u₁ = 1, v₁ = 0, θᵥ₀ = 300 > θᵥ₁ = 299 — slightly unstable), verifying the default wiring.

Unit balance (verified): V₁ is sqrt(m²/s²) = m/s; (z₁+z₀ₘ)/z₀ₘ is m/m = 1, so Cₙ = κ²/(log 1)² is dimensionless; Ri₀ = (m/s²)·m·K/(K·m²/s²) = 1, dimensionless; fₘ/fₕ are dimensionless (the sqrt argument (z₁+z₀ₘ)/z₀ₘ·|Ri₀| is dimensionless); Cₘ/Cₕ are dimensionless; wu₀/wv₀ = 1·(m/s)·(m/s) = m²/s²; wθ₀ = 1·(m/s)·K = K·m/s; wq₀ = 1·1·(m/s)·1 = m/s. SI throughout; no unit conversion at parse or runtime._

Parameters

NameUnitsDefaultDescription
gm/s^29.81Gravitational acceleration. Upstream MTK @constants; appears in the surface bulk Richardson number Ri₀ (Eq. 2.8).
κ10.4Von Karman constant (dimensionless). Upstream MTK @constants; sets the neutral exchange coefficient Cₙ = κ²/[ln((z₁+z₀ₘ)/z₀ₘ)]² (Eq. 2.7).
V_min_sqm^2/s^21.0Minimum wind speed squared. Upstream MTK @constants; the floor added inside V₁ = sqrt(u₁² + v₁² + V_min_sq) so the Richardson-number and flux denominators stay finite in calm conditions.
zero_Ri10.0Zero Richardson number, the stability-branch threshold (dimensionless). Upstream MTK @constants; the stability functions use the unstable branch when Ri₀ < zero_Ri and the stable branch otherwise (so exactly-neutral Ri₀ = 0 takes the stable branch, giving fₘ = fₕ = 1).
z₁m10.0Height of the lowest model level.
z₀ₘm0.0001Roughness length for momentum. Enters the neutral exchange coefficient through the roughness ratio (z₁+z₀ₘ)/z₀ₘ (Eq. 2.7).
u₁m/s1.0Zonal (eastward) wind component at the lowest model level.
v₁m/s0.0Meridional (northward) wind component at the lowest model level.
θ₀K300.0Potential temperature at the surface. Drives the surface heat flux wθ₀ (Eq. 2.3).
θ₁K299.0Potential temperature at the lowest model level. Appears in the surface heat flux (Eq. 2.3) and as the denominator reference temperature of the bulk Richardson number (Eq. 2.8).
θᵥ₀K300.0Virtual potential temperature at the surface. Sets the surface-air buoyancy contrast in the bulk Richardson number (Eq. 2.8).
θᵥ₁K299.0Virtual potential temperature at the lowest model level. With θᵥ₁ < θᵥ₀ the air is unstable (Ri₀ < 0); with θᵥ₁ > θᵥ₀ it is stable (Ri₀ > 0).
q₀10.01Specific humidity at the surface (dimensionless mass mixing ratio). Drives the surface moisture flux wq₀ (Eq. 2.4).
q₁10.008Specific humidity at the lowest model level (dimensionless mass mixing ratio).
Dw11.0Water availability factor (dimensionless). Scales the surface moisture flux wq₀ (Eq. 2.4); 1 for a saturated surface, smaller for a moisture-limited one.

Observed

NameUnitsDefaultDescription
V₁m/sHorizontal wind speed at the lowest model level: V₁ = sqrt(u₁² + v₁² + V_min_sq). The V_min_sq floor keeps V₁ ≥ 1 m/s so the bulk-aerodynamic denominators stay finite in calm conditions.
Ri₀1Surface bulk Richardson number (Eq. 2.8), dimensionless. Negative for unstable stratification (θᵥ₁ < θᵥ₀), positive for stable; its sign selects the unstable vs stable branch of the stability functions.
Cₙ1Neutral exchange coefficient (Eq. 2.7), dimensionless: Cₙ = κ²/[ln((z₁+z₀ₘ)/z₀ₘ)]². The drag coefficient that would apply under neutral stratification; increases with surface roughness.
fₘ1Momentum stability function (dimensionless), Eq. 2.9 (unstable) / Eq. 2.11 (stable). fₘ > 1 enhances mixing in unstable air, fₘ < 1 suppresses it in stable air.
fₕ1Heat stability function (dimensionless), Eq. 2.10 (unstable) / Eq. 2.11 (stable). Differs from fₘ only by the unstable-branch coefficient 15 vs 10, so heat mixing is more strongly enhanced than momentum (fₕ > fₘ when unstable); fₕ = fₘ when stable.
Cₘ1Momentum exchange coefficient (Eq. 2.5), dimensionless: Cₘ = Cₙ·fₘ.
Cₕ1Heat exchange coefficient (Eq. 2.6), dimensionless: Cₕ = Cₙ·fₕ.
wu₀m^2/s^2Surface kinematic momentum flux, u-component (Eq. 2.1): wu₀ = −Cₘ·V₁·u₁. Negative for a positive zonal wind — the flux removes momentum from the flow (surface drag).
wv₀m^2/s^2Surface kinematic momentum flux, v-component (Eq. 2.2): wv₀ = −Cₘ·V₁·v₁. Negative for a positive meridional wind; zero when v₁ = 0.
wθ₀K*m/sSurface kinematic heat flux (Eq. 2.3): wθ₀ = Cₕ·V₁·(θ₀ − θ₁). Positive (upward) when the surface is warmer than the air, negative (downward) when it is cooler.
wq₀m/sSurface kinematic moisture flux (Eq. 2.4): wq₀ = Dw·Cₕ·V₁·(q₀ − q₁). Positive (evaporation) when the surface is moister than the air.

Observed expressions

$$ V₁ = \sqrt{u₁^{2.0} + v₁^{2.0} + V_{min\,sq}} $$

Horizontal wind speed at the lowest model level: V₁ = sqrt(u₁² + v₁² + V_min_sq). The V_min_sq floor keeps V₁ ≥ 1 m/s so the bulk-aerodynamic denominators stay finite in calm conditions.

$$ Ri₀ = \frac{g \cdot z₁ \cdot \left(θᵥ₁ - θᵥ₀\right)}{θ₁ \cdot V₁^{2.0}} $$

Surface bulk Richardson number (Eq. 2.8), dimensionless. Negative for unstable stratification (θᵥ₁ < θᵥ₀), positive for stable; its sign selects the unstable vs stable branch of the stability functions.

$$ Cₙ = \frac{κ^{2.0}}{\ln\left(\frac{z₁ + z₀ₘ}{z₀ₘ}\right)^{2.0}} $$

Neutral exchange coefficient (Eq. 2.7), dimensionless: Cₙ = κ²/[ln((z₁+z₀ₘ)/z₀ₘ)]². The drag coefficient that would apply under neutral stratification; increases with surface roughness.

$$ fₘ = \mathrm{ifelse}\left(\mathrm{<}\left(Ri₀, zero_{Ri}\right), 1.0 - \frac{10.0 \cdot Ri₀}{1.0 + 75.0 \cdot Cₙ \cdot \sqrt{\frac{z₁ + z₀ₘ}{z₀ₘ} \cdot \left|Ri₀\right|}}, \frac{1.0}{1.0 + 10.0 \cdot Ri₀ \cdot \left(1.0 + 8.0 \cdot Ri₀\right)}\right) $$

Momentum stability function (dimensionless), Eq. 2.9 (unstable) / Eq. 2.11 (stable). fₘ > 1 enhances mixing in unstable air, fₘ < 1 suppresses it in stable air.

$$ fₕ = \mathrm{ifelse}\left(\mathrm{<}\left(Ri₀, zero_{Ri}\right), 1.0 - \frac{15.0 \cdot Ri₀}{1.0 + 75.0 \cdot Cₙ \cdot \sqrt{\frac{z₁ + z₀ₘ}{z₀ₘ} \cdot \left|Ri₀\right|}}, \frac{1.0}{1.0 + 10.0 \cdot Ri₀ \cdot \left(1.0 + 8.0 \cdot Ri₀\right)}\right) $$

Heat stability function (dimensionless), Eq. 2.10 (unstable) / Eq. 2.11 (stable). Differs from fₘ only by the unstable-branch coefficient 15 vs 10, so heat mixing is more strongly enhanced than momentum (fₕ > fₘ when unstable); fₕ = fₘ when stable.

$$ Cₘ = Cₙ \cdot fₘ $$

Momentum exchange coefficient (Eq. 2.5), dimensionless: Cₘ = Cₙ·fₘ.

$$ Cₕ = Cₙ \cdot fₕ $$

Heat exchange coefficient (Eq. 2.6), dimensionless: Cₕ = Cₙ·fₕ.

$$ wu₀ = \left(-1.0\right) \cdot Cₘ \cdot V₁ \cdot u₁ $$

Surface kinematic momentum flux, u-component (Eq. 2.1): wu₀ = −Cₘ·V₁·u₁. Negative for a positive zonal wind — the flux removes momentum from the flow (surface drag).

$$ wv₀ = \left(-1.0\right) \cdot Cₘ \cdot V₁ \cdot v₁ $$

Surface kinematic momentum flux, v-component (Eq. 2.2): wv₀ = −Cₘ·V₁·v₁. Negative for a positive meridional wind; zero when v₁ = 0.

$$ wθ₀ = Cₕ \cdot V₁ \cdot \left(θ₀ - θ₁\right) $$

Surface kinematic heat flux (Eq. 2.3): wθ₀ = Cₕ·V₁·(θ₀ − θ₁). Positive (upward) when the surface is warmer than the air, negative (downward) when it is cooler.

$$ wq₀ = Dw \cdot Cₕ \cdot V₁ \cdot \left(q₀ - q₁\right) $$

Surface kinematic moisture flux (Eq. 2.4): wq₀ = Dw·Cₕ·V₁·(q₀ − q₁). Positive (evaporation) when the surface is moister than the air.

Analyses

stability_functions

Reproduces the documented ‘Surface Layer Stability Functions (Eq. 2.9-2.11)’ figure from the AtmosphericDynamics.jl Holtslag & Boville (1993) page. The lowest-level virtual potential temperature θᵥ₁ is swept from 290 to 310 K with the surface value held at θᵥ₀ = 300 K, carrying the surface bulk Richardson number Ri₀ from unstable (negative) through neutral (0) to stable (positive). Plotting the momentum and heat stability functions against Ri₀ shows the asymmetric response: in unstable air (Ri₀ < 0) both functions exceed 1 and fₕ rises faster than fₘ (numerator coefficient 15 vs 10); in stable air (Ri₀ > 0) the two coincide and decay below 1. θ₁ is held at 300 K (the stability functions depend on the inputs only through Ri₀, so the curves are independent of how Ri₀ is generated).

Time span: 0.0 → 1.0

Swept over: θᵥ₁

Momentum stability function fₘ as a function of the surface bulk Richardson number Ri₀ — enhanced (> 1) in unstable air, suppressed (< 1) in stable air.

Heat stability function fₕ as a function of the surface bulk Richardson number Ri₀. In unstable air fₕ rises above fₘ; in stable air fₕ = fₘ.

surface_fluxes_vs_wind

Surface momentum and heat fluxes as a function of the lowest-level wind speed. The zonal wind u₁ is swept from 0 to 20 m/s under neutral virtual-temperature stratification (θᵥ₀ = θᵥ₁ = 300 K) with a 2 K surface-air potential-temperature contrast (θ₀ = 302 K, θ₁ = 300 K). The momentum flux wu₀ = −Cₘ·V₁·u₁ grows in magnitude roughly with the square of the wind speed, and the heat flux wθ₀ = Cₕ·V₁·(θ₀−θ₁) grows linearly with V₁ — the bulk-aerodynamic ventilation effect, stronger wind drawing more flux from the surface.

Time span: 0.0 → 1.0

Swept over: u₁

Surface momentum flux wu₀ versus the lowest-level zonal wind u₁ — increasingly negative (stronger drag) as the wind strengthens.

Surface heat flux wθ₀ versus the lowest-level zonal wind u₁ — rising with wind speed through the bulk-aerodynamic ventilation factor V₁.

neutral_coefficient_vs_roughness

The neutral exchange coefficient Cₙ = κ²/[ln((z₁+z₀ₘ)/z₀ₘ)]² as a function of the momentum roughness length z₀ₘ. The roughness length is swept logarithmically from 1e-5 m (very smooth, e.g. open water) to 1e-1 m (rough, e.g. tall vegetation), with the lowest model level held at z₁ = 10 m. As the surface roughens the logarithmic roughness ratio shrinks and Cₙ rises — the surface couples more strongly to the atmosphere (Eq. 2.7).

Time span: 0.0 → 1.0

Swept over: z₀ₘ

Neutral exchange coefficient Cₙ as a function of the momentum roughness length z₀ₘ — monotonically increasing with surface roughness.

Raw .esm

Raw .esm JSON (this component)
{
  "reference": {
    "citation": "Holtslag, A. A. M., & Boville, B. A. (1993). Local Versus Nonlocal Boundary-Layer Diffusion in a Global Climate Model. Journal of Climate, 6(10), 1825-1842, Section 2 (Eqs. 2.1-2.11).",
    "notes": "source_ref: AtmosphericDynamics.jl@816cc3918e1eef2c2ee6d8fa8dd73c8f13a85a47:src/holtslag_boville_1993.jl:35\n\nUpstream `HoltslagBovilleSurfaceFlux` is an MTK `@component` returning `System(eqs, t; name)` with 4 `@constants` (g, κ, V_min_sq, zero_Ri), 11 `@parameters` (z₁, z₀ₘ, u₁, v₁, θ₀, θ₁, θᵥ₀, θᵥ₁, q₀, q₁, Dw), 11 `@variables` (V₁, Ri₀, Cₙ, fₘ, fₕ, Cₘ, Cₕ, wu₀, wv₀, wθ₀, wq₀), and 11 equations. Every equation is algebraic — there is no `D(...)` time derivative anywhere — so the system is a diagnostic surface-flux scheme: given the lowest-model-level state it returns the surface fluxes by direct substitution.\n\nStructural shape (matches upstream test `SurfaceFlux structure`, test/holtslag_boville_1993_test.jl:14-43): `length(equations()) == 11`, `length(unknowns()) == 11`, `length(parameters()) == 15`. The .esm preserves this exactly. All 11 `@variables` are encoded as `type: state` and carry no `default`: each is the LHS of one non-differential equation — an algebraic state determined by its equation, not initial-valued. After `mtkcompile` the upstream system moves all 11 to its `observed` set; the canonical Python runner resolves them through its topological algebraic-state substitution loop (the equation set is a DAG: V₁ and Cₙ depend only on parameters, Ri₀ on V₁, fₘ/fₕ on Ri₀ and Cₙ, Cₘ/Cₕ on Cₙ and fₘ/fₕ, and the four fluxes on Cₘ/Cₕ and V₁). The 4 MTK `@constants` and the 11 `@parameters` together appear in `ModelingToolkit.parameters(sys)`, so all 15 are encoded as `type: parameter`; the .esm schema has no dedicated constant variable type, and physical constants are carried as parameters — the same treatment used in the sibling general_circulation.esm migration (Omega, R_earth, g).\n\nLocal intermediate (not a variable): the upstream component defines a plain Julia local `z_ratio = (z₁ + z₀ₘ) / z₀ₘ` — the dimensionless roughness ratio (z₁+z₀ₘ)/z₀ₘ. It is neither a `@variable` nor a `@parameter`, so it is NOT carried as a 12th variable (that would break the `length(equations()) == 11` / `length(unknowns()) == 11` structural invariant). It is inlined verbatim as the sub-expression `(z₁ + z₀ₘ) / z₀ₘ` everywhere it appears — in the Cₙ equation and in the unstable branch of both the fₘ and fₕ equations.\n\nEquation set (matches src/holtslag_boville_1993.jl:110-150 verbatim):\n  V₁  = sqrt(u₁² + v₁² + V_min_sq)                         — horizontal wind speed with the V_min_sq = 1 m²/s² floor that keeps the Ri₀ and flux denominators away from 0 in calm conditions.\n  Cₙ  = κ² / (log((z₁+z₀ₘ)/z₀ₘ))²                          — Eq. 2.7, neutral exchange coefficient.\n  Ri₀ = g·z₁·(θᵥ₁ − θᵥ₀) / (θ₁·V₁²)                        — Eq. 2.8, surface bulk Richardson number; negative for unstable (θᵥ₁ < θᵥ₀), positive for stable.\n  fₘ  = ifelse(Ri₀ < zero_Ri, 1 − 10·Ri₀/(1 + 75·Cₙ·sqrt((z₁+z₀ₘ)/z₀ₘ·|Ri₀|)), 1/(1 + 10·Ri₀·(1 + 8·Ri₀)))   — Eq. 2.9 (unstable, Louis et al. 1982) / Eq. 2.11 (stable, Holtslag & Beljaars 1989).\n  fₕ  = ifelse(Ri₀ < zero_Ri, 1 − 15·Ri₀/(1 + 75·Cₙ·sqrt((z₁+z₀ₘ)/z₀ₘ·|Ri₀|)), 1/(1 + 10·Ri₀·(1 + 8·Ri₀)))   — Eq. 2.10 (unstable) / Eq. 2.11 (stable); the only difference from fₘ is the numerator coefficient 15 vs 10, so heat mixing is more strongly enhanced than momentum in unstable air.\n  Cₘ  = Cₙ·fₘ                                              — Eq. 2.5, momentum exchange coefficient.\n  Cₕ  = Cₙ·fₕ                                              — Eq. 2.6, heat exchange coefficient.\n  wu₀ = −Cₘ·V₁·u₁                                          — Eq. 2.1, surface momentum flux (u-component); opposes the wind.\n  wv₀ = −Cₘ·V₁·v₁                                          — Eq. 2.2, surface momentum flux (v-component); opposes the wind.\n  wθ₀ = Cₕ·V₁·(θ₀ − θ₁)                                    — Eq. 2.3, surface heat flux; up (positive) when the surface is warmer than the air.\n  wq₀ = Dw·Cₕ·V₁·(q₀ − q₁)                                 — Eq. 2.4, surface moisture flux, scaled by the water-availability factor Dw.\nThe `ifelse`, `sqrt`, `abs`, `log`, `<`, `^` ops are all first-class ESS expression ops (esm-schema.json ExpressionNode op enum). zero_Ri (= 0) is the upstream `@constants` comparison threshold; the strict `Ri₀ < zero_Ri` test sends exactly-neutral conditions (Ri₀ = 0) into the stable branch, where 1/(1 + 0) = 1, so fₘ = fₕ = 1 — the correct neutral limit.\n\nWell-posedness (no singularities): V₁ ≥ sqrt(V_min_sq) = 1 m/s always, so the V₁² denominator of Ri₀ is bounded away from 0; z₁ > 0 and z₀ₘ > 0 give (z₁+z₀ₘ)/z₀ₘ > 1, so log(·) > 0 and Cₙ is finite and positive; the unstable-branch denominator 1 + 75·Cₙ·sqrt(·) ≥ 1; the stable-branch denominator 1 + 10·Ri₀·(1 + 8·Ri₀) = 1 + 10·Ri₀ + 80·Ri₀² has discriminant 100 − 320 < 0 and is therefore strictly positive for every real Ri₀. Every variable is a finite closed-form function of the parameters at every test point, so all inline tests are simulated directly (unlike general_circulation.esm, this model has no equatorial-style 0/0 point to avoid).\n\nTolerance rationale (model-level rel=1e-6, abs=1e-9): a diagnostic algebraic system with no ODE integration — the assertion values are the closed-form evaluation of the upstream equations in IEEE-754 double precision. rel=1e-6 covers float64 round-off in the trig-free closed-form expressions (log, sqrt, ^, divisions) and any sub-ULP libm difference between the Julia walker and the Python gate-of-record; abs=1e-9 covers the intended exact zeros — Ri₀ = 0 and wθ₀ = 0 under exactly-neutral stratification (θᵥ₁ = θᵥ₀, θ₀ = θ₁), and wv₀ = 0 when the meridional wind v₁ = 0 — for which a pure relative bound is undefined. This rel/abs pair is the same one used by the sibling algebraic migration general_circulation.esm.\n\nUpstream test reproduction (test/holtslag_boville_1993_test.jl). The 9 inline tests pin all 11 variables for each distinct parameter configuration exercised by the upstream `@testitem`s; assertion values are the closed-form evaluation of the upstream equations:\n- `SurfaceFlux structure` (lines 14-43) — reproduced by the structural shape: 11 `type: state` variables, 15 `type: parameter` variables, 11 entries in `equations`.\n- `SurfaceFlux neutral conditions` (lines 105-143) — reproduced by `neutral_conditions`: θᵥ₁ = θᵥ₀ gives Ri₀ = 0, fₘ = fₕ = 1, Cₘ = Cₕ = Cₙ; the pinned Cₙ = 0.0075119… matches the upstream hand-computed κ²/(log(101))² check.\n- `SurfaceFlux stable conditions` (lines 145-180) and `SurfaceFlux stable stability function numerical Eq. 2.11` (lines 1057-1082) — reproduced by `stable_conditions` and `stable_stability_function`: Ri₀ > 0, fₘ = fₕ < 1, wθ₀ < 0 (downward heat flux).\n- `SurfaceFlux unstable conditions` (lines 182-215) and `SurfaceFlux unstable stability function numerical Eq. 2.9-2.10` (lines 1021-1055) — reproduced by `unstable_conditions`: Ri₀ < 0, fₘ > 1, fₕ > 1, fₕ > fₘ, wθ₀ > 0 (upward heat flux).\n- `SurfaceFlux momentum flux direction` (lines 217-242) — reproduced by `momentum_flux_direction`: with u₁ = 5, v₁ = 3 both wu₀ < 0 and wv₀ < 0, the flux opposing the wind.\n- `SurfaceFlux Ri₀ numerical verification Eq. 2.8` (lines 244-270) — reproduced by `richardson_number`: u₁ = 3, v₁ = 4, z₁ = 20, z₀ₘ = 0.01 give the pinned Ri₀ = 0.12576…, matching g·z₁·(θᵥ₁−θᵥ₀)/(θ₁·V₁²) with V₁ = sqrt(26).\n- `SurfaceFlux moisture flux Eq. 2.4` (lines 272-304) — reproduced by `moisture_flux`: q₀ = 0.015, q₁ = 0.01, Dw = 0.8 give wq₀ = Dw·Cₕ·V₁·(q₀−q₁) > 0 (evaporation).\n- `SurfaceFlux numerical flux values Eq. 2.1-2.3` (lines 306-337) — reproduced by `numerical_flux_values`: neutral wind/virtual-temperature setup with θ₀ = 305 > θ₁ = 300 gives wu₀ = −Cₘ·V₁·u₁ and wθ₀ = Cₕ·V₁·5.\nThe `default_parameters` test additionally pins all 11 variables at the declared defaults (u₁ = 1, v₁ = 0, θᵥ₀ = 300 > θᵥ₁ = 299 — slightly unstable), verifying the default wiring.\n\nUnit balance (verified): V₁ is sqrt(m²/s²) = m/s; (z₁+z₀ₘ)/z₀ₘ is m/m = 1, so Cₙ = κ²/(log 1)² is dimensionless; Ri₀ = (m/s²)·m·K/(K·m²/s²) = 1, dimensionless; fₘ/fₕ are dimensionless (the sqrt argument (z₁+z₀ₘ)/z₀ₘ·|Ri₀| is dimensionless); Cₘ/Cₕ are dimensionless; wu₀/wv₀ = 1·(m/s)·(m/s) = m²/s²; wθ₀ = 1·(m/s)·K = K·m/s; wq₀ = 1·1·(m/s)·1 = m/s. SI throughout; no unit conversion at parse or runtime."
  },
  "tolerance": {
    "rel": 1e-06,
    "abs": 1e-09
  },
  "variables": {
    "g": {
      "type": "parameter",
      "units": "m/s^2",
      "default": 9.81,
      "description": "Gravitational acceleration. Upstream MTK `@constants`; appears in the surface bulk Richardson number Ri₀ (Eq. 2.8)."
    },
    "κ": {
      "type": "parameter",
      "units": "1",
      "default": 0.4,
      "description": "Von Karman constant (dimensionless). Upstream MTK `@constants`; sets the neutral exchange coefficient Cₙ = κ²/[ln((z₁+z₀ₘ)/z₀ₘ)]² (Eq. 2.7)."
    },
    "V_min_sq": {
      "type": "parameter",
      "units": "m^2/s^2",
      "default": 1.0,
      "description": "Minimum wind speed squared. Upstream MTK `@constants`; the floor added inside V₁ = sqrt(u₁² + v₁² + V_min_sq) so the Richardson-number and flux denominators stay finite in calm conditions."
    },
    "zero_Ri": {
      "type": "parameter",
      "units": "1",
      "default": 0.0,
      "description": "Zero Richardson number, the stability-branch threshold (dimensionless). Upstream MTK `@constants`; the stability functions use the unstable branch when Ri₀ < zero_Ri and the stable branch otherwise (so exactly-neutral Ri₀ = 0 takes the stable branch, giving fₘ = fₕ = 1)."
    },
    "z₁": {
      "type": "parameter",
      "units": "m",
      "default": 10.0,
      "description": "Height of the lowest model level."
    },
    "z₀ₘ": {
      "type": "parameter",
      "units": "m",
      "default": 0.0001,
      "description": "Roughness length for momentum. Enters the neutral exchange coefficient through the roughness ratio (z₁+z₀ₘ)/z₀ₘ (Eq. 2.7)."
    },
    "u₁": {
      "type": "parameter",
      "units": "m/s",
      "default": 1.0,
      "description": "Zonal (eastward) wind component at the lowest model level."
    },
    "v₁": {
      "type": "parameter",
      "units": "m/s",
      "default": 0.0,
      "description": "Meridional (northward) wind component at the lowest model level."
    },
    "θ₀": {
      "type": "parameter",
      "units": "K",
      "default": 300.0,
      "description": "Potential temperature at the surface. Drives the surface heat flux wθ₀ (Eq. 2.3)."
    },
    "θ₁": {
      "type": "parameter",
      "units": "K",
      "default": 299.0,
      "description": "Potential temperature at the lowest model level. Appears in the surface heat flux (Eq. 2.3) and as the denominator reference temperature of the bulk Richardson number (Eq. 2.8)."
    },
    "θᵥ₀": {
      "type": "parameter",
      "units": "K",
      "default": 300.0,
      "description": "Virtual potential temperature at the surface. Sets the surface-air buoyancy contrast in the bulk Richardson number (Eq. 2.8)."
    },
    "θᵥ₁": {
      "type": "parameter",
      "units": "K",
      "default": 299.0,
      "description": "Virtual potential temperature at the lowest model level. With θᵥ₁ < θᵥ₀ the air is unstable (Ri₀ < 0); with θᵥ₁ > θᵥ₀ it is stable (Ri₀ > 0)."
    },
    "q₀": {
      "type": "parameter",
      "units": "1",
      "default": 0.01,
      "description": "Specific humidity at the surface (dimensionless mass mixing ratio). Drives the surface moisture flux wq₀ (Eq. 2.4)."
    },
    "q₁": {
      "type": "parameter",
      "units": "1",
      "default": 0.008,
      "description": "Specific humidity at the lowest model level (dimensionless mass mixing ratio)."
    },
    "Dw": {
      "type": "parameter",
      "units": "1",
      "default": 1.0,
      "description": "Water availability factor (dimensionless). Scales the surface moisture flux wq₀ (Eq. 2.4); 1 for a saturated surface, smaller for a moisture-limited one."
    },
    "V₁": {
      "type": "unknown",
      "units": "m/s",
      "description": "Horizontal wind speed at the lowest model level: V₁ = sqrt(u₁² + v₁² + V_min_sq). The V_min_sq floor keeps V₁ ≥ 1 m/s so the bulk-aerodynamic denominators stay finite in calm conditions."
    },
    "Ri₀": {
      "type": "unknown",
      "units": "1",
      "description": "Surface bulk Richardson number (Eq. 2.8), dimensionless. Negative for unstable stratification (θᵥ₁ < θᵥ₀), positive for stable; its sign selects the unstable vs stable branch of the stability functions."
    },
    "Cₙ": {
      "type": "unknown",
      "units": "1",
      "description": "Neutral exchange coefficient (Eq. 2.7), dimensionless: Cₙ = κ²/[ln((z₁+z₀ₘ)/z₀ₘ)]². The drag coefficient that would apply under neutral stratification; increases with surface roughness."
    },
    "fₘ": {
      "type": "unknown",
      "units": "1",
      "description": "Momentum stability function (dimensionless), Eq. 2.9 (unstable) / Eq. 2.11 (stable). fₘ > 1 enhances mixing in unstable air, fₘ < 1 suppresses it in stable air."
    },
    "fₕ": {
      "type": "unknown",
      "units": "1",
      "description": "Heat stability function (dimensionless), Eq. 2.10 (unstable) / Eq. 2.11 (stable). Differs from fₘ only by the unstable-branch coefficient 15 vs 10, so heat mixing is more strongly enhanced than momentum (fₕ > fₘ when unstable); fₕ = fₘ when stable."
    },
    "Cₘ": {
      "type": "unknown",
      "units": "1",
      "description": "Momentum exchange coefficient (Eq. 2.5), dimensionless: Cₘ = Cₙ·fₘ."
    },
    "Cₕ": {
      "type": "unknown",
      "units": "1",
      "description": "Heat exchange coefficient (Eq. 2.6), dimensionless: Cₕ = Cₙ·fₕ."
    },
    "wu₀": {
      "type": "unknown",
      "units": "m^2/s^2",
      "description": "Surface kinematic momentum flux, u-component (Eq. 2.1): wu₀ = −Cₘ·V₁·u₁. Negative for a positive zonal wind — the flux removes momentum from the flow (surface drag)."
    },
    "wv₀": {
      "type": "unknown",
      "units": "m^2/s^2",
      "description": "Surface kinematic momentum flux, v-component (Eq. 2.2): wv₀ = −Cₘ·V₁·v₁. Negative for a positive meridional wind; zero when v₁ = 0."
    },
    "wθ₀": {
      "type": "unknown",
      "units": "K*m/s",
      "description": "Surface kinematic heat flux (Eq. 2.3): wθ₀ = Cₕ·V₁·(θ₀ − θ₁). Positive (upward) when the surface is warmer than the air, negative (downward) when it is cooler."
    },
    "wq₀": {
      "type": "unknown",
      "units": "m/s",
      "description": "Surface kinematic moisture flux (Eq. 2.4): wq₀ = Dw·Cₕ·V₁·(q₀ − q₁). Positive (evaporation) when the surface is moister than the air."
    }
  },
  "equations": [
    {
      "_comment": "Horizontal wind speed at the lowest model level, with the V_min_sq floor (prevents a zero flux/Richardson-number denominator in calm conditions).",
      "lhs": "V₁",
      "rhs": {
        "op": "sqrt",
        "args": [
          {
            "op": "+",
            "args": [
              {
                "op": "^",
                "args": [
                  "u₁",
                  2.0
                ]
              },
              {
                "op": "^",
                "args": [
                  "v₁",
                  2.0
                ]
              },
              "V_min_sq"
            ]
          }
        ]
      }
    },
    {
      "_comment": "Eq. 2.7 — neutral exchange coefficient Cₙ = κ²/[ln((z₁+z₀ₘ)/z₀ₘ)]². The roughness ratio (z₁+z₀ₘ)/z₀ₘ is an upstream Julia local, inlined here.",
      "lhs": "Cₙ",
      "rhs": {
        "op": "/",
        "args": [
          {
            "op": "^",
            "args": [
              "κ",
              2.0
            ]
          },
          {
            "op": "^",
            "args": [
              {
                "op": "log",
                "args": [
                  {
                    "op": "/",
                    "args": [
                      {
                        "op": "+",
                        "args": [
                          "z₁",
                          "z₀ₘ"
                        ]
                      },
                      "z₀ₘ"
                    ]
                  }
                ]
              },
              2.0
            ]
          }
        ]
      }
    },
    {
      "_comment": "Eq. 2.8 — surface bulk Richardson number Ri₀ = g·z₁·(θᵥ₁−θᵥ₀)/(θ₁·V₁²). Negative when the air is unstable (θᵥ₁ < θᵥ₀).",
      "lhs": "Ri₀",
      "rhs": {
        "op": "/",
        "args": [
          {
            "op": "*",
            "args": [
              "g",
              "z₁",
              {
                "op": "-",
                "args": [
                  "θᵥ₁",
                  "θᵥ₀"
                ]
              }
            ]
          },
          {
            "op": "*",
            "args": [
              "θ₁",
              {
                "op": "^",
                "args": [
                  "V₁",
                  2.0
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "_comment": "Eq. 2.9 (unstable, Ri₀ < 0) / Eq. 2.11 (stable) — momentum stability function fₘ. The roughness ratio (z₁+z₀ₘ)/z₀ₘ is inlined inside the unstable-branch sqrt.",
      "lhs": "fₘ",
      "rhs": {
        "op": "ifelse",
        "args": [
          {
            "op": "<",
            "args": [
              "Ri₀",
              "zero_Ri"
            ]
          },
          {
            "op": "-",
            "args": [
              1.0,
              {
                "op": "/",
                "args": [
                  {
                    "op": "*",
                    "args": [
                      10.0,
                      "Ri₀"
                    ]
                  },
                  {
                    "op": "+",
                    "args": [
                      1.0,
                      {
                        "op": "*",
                        "args": [
                          75.0,
                          "Cₙ",
                          {
                            "op": "sqrt",
                            "args": [
                              {
                                "op": "*",
                                "args": [
                                  {
                                    "op": "/",
                                    "args": [
                                      {
                                        "op": "+",
                                        "args": [
                                          "z₁",
                                          "z₀ₘ"
                                        ]
                                      },
                                      "z₀ₘ"
                                    ]
                                  },
                                  {
                                    "op": "abs",
                                    "args": [
                                      "Ri₀"
                                    ]
                                  }
                                ]
                              }
                            ]
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "op": "/",
            "args": [
              1.0,
              {
                "op": "+",
                "args": [
                  1.0,
                  {
                    "op": "*",
                    "args": [
                      10.0,
                      "Ri₀",
                      {
                        "op": "+",
                        "args": [
                          1.0,
                          {
                            "op": "*",
                            "args": [
                              8.0,
                              "Ri₀"
                            ]
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "_comment": "Eq. 2.10 (unstable, Ri₀ < 0) / Eq. 2.11 (stable) — heat stability function fₕ. Identical to fₘ except the unstable-branch numerator coefficient is 15 instead of 10.",
      "lhs": "fₕ",
      "rhs": {
        "op": "ifelse",
        "args": [
          {
            "op": "<",
            "args": [
              "Ri₀",
              "zero_Ri"
            ]
          },
          {
            "op": "-",
            "args": [
              1.0,
              {
                "op": "/",
                "args": [
                  {
                    "op": "*",
                    "args": [
                      15.0,
                      "Ri₀"
                    ]
                  },
                  {
                    "op": "+",
                    "args": [
                      1.0,
                      {
                        "op": "*",
                        "args": [
                          75.0,
                          "Cₙ",
                          {
                            "op": "sqrt",
                            "args": [
                              {
                                "op": "*",
                                "args": [
                                  {
                                    "op": "/",
                                    "args": [
                                      {
                                        "op": "+",
                                        "args": [
                                          "z₁",
                                          "z₀ₘ"
                                        ]
                                      },
                                      "z₀ₘ"
                                    ]
                                  },
                                  {
                                    "op": "abs",
                                    "args": [
                                      "Ri₀"
                                    ]
                                  }
                                ]
                              }
                            ]
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "op": "/",
            "args": [
              1.0,
              {
                "op": "+",
                "args": [
                  1.0,
                  {
                    "op": "*",
                    "args": [
                      10.0,
                      "Ri₀",
                      {
                        "op": "+",
                        "args": [
                          1.0,
                          {
                            "op": "*",
                            "args": [
                              8.0,
                              "Ri₀"
                            ]
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "_comment": "Eq. 2.5 — momentum exchange coefficient.",
      "lhs": "Cₘ",
      "rhs": {
        "op": "*",
        "args": [
          "Cₙ",
          "fₘ"
        ]
      }
    },
    {
      "_comment": "Eq. 2.6 — heat exchange coefficient.",
      "lhs": "Cₕ",
      "rhs": {
        "op": "*",
        "args": [
          "Cₙ",
          "fₕ"
        ]
      }
    },
    {
      "_comment": "Eq. 2.1 — surface momentum flux, u-component (kinematic). Opposes the wind.",
      "lhs": "wu₀",
      "rhs": {
        "op": "*",
        "args": [
          -1.0,
          "Cₘ",
          "V₁",
          "u₁"
        ]
      }
    },
    {
      "_comment": "Eq. 2.2 — surface momentum flux, v-component (kinematic). Opposes the wind.",
      "lhs": "wv₀",
      "rhs": {
        "op": "*",
        "args": [
          -1.0,
          "Cₘ",
          "V₁",
          "v₁"
        ]
      }
    },
    {
      "_comment": "Eq. 2.3 — surface heat flux (kinematic). Up when the surface is warmer than the air.",
      "lhs": "wθ₀",
      "rhs": {
        "op": "*",
        "args": [
          "Cₕ",
          "V₁",
          {
            "op": "-",
            "args": [
              "θ₀",
              "θ₁"
            ]
          }
        ]
      }
    },
    {
      "_comment": "Eq. 2.4 — surface moisture flux (kinematic), scaled by the water-availability factor Dw.",
      "lhs": "wq₀",
      "rhs": {
        "op": "*",
        "args": [
          "Dw",
          "Cₕ",
          "V₁",
          {
            "op": "-",
            "args": [
              "q₀",
              "q₁"
            ]
          }
        ]
      }
    }
  ],
  "tests": [
    {
      "id": "neutral_conditions",
      "description": "Reproduces the upstream `SurfaceFlux neutral conditions` test (test/holtslag_boville_1993_test.jl:105-143). Equal surface and lowest-level virtual potential temperatures (θᵥ₀ = θᵥ₁ = 300 K) give a surface bulk Richardson number of exactly Ri₀ = 0; the stability functions take their neutral value fₘ = fₕ = 1, so the exchange coefficients reduce to the neutral coefficient Cₘ = Cₕ = Cₙ. The pinned Cₙ = 0.0075119… equals κ²/[ln((10+0.1)/0.1)]² = 0.16/ln(101)², the value the upstream test verifies by hand. Pins all 11 variables.",
      "parameter_overrides": {
        "θᵥ₀": 300.0,
        "θᵥ₁": 300.0,
        "θ₀": 300.0,
        "θ₁": 300.0,
        "u₁": 5.0,
        "v₁": 0.0,
        "z₁": 10.0,
        "z₀ₘ": 0.1
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "V₁",
          "time": 0.0,
          "expected": 5.0990195135927845
        },
        {
          "variable": "Ri₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "Cₙ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "fₘ",
          "time": 0.0,
          "expected": 1.0
        },
        {
          "variable": "fₕ",
          "time": 0.0,
          "expected": 1.0
        },
        {
          "variable": "Cₘ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "Cₕ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "wu₀",
          "time": 0.0,
          "expected": -0.1915184278809028
        },
        {
          "variable": "wv₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "wθ₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "wq₀",
          "time": 0.0,
          "expected": 7.660737115236112e-05
        }
      ]
    },
    {
      "id": "stable_conditions",
      "description": "Reproduces the upstream `SurfaceFlux stable conditions` test (test/holtslag_boville_1993_test.jl:145-180). Warm air over a cooler surface (θᵥ₁ = 295 K > θᵥ₀ = 290 K) gives a positive surface bulk Richardson number Ri₀ > 0. In stable stratification both stability functions take the same Holtslag & Beljaars (1989) form (Eq. 2.11), so fₘ = fₕ, both below 1 (mixing suppressed), and the surface heat flux wθ₀ < 0 (downward, into the cooler surface). Pins all 11 variables.",
      "parameter_overrides": {
        "θᵥ₀": 290.0,
        "θᵥ₁": 295.0,
        "θ₀": 290.0,
        "θ₁": 295.0,
        "u₁": 5.0,
        "v₁": 0.0,
        "z₁": 10.0,
        "z₀ₘ": 0.1
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "V₁",
          "time": 0.0,
          "expected": 5.0990195135927845
        },
        {
          "variable": "Ri₀",
          "time": 0.0,
          "expected": 0.06395045632333769
        },
        {
          "variable": "Cₙ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "fₘ",
          "time": 0.0,
          "expected": 0.5084717928577003
        },
        {
          "variable": "fₕ",
          "time": 0.0,
          "expected": 0.5084717928577003
        },
        {
          "variable": "Cₘ",
          "time": 0.0,
          "expected": 0.003819625248748082
        },
        {
          "variable": "Cₕ",
          "time": 0.0,
          "expected": 0.003819625248748082
        },
        {
          "variable": "wu₀",
          "time": 0.0,
          "expected": -0.09738171838989082
        },
        {
          "variable": "wv₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "wθ₀",
          "time": 0.0,
          "expected": -0.09738171838989082
        },
        {
          "variable": "wq₀",
          "time": 0.0,
          "expected": 3.895268735595633e-05
        }
      ]
    },
    {
      "id": "unstable_conditions",
      "description": "Reproduces the upstream `SurfaceFlux unstable conditions` and `SurfaceFlux unstable stability function numerical Eq. 2.9-2.10` tests (test/holtslag_boville_1993_test.jl:182-215, 1021-1055). Cool air over a warmer surface (θᵥ₁ = 300 K < θᵥ₀ = 305 K) gives a negative surface bulk Richardson number Ri₀ < 0. The unstable-branch Louis et al. (1982) stability functions exceed 1 (enhanced mixing) and, because the heat numerator coefficient (15) is larger than the momentum one (10), fₕ > fₘ. The surface heat flux wθ₀ > 0 (upward, off the warm surface). Pins all 11 variables.",
      "parameter_overrides": {
        "θᵥ₀": 305.0,
        "θᵥ₁": 300.0,
        "θ₀": 305.0,
        "θ₁": 300.0,
        "u₁": 5.0,
        "v₁": 0.0,
        "z₁": 10.0,
        "z₀ₘ": 0.1
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "V₁",
          "time": 0.0,
          "expected": 5.0990195135927845
        },
        {
          "variable": "Ri₀",
          "time": 0.0,
          "expected": -0.0628846153846154
        },
        {
          "variable": "Cₙ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "fₘ",
          "time": 0.0,
          "expected": 1.259867931537484
        },
        {
          "variable": "fₕ",
          "time": 0.0,
          "expected": 1.389801897306226
        },
        {
          "variable": "Cₘ",
          "time": 0.0,
          "expected": 0.00946409108427246
        },
        {
          "variable": "Cₕ",
          "time": 0.0,
          "expected": 0.010440151238034321
        },
        {
          "variable": "wu₀",
          "time": 0.0,
          "expected": -0.24128792558562384
        },
        {
          "variable": "wv₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "wθ₀",
          "time": 0.0,
          "expected": 0.26617267443798437
        },
        {
          "variable": "wq₀",
          "time": 0.0,
          "expected": 0.00010646906977519375
        }
      ]
    },
    {
      "id": "momentum_flux_direction",
      "description": "Reproduces the upstream `SurfaceFlux momentum flux direction` test (test/holtslag_boville_1993_test.jl:217-242). With both wind components positive (u₁ = 5 m/s, v₁ = 3 m/s) and neutral stratification, both momentum-flux components are negative — wu₀ < 0 and wv₀ < 0 — confirming that the surface stress (Eqs. 2.1-2.2) opposes the wind. Pins all 11 variables.",
      "parameter_overrides": {
        "θᵥ₀": 300.0,
        "θᵥ₁": 300.0,
        "θ₀": 300.0,
        "θ₁": 300.0,
        "u₁": 5.0,
        "v₁": 3.0,
        "z₁": 10.0,
        "z₀ₘ": 0.1
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "V₁",
          "time": 0.0,
          "expected": 5.916079783099616
        },
        {
          "variable": "Ri₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "Cₙ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "fₘ",
          "time": 0.0,
          "expected": 1.0
        },
        {
          "variable": "fₕ",
          "time": 0.0,
          "expected": 1.0
        },
        {
          "variable": "Cₘ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "Cₕ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "wu₀",
          "time": 0.0,
          "expected": -0.2222070922177916
        },
        {
          "variable": "wv₀",
          "time": 0.0,
          "expected": -0.133324255330675
        },
        {
          "variable": "wθ₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "wq₀",
          "time": 0.0,
          "expected": 8.888283688711665e-05
        }
      ]
    },
    {
      "id": "richardson_number",
      "description": "Reproduces the upstream `SurfaceFlux Ri₀ numerical verification Eq. 2.8` test (test/holtslag_boville_1993_test.jl:244-270). A distinct configuration (u₁ = 3, v₁ = 4 m/s so V₁ = sqrt(26); z₁ = 20 m, z₀ₘ = 0.01 m; θᵥ₁ = 300 K > θᵥ₀ = 295 K, stable) verifies the surface bulk Richardson number Ri₀ = g·z₁·(θᵥ₁−θᵥ₀)/(θ₁·V₁²) = 9.81·20·5/(300·26) = 0.12576…. Pins all 11 variables.",
      "parameter_overrides": {
        "θᵥ₀": 295.0,
        "θᵥ₁": 300.0,
        "θ₀": 295.0,
        "θ₁": 300.0,
        "u₁": 3.0,
        "v₁": 4.0,
        "z₁": 20.0,
        "z₀ₘ": 0.01
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "V₁",
          "time": 0.0,
          "expected": 5.0990195135927845
        },
        {
          "variable": "Ri₀",
          "time": 0.0,
          "expected": 0.1257692307692308
        },
        {
          "variable": "Cₙ",
          "time": 0.0,
          "expected": 0.0027690611265885298
        },
        {
          "variable": "fₘ",
          "time": 0.0,
          "expected": 0.2838389810012629
        },
        {
          "variable": "fₕ",
          "time": 0.0,
          "expected": 0.2838389810012629
        },
        {
          "variable": "Cₘ",
          "time": 0.0,
          "expected": 0.0007859674885010974
        },
        {
          "variable": "Cₕ",
          "time": 0.0,
          "expected": 0.0007859674885010974
        },
        {
          "variable": "wu₀",
          "time": 0.0,
          "expected": -0.012022990682749824
        },
        {
          "variable": "wv₀",
          "time": 0.0,
          "expected": -0.016030654243666433
        },
        {
          "variable": "wθ₀",
          "time": 0.0,
          "expected": -0.02003831780458304
        },
        {
          "variable": "wq₀",
          "time": 0.0,
          "expected": 8.015327121833216e-06
        }
      ]
    },
    {
      "id": "moisture_flux",
      "description": "Reproduces the upstream `SurfaceFlux moisture flux Eq. 2.4` test (test/holtslag_boville_1993_test.jl:272-304). Neutral stratification with a moister surface than air (q₀ = 0.015 > q₁ = 0.01) and a water-availability factor Dw = 0.8 gives a positive surface moisture flux wq₀ = Dw·Cₕ·V₁·(q₀−q₁) > 0 (evaporation). Pins all 11 variables.",
      "parameter_overrides": {
        "θᵥ₀": 300.0,
        "θᵥ₁": 300.0,
        "θ₀": 300.0,
        "θ₁": 300.0,
        "u₁": 5.0,
        "v₁": 0.0,
        "z₁": 10.0,
        "z₀ₘ": 0.1,
        "q₀": 0.015,
        "q₁": 0.01,
        "Dw": 0.8
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "V₁",
          "time": 0.0,
          "expected": 5.0990195135927845
        },
        {
          "variable": "Ri₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "Cₙ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "fₘ",
          "time": 0.0,
          "expected": 1.0
        },
        {
          "variable": "fₕ",
          "time": 0.0,
          "expected": 1.0
        },
        {
          "variable": "Cₘ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "Cₕ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "wu₀",
          "time": 0.0,
          "expected": -0.1915184278809028
        },
        {
          "variable": "wv₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "wθ₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "wq₀",
          "time": 0.0,
          "expected": 0.00015321474230472223
        }
      ]
    },
    {
      "id": "numerical_flux_values",
      "description": "Reproduces the upstream `SurfaceFlux numerical flux values Eq. 2.1-2.3` test (test/holtslag_boville_1993_test.jl:306-337). Neutral virtual-temperature stratification (θᵥ₀ = θᵥ₁ = 300 K) but with a warmer surface than air (θ₀ = 305 K > θ₁ = 300 K) verifies the momentum flux wu₀ = −Cₘ·V₁·u₁ and the upward heat flux wθ₀ = Cₕ·V₁·(θ₀−θ₁). Pins all 11 variables.",
      "parameter_overrides": {
        "θᵥ₀": 300.0,
        "θᵥ₁": 300.0,
        "θ₀": 305.0,
        "θ₁": 300.0,
        "u₁": 5.0,
        "v₁": 0.0,
        "z₁": 10.0,
        "z₀ₘ": 0.1
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "V₁",
          "time": 0.0,
          "expected": 5.0990195135927845
        },
        {
          "variable": "Ri₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "Cₙ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "fₘ",
          "time": 0.0,
          "expected": 1.0
        },
        {
          "variable": "fₕ",
          "time": 0.0,
          "expected": 1.0
        },
        {
          "variable": "Cₘ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "Cₕ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "wu₀",
          "time": 0.0,
          "expected": -0.1915184278809028
        },
        {
          "variable": "wv₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "wθ₀",
          "time": 0.0,
          "expected": 0.1915184278809028
        },
        {
          "variable": "wq₀",
          "time": 0.0,
          "expected": 7.660737115236112e-05
        }
      ]
    },
    {
      "id": "stable_stability_function",
      "description": "Reproduces the upstream `SurfaceFlux stable stability function numerical Eq. 2.11` test (test/holtslag_boville_1993_test.jl:1057-1082). A second stable configuration (θᵥ₁ = 300 K > θᵥ₀ = 295 K) verifies the Holtslag & Beljaars (1989) stable-branch stability function fₘ = fₕ = 1/(1 + 10·Ri₀·(1 + 8·Ri₀)) — equal for momentum and heat, and below 1. Pins all 11 variables.",
      "parameter_overrides": {
        "θᵥ₀": 295.0,
        "θᵥ₁": 300.0,
        "θ₀": 295.0,
        "θ₁": 300.0,
        "u₁": 5.0,
        "v₁": 0.0,
        "z₁": 10.0,
        "z₀ₘ": 0.1
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "V₁",
          "time": 0.0,
          "expected": 5.0990195135927845
        },
        {
          "variable": "Ri₀",
          "time": 0.0,
          "expected": 0.0628846153846154
        },
        {
          "variable": "Cₙ",
          "time": 0.0,
          "expected": 0.007511970776748738
        },
        {
          "variable": "fₘ",
          "time": 0.0,
          "expected": 0.5140848605050503
        },
        {
          "variable": "fₕ",
          "time": 0.0,
          "expected": 0.5140848605050503
        },
        {
          "variable": "Cₘ",
          "time": 0.0,
          "expected": 0.0038617904488828896
        },
        {
          "variable": "Cₕ",
          "time": 0.0,
          "expected": 0.0038617904488828896
        },
        {
          "variable": "wu₀",
          "time": 0.0,
          "expected": -0.09845672428130046
        },
        {
          "variable": "wv₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "wθ₀",
          "time": 0.0,
          "expected": -0.09845672428130046
        },
        {
          "variable": "wq₀",
          "time": 0.0,
          "expected": 3.9382689712520186e-05
        }
      ]
    },
    {
      "id": "default_parameters",
      "description": "All parameters at their declared defaults (u₁ = 1 m/s, v₁ = 0, z₁ = 10 m, z₀ₘ = 1e-4 m, θ₀ = 300 K, θ₁ = 299 K, θᵥ₀ = 300 K, θᵥ₁ = 299 K, q₀ = 0.01, q₁ = 0.008, Dw = 1). With θᵥ₁ = 299 K < θᵥ₀ = 300 K the default state is mildly unstable (Ri₀ < 0, fₘ and fₕ above 1). Verifies the declared defaults are wired correctly and the unstable branch is reached without any override. Pins all 11 variables.",
      "parameter_overrides": {},
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "assertions": [
        {
          "variable": "V₁",
          "time": 0.0,
          "expected": 1.4142135623730951
        },
        {
          "variable": "Ri₀",
          "time": 0.0,
          "expected": -0.1640468227424749
        },
        {
          "variable": "Cₙ",
          "time": 0.0,
          "expected": 0.0012071127639142537
        },
        {
          "variable": "fₘ",
          "time": 0.0,
          "expected": 1.1302408302016616
        },
        {
          "variable": "fₕ",
          "time": 0.0,
          "expected": 1.1953612453024924
        },
        {
          "variable": "Cₘ",
          "time": 0.0,
          "expected": 0.0013643281324334684
        },
        {
          "variable": "Cₕ",
          "time": 0.0,
          "expected": 0.0014429358166930758
        },
        {
          "variable": "wu₀",
          "time": 0.0,
          "expected": -0.0019294513484145674
        },
        {
          "variable": "wv₀",
          "time": 0.0,
          "expected": 0.0
        },
        {
          "variable": "wθ₀",
          "time": 0.0,
          "expected": 0.002040619401601246
        },
        {
          "variable": "wq₀",
          "time": 0.0,
          "expected": 4.081238803202493e-06
        }
      ]
    }
  ],
  "analyses": [
    {
      "id": "stability_functions",
      "description": "Reproduces the documented 'Surface Layer Stability Functions (Eq. 2.9-2.11)' figure from the AtmosphericDynamics.jl Holtslag & Boville (1993) page. The lowest-level virtual potential temperature θᵥ₁ is swept from 290 to 310 K with the surface value held at θᵥ₀ = 300 K, carrying the surface bulk Richardson number Ri₀ from unstable (negative) through neutral (0) to stable (positive). Plotting the momentum and heat stability functions against Ri₀ shows the asymmetric response: in unstable air (Ri₀ < 0) both functions exceed 1 and fₕ rises faster than fₘ (numerator coefficient 15 vs 10); in stable air (Ri₀ > 0) the two coincide and decay below 1. θ₁ is held at 300 K (the stability functions depend on the inputs only through Ri₀, so the curves are independent of how Ri₀ is generated).",
      "initial_state": {},
      "parameters": {
        "θᵥ₀": 300.0,
        "θ₀": 300.0,
        "θ₁": 300.0,
        "u₁": 5.0,
        "v₁": 0.0,
        "z₁": 10.0,
        "z₀ₘ": 0.1
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "θᵥ₁",
            "range": {
              "start": 290.0,
              "stop": 310.0,
              "count": 41,
              "scale": "linear"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "fm_vs_Ri",
          "type": "line",
          "description": "Momentum stability function fₘ as a function of the surface bulk Richardson number Ri₀ — enhanced (> 1) in unstable air, suppressed (< 1) in stable air.",
          "x": {
            "variable": "Ri₀",
            "label": "Surface bulk Richardson number Ri₀"
          },
          "y": {
            "variable": "fₘ",
            "label": "Momentum stability function fₘ"
          }
        },
        {
          "id": "fh_vs_Ri",
          "type": "line",
          "description": "Heat stability function fₕ as a function of the surface bulk Richardson number Ri₀. In unstable air fₕ rises above fₘ; in stable air fₕ = fₘ.",
          "x": {
            "variable": "Ri₀",
            "label": "Surface bulk Richardson number Ri₀"
          },
          "y": {
            "variable": "fₕ",
            "label": "Heat stability function fₕ"
          }
        }
      ]
    },
    {
      "id": "surface_fluxes_vs_wind",
      "description": "Surface momentum and heat fluxes as a function of the lowest-level wind speed. The zonal wind u₁ is swept from 0 to 20 m/s under neutral virtual-temperature stratification (θᵥ₀ = θᵥ₁ = 300 K) with a 2 K surface-air potential-temperature contrast (θ₀ = 302 K, θ₁ = 300 K). The momentum flux wu₀ = −Cₘ·V₁·u₁ grows in magnitude roughly with the square of the wind speed, and the heat flux wθ₀ = Cₕ·V₁·(θ₀−θ₁) grows linearly with V₁ — the bulk-aerodynamic ventilation effect, stronger wind drawing more flux from the surface.",
      "initial_state": {},
      "parameters": {
        "θᵥ₀": 300.0,
        "θᵥ₁": 300.0,
        "θ₀": 302.0,
        "θ₁": 300.0,
        "v₁": 0.0,
        "z₁": 10.0,
        "z₀ₘ": 0.1
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "u₁",
            "range": {
              "start": 0.0,
              "stop": 20.0,
              "count": 41,
              "scale": "linear"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "wu0_vs_wind",
          "type": "line",
          "description": "Surface momentum flux wu₀ versus the lowest-level zonal wind u₁ — increasingly negative (stronger drag) as the wind strengthens.",
          "x": {
            "variable": "u₁",
            "label": "Lowest-level zonal wind u₁ (m/s)"
          },
          "y": {
            "variable": "wu₀",
            "label": "Surface momentum flux wu₀ (m^2/s^2)"
          }
        },
        {
          "id": "wth0_vs_wind",
          "type": "line",
          "description": "Surface heat flux wθ₀ versus the lowest-level zonal wind u₁ — rising with wind speed through the bulk-aerodynamic ventilation factor V₁.",
          "x": {
            "variable": "u₁",
            "label": "Lowest-level zonal wind u₁ (m/s)"
          },
          "y": {
            "variable": "wθ₀",
            "label": "Surface heat flux wθ₀ (K*m/s)"
          }
        }
      ]
    },
    {
      "id": "neutral_coefficient_vs_roughness",
      "description": "The neutral exchange coefficient Cₙ = κ²/[ln((z₁+z₀ₘ)/z₀ₘ)]² as a function of the momentum roughness length z₀ₘ. The roughness length is swept logarithmically from 1e-5 m (very smooth, e.g. open water) to 1e-1 m (rough, e.g. tall vegetation), with the lowest model level held at z₁ = 10 m. As the surface roughens the logarithmic roughness ratio shrinks and Cₙ rises — the surface couples more strongly to the atmosphere (Eq. 2.7).",
      "initial_state": {},
      "parameters": {
        "z₁": 10.0
      },
      "time_span": {
        "start": 0.0,
        "end": 1.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "z₀ₘ",
            "range": {
              "start": 1e-05,
              "stop": 0.1,
              "count": 41,
              "scale": "log"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "Cn_vs_z0m",
          "type": "line",
          "description": "Neutral exchange coefficient Cₙ as a function of the momentum roughness length z₀ₘ — monotonically increasing with surface roughness.",
          "x": {
            "variable": "z₀ₘ",
            "label": "Momentum roughness length z₀ₘ (m)"
          },
          "y": {
            "variable": "Cₙ",
            "label": "Neutral exchange coefficient Cₙ"
          }
        }
      ]
    }
  ]
}