Version: dev

BoundaryLayerMixingKC

Kantha-Clayson & Garratt boundary-layer turbulence parameterization for Lagrangian particle dispersion, as described in NOAA ARL-224 and the HYSPLIT documentation. Computes turbulent velocity standard deviations (sigmaw, sigma_u, sigma_v), Lagrangian vertical time scale (TL_v), Monin-Obukhov length (Lmo), and convective velocity scale (wstar) from surface-layer similarity theory; integrates stochastic Langevin equations for the turbulent velocity components (wprime, uprime_x, uprime_y) and the resulting Lagrangian particle position (lon, lat, lev). The upstream Julia source uses a single @brownians Bw term on all three velocity ODEs (gt-kuxo gap); this ESM migrates the deterministic drift equations only. Migrated from EnvironmentalTransport.jl@a48559c4114eb9f06668ecf9f06235aad20f63f0:src/BoundaryLayerMixingKC.jl:15.

.esm schema
1.0.0
Source
components/environmental_transport/boundary_layer_mixing_kc.esm

Description

Kantha-Clayson & Garratt boundary-layer turbulence parameterization for Lagrangian particle dispersion, as described in NOAA ARL-224 and the HYSPLIT documentation. Computes turbulent velocity standard deviations (sigmaw, sigma_u, sigma_v), Lagrangian vertical time scale (TL_v), Monin-Obukhov length (Lmo), and convective velocity scale (wstar) from surface-layer similarity theory; integrates stochastic Langevin equations for the turbulent velocity components (wprime, uprime_x, uprime_y) and the resulting Lagrangian particle position (lon, lat, lev). The upstream Julia source uses a single @brownians Bw term on all three velocity ODEs (gt-kuxo gap); this ESM migrates the deterministic drift equations only. Migrated from EnvironmentalTransport.jl@a48559c4114eb9f06668ecf9f06235aad20f63f0:src/BoundaryLayerMixingKC.jl:15.

Reference

Draxler and Rolph (2003). NOAA ARL-224.

_source_ref: EnvironmentalTransport.jl@a48559c4114eb9f06668ecf9f06235aad20f63f0:src/BoundaryLayerMixingKC.jl:15

Upstream BoundaryLayerMixingKC is an MTK @component implementing the Kantha-Clayson & Garratt turbulence closure for HYSPLIT Lagrangian transport. It declares:

  • 9 @constants (kappa, one, m0, m1, ms0, kms0, kms1e_8, S, offset) used as MTK unit sentinels
  • 26 @parameters (PBLH, USTAR, HFLUX, EFLUX, PS, T2M, QV2M, z2lev, TL_h, TL_v_min, TL_v_max, Rd, cp, Lv, g, R_inv, KMIX0, TKEMIN, x_trans, y_trans, z_agl, lev_gnd, lev_top, w_damp)
  • 20 @variables (wprime, sigmaw, sigma_u, sigma_v, TL_v, zi, wprime_ref, Tv2m, rho, wth, wq, wthv, wthv_eff, Lmo, wstar, uprime_x, uprime_y, lon, lat, lev)
  • 1 @brownians Bw (single Wiener process for all three velocity components; upstream TODO notes intent to replace with separate Bx, By)

Migration approach: The 9 MTK @constants are substituted as literal values in expressions (kappa = 0.4 retained as named parameter for legibility; one=1.0, m0=0.0, m1=1.0, ms0=0.0, kms0=0.0, kms1e_8=1e-8, S=1.0, offset=0.05 inlined directly). All 14 non-ODE @variables (zi, Tv2m, rho, wth, wq, wthv, wthv_eff, Lmo, wstar, sigmaw, sigma_u, sigma_v, TL_v, wprime_ref) are migrated as observed type, with their expressions computed from parameters and ODE states. Three additional observed intermediates (zeta_clip, sigma_w2, sigma_u2) are added for readability; they correspond to Julia local variables zeta_clip, sigma_w2_expr, sigma_u2_expr. sigma_v2 is the same expression as sigma_u2 in both stable and unstable regimes (both use 0.36*wstar^2 unstable, and differ only by coefficient in stable: 4.0 vs 5.0 for USTAR^2 term). Unicode variable names mapped to ASCII: sigma_u (from Julia sigma_u), sigma_v (from Julia sigma_v).

Key physics:

  • Stability regime determined by Lmo: unstable (Lmo < 0), stable/neutral (Lmo >= 0)
  • Within unstable regime: surface layer (zeta_clip < 0.1, uses Hanna 1982 USL / ARL-224 Eq. 92) vs mixed layer (Kantha-Clayson / ARL-224 Eq. 90)
  • Lagrangian time scale TL_v clamped to [TL_v_min, TL_v_max] to prevent singular behavior
  • wprime_ref implements boundary reflection: partial absorption at ground (lev <= lev_gnd + 0.05) and lid (lev >= lev_top - 0.05)

TODO_GAP: gt-kuxo — Upstream Julia component uses @brownians Bw (single Wiener process) to add diffusion noise to the three velocity Langevin equations: D(wprime) ~ -(wprime/TL_v) + sqrt(2sigmaw^2/TL_v/1s) * Bw D(uprime_x) ~ -(uprime_x/TL_h) + sqrt(2sigma_u2/TL_h/1s) * Bw D(uprime_y) ~ -(uprime_y/TL_h) + sqrt(2*sigma_v2/TL_h/1s) * Bw The ESM schema v0.4.0 has no brownian variable type (gap gt-kuxo). This ESM migrates the deterministic drift only: D(wprime)/dt = -wprime/TL_v D(uprime_x)/dt = -uprime_x/TL_h D(uprime_y)/dt = -uprime_y/TL_h The upstream source also contains a TODO noting that Bw should be replaced by separate Bx, By for uprime_x and uprime_y. When gt-kuxo is resolved, add three brownian variables (Bw, Bx, By) and restore the full SDE diffusion terms.

Coupling: sigma_u and sigma_v are consumed by GaussianKC (as sigma_u_x, sigma_u_y) for Gaussian puff dispersion. When coupled, these are promoted to shared algebraic variables via connector equations.

Tolerance rationale (rel=1e-6, abs=1e-10): algebraic outputs are smooth functions of bounded parameters; ODE drift terms are linear in state variables with constant (parameter-derived) coefficients. Relative tolerance 1e-6 gives margin well above roundoff while remaining tight enough to catch sign errors._

Variables

NameUnitsDefaultDescription
wprimem/s0.0Turbulent vertical velocity fluctuation w’ (m/s). ODE state governed by Langevin equation D(wprime)/dt = -wprime/TL_v (deterministic drift; Brownian noise term omitted — see TODO_GAP gt-kuxo).
uprime_xm/s0.0Turbulent horizontal velocity fluctuation in x (eastward, m/s). ODE state: D(uprime_x)/dt = -uprime_x/TL_h (deterministic drift; Brownian noise omitted).
uprime_ym/s0.0Turbulent horizontal velocity fluctuation in y (northward, m/s). ODE state: D(uprime_y)/dt = -uprime_y/TL_h (deterministic drift; Brownian noise omitted).
lon10.0Lagrangian particle longitude coordinate (radians, dimensionless). ODE state: D(lon)/dt = uprime_x * x_trans.
lat10.0Lagrangian particle latitude coordinate (radians, dimensionless). ODE state: D(lat)/dt = uprime_y * y_trans.
lev11.0Lagrangian particle level coordinate (dimensionless level index). ODE state: D(lev)/dt = wprime_ref * z2lev. Boundary conditions applied via wprime_ref reflection.

Parameters

NameUnitsDefaultDescription
kappa10.4von Karman constant (dimensionless). Appears in the Monin-Obukhov length formula (ARL-224 Eq. 27). Upstream Julia @constants kappa = 0.4.
PBLHm1000.0Planetary boundary layer height (m). Input meteorological field; sets the upper bound of the boundary layer used in stability calculations.
USTARm/s0.3Surface friction velocity u* (m/s). Input meteorological field; drives stable/neutral turbulence intensity (ARL-224 Eqs. 71-76).
HFLUXW/m^20.0Surface sensible heat flux H (W/m^2). Positive upward (daytime unstable); negative downward (nighttime stable). Drives convective turbulence intensity.
EFLUXW/m^20.0Surface latent heat flux LE (W/m^2). Positive upward. Contributes to the virtual temperature flux (moisture buoyancy correction).
PSPa101325.0Surface air pressure (Pa). Used with T2M and Rd to compute surface air density rho.
T2MK293.152-metre air temperature (K). Used to compute virtual temperature Tv2m, air density rho, and wstar.
QV2M10.0092-metre specific humidity (dimensionless, kg/kg). Moisture correction in virtual temperature: Tv2m = T2M * (1 + 0.61 * QV2M).
z2lev1/m0.0Vertical coordinate gradient d(lev)/dz (1/m). Converts vertical turbulent velocity wprime_ref to level-coordinate velocity D(lev)/dt.
TL_hs10800.0Lagrangian time scale for horizontal velocity fluctuations (s). Default 3 h. Governs decorrelation of uprime_x, uprime_y: D(u)/dt = -u/TL_h + noise.
TL_v_mins5.0Minimum Lagrangian time scale for vertical turbulence (s). Clamps TL_v from below to prevent singular Langevin dynamics near the surface.
TL_v_maxs1000.0Maximum Lagrangian time scale for vertical turbulence (s). Clamps TL_v from above to prevent unrealistically slow decorrelation.
RdJ/(kg*K)287.05Specific gas constant for dry air (J/(kg K)). Used to compute surface air density rho = PS / (Rd * Tv2m).
cpJ/(kg*K)1004.0Specific heat capacity of dry air at constant pressure near 300 K (J/(kg K)). Converts surface sensible heat flux to kinematic form: wth = HFLUX / (rho * cp).
LvJ/kg2500000.0Latent heat of vaporization of water (J/kg). Converts surface latent heat flux to kinematic form: wq = EFLUX / (rho * Lv).
gm/s^29.80665Gravitational acceleration (m/s^2). Appears in Monin-Obukhov length (ARL-224 Eq. 27) and convective velocity scale wstar (ARL-224 Eq. 28).
R_inv10.2Heat-flux ratio at the inversion (dimensionless). Appears in the unstable mixed-layer sigma_w formula (ARL-224 Eq. 90): sigma_w2_UML = wstar^2 * zeta^(2/3) * (1-zeta)^(2/3) * (1 + 0.5*R_inv^(2/3)).
KMIX0m150.0Minimum mixing depth (m). The effective PBL height zi = max(PBLH, KMIX0) ensures a minimum well-mixed layer.
TKEMINm^2/s^20.001Minimum turbulent kinetic energy (m^2/s^2). Sets a floor on the vertical velocity variance: sigma_w2_min = (2/3) * TKEMIN (isotropic TKE partition).
x_trans1/m0.01Longitude scaling: d(lon)/d(x) in radians per meter (1/m). Converts zonal turbulent velocity uprime_x to longitude rate: D(lon)/dt = uprime_x * x_trans.
y_trans1/m0.01Latitude scaling: d(lat)/d(y) in radians per meter (1/m). Converts meridional turbulent velocity uprime_y to latitude rate: D(lat)/dt = uprime_y * y_trans.
z_aglm50.0Particle height above ground level (m). Used to compute the normalized height zeta_clip = z_agl / zi, which determines the stability sub-regime.
lev_gnd11.0Level index of the ground surface (dimensionless). Boundary reflection condition triggers when lev <= lev_gnd + 0.05.
lev_top172.0Top level index of the domain (dimensionless). Boundary reflection condition triggers when lev >= lev_top - 0.05.
w_damp11.0Fraction of vertical velocity absorbed on boundary impact (dimensionless). 0.0 = perfect elastic bounce; 1.0 = complete absorption. wprime_ref = (1 - w_damp) * |wprime| at boundaries.

Observed

NameUnitsDefaultDescription
zimEffective planetary boundary layer depth (m). zi = max(PBLH, KMIX0). Provides a minimum mixing depth floor.
Tv2mK2-metre virtual temperature (K). Tv2m = T2M * (1 + 0.61 * QV2M). Moisture-corrected temperature for density and buoyancy calculations.
rhokg/m^3Surface air density (kg/m^3). rho = PS / (Rd * Tv2m). Used to convert heat and moisture fluxes from energy to kinematic units.
wthK*m/sKinematic sensible heat flux w’theta’ (K m/s). wth = HFLUX / (rho * cp). ARL-224 Eq. 27.
wqm/sKinematic moisture flux w’q’ (m/s). wq = EFLUX / (rho * Lv). Units m/s since q is dimensionless kg/kg.
wthvK*m/sKinematic virtual potential temperature flux w’theta_v’ (K m/s). wthv = wth*(1+0.61QV2M) + 0.61T2M*wq. Combines sensible and moisture buoyancy contributions.
wthv_numK*m/sSign proxy for wthv: wthv_num = HFLUX*(1+0.61QV2M)/cp + 0.61T2MEFLUX/Lv = rhowthv. Since rho>0, sign(wthv_num)==sign(wthv), so wthv_num>0 iff Lmo<0 (unstable). Uses only multiplication/addition of parameters — no division by rho — so evaluates to 0 (not NaN) when SymPy substitutes 0 for all free symbols during Piecewise-condition simplification.
wthv_effK*m/sEffective virtual temperature flux (K m/s), bounded away from zero to prevent singular Lmo. wthv_eff = ifelse(wthv>=0, max(wthv,1e-8), min(wthv,-1e-8)). Stability branch uses wthv_num>=0 (sign proxy without rho division) to avoid NaN when SymPy substitutes 0 for all free symbols.
LmomMonin-Obukhov length L (m). Lmo = -USTAR^3 * Tv2m / (kappa * g * wthv_eff). Negative in unstable conditions (upward heat flux), positive in stable conditions. ARL-224 Eq. 27.
wstarm/sDeardorff convective velocity scale w* (m/s). wstar = (g/Tv2m * wthv * zi)^(1/3) when wthv > 0, else 0. ARL-224 Eq. 28. Stability condition uses wthv_num>0 (sign proxy, no rho division).
zeta_clip1Normalized height within the PBL: zeta_clip = clamp(z_agl / max(zi, 1.0), 0, 1). Dimensionless stability coordinate (0 at surface, 1 at PBL top). Corresponds to Julia local variable zeta_clip.
sigma_w2m^2/s^2Vertical turbulent velocity variance sigma_w^2 (m^2/s^2). Unstable surface layer (Lmo<0, zeta_clip<0.1): 1.74USTAR^2(1-3z_agl/L_eff)^(2/3) (Hanna 1982, ARL-224 Eq.92). Unstable mixed layer: wstar^2zeta^(2/3)(1-zeta)^(2/3)(1+0.5R_inv^(2/3)) (ARL-224 Eq.90). Stable/neutral: 1.7USTAR^2*(1-zeta)^1.5 (ARL-224 Eq.71). Corresponds to Julia sigma_w2_expr.
sigmawm/sStandard deviation of turbulent vertical velocity sigma_w (m/s). sigmaw = sqrt(max(sigma_w2, (2/3)*TKEMIN)). Enters the Lagrangian time scale TL_v computation.
sigma_u2m^2/s^2Horizontal x-direction turbulent velocity variance (m^2/s^2). Unstable: 0.36wstar^2 (Kantha-Clayson, ARL-224 Eq. 91). Stable/neutral: 4.0USTAR^2*(1-zeta)^1.5 (ARL-224 Eq. 72-73). Corresponds to Julia sigma_u2_expr.
sigma_um/sStandard deviation of turbulent horizontal x-velocity sigma_u (m/s). sigma_u = sqrt(sigma_u2). Consumed by GaussianKC as sigma_u_x when coupled.
sigma_v2m^2/s^2Horizontal y-direction turbulent velocity variance (m^2/s^2). Unstable: 0.36wstar^2 (ARL-224 Eq. 93, same as sigma_u2). Stable/neutral: 5.0USTAR^2*(1-zeta)^1.5 (ARL-224 Eq. 75-76, coefficient 5.0 vs 4.0 for x). Corresponds to Julia sigma_v2_expr.
sigma_vm/sStandard deviation of turbulent horizontal y-velocity sigma_v (m/s). sigma_v = sqrt(sigma_v2). Consumed by GaussianKC as sigma_u_y when coupled.
TL_vsLagrangian time scale for vertical velocity (s), clamped to [TL_v_min, TL_v_max]. Unstable: 0.15zi/sigmaw(1-exp(-5zeta_clip)); Stable: 0.1zi/sigmaw*zeta_clip^0.8. Hanna (1982), ARL-224 Eqs. 63, 66.
wprime_refm/sReflected turbulent vertical velocity near boundaries (m/s). At ground (lev <= lev_gnd+0.05): wprime_ref = (1-w_damp)|wprime|. At lid (lev >= lev_top-0.05): wprime_ref = -(1-w_damp)|wprime|. Elsewhere: wprime_ref = wprime.

Observed expressions

$$ zi = \mathrm{max}\left(PBLH, KMIX0\right) $$

Effective planetary boundary layer depth (m). zi = max(PBLH, KMIX0). Provides a minimum mixing depth floor.

$$ Tv2m = T2M \cdot \left(1.0 + 0.61 \cdot QV2M\right) $$

2-metre virtual temperature (K). Tv2m = T2M * (1 + 0.61 * QV2M). Moisture-corrected temperature for density and buoyancy calculations.

$$ rho = \frac{PS}{Rd \cdot Tv2m} $$

Surface air density (kg/m^3). rho = PS / (Rd * Tv2m). Used to convert heat and moisture fluxes from energy to kinematic units.

$$ wth = \frac{HFLUX}{rho \cdot cp} $$

Kinematic sensible heat flux w’theta’ (K m/s). wth = HFLUX / (rho * cp). ARL-224 Eq. 27.

$$ wq = \frac{EFLUX}{rho \cdot Lv} $$

Kinematic moisture flux w’q’ (m/s). wq = EFLUX / (rho * Lv). Units m/s since q is dimensionless kg/kg.

$$ wthv = wth \cdot \left(1.0 + 0.61 \cdot QV2M\right) + 0.61 \cdot T2M \cdot wq $$

Kinematic virtual potential temperature flux w’theta_v’ (K m/s). wthv = wth*(1+0.61QV2M) + 0.61T2M*wq. Combines sensible and moisture buoyancy contributions.

$$ wthv_{num} = HFLUX \cdot \left(1.0 + 0.61 \cdot QV2M\right) \cdot \frac{1.0}{cp} + 0.61 \cdot T2M \cdot EFLUX \cdot \frac{1.0}{Lv} $$

Sign proxy for wthv: wthv_num = HFLUX*(1+0.61QV2M)/cp + 0.61T2MEFLUX/Lv = rhowthv. Since rho>0, sign(wthv_num)==sign(wthv), so wthv_num>0 iff Lmo<0 (unstable). Uses only multiplication/addition of parameters — no division by rho — so evaluates to 0 (not NaN) when SymPy substitutes 0 for all free symbols during Piecewise-condition simplification.

$$ wthv_{eff} = \mathrm{ifelse}\left(\mathrm{>=}\left(wthv_{num}, 0.0\right), \mathrm{max}\left(wthv, 1 \times 10^{-8}\right), \mathrm{min}\left(wthv, -1 \times 10^{-8}\right)\right) $$

Effective virtual temperature flux (K m/s), bounded away from zero to prevent singular Lmo. wthv_eff = ifelse(wthv>=0, max(wthv,1e-8), min(wthv,-1e-8)). Stability branch uses wthv_num>=0 (sign proxy without rho division) to avoid NaN when SymPy substitutes 0 for all free symbols.

$$ Lmo = \left(-1.0\right) \cdot \frac{USTAR^{3} \cdot Tv2m}{kappa \cdot g \cdot wthv_{eff}} $$

Monin-Obukhov length L (m). Lmo = -USTAR^3 * Tv2m / (kappa * g * wthv_eff). Negative in unstable conditions (upward heat flux), positive in stable conditions. ARL-224 Eq. 27.

$$ wstar = \mathrm{ifelse}\left(\mathrm{>}\left(wthv_{num}, 0.0\right), \left(\frac{g}{Tv2m} \cdot wthv \cdot zi\right)^{0.3333333333333333}, 0.0\right) $$

Deardorff convective velocity scale w* (m/s). wstar = (g/Tv2m * wthv * zi)^(1/3) when wthv > 0, else 0. ARL-224 Eq. 28. Stability condition uses wthv_num>0 (sign proxy, no rho division).

$$ zeta_{clip} = \mathrm{max}\left(\mathrm{min}\left(\frac{z_{agl}}{\mathrm{max}\left(zi, 1.0\right)}, 1.0\right), 0.0\right) $$

Normalized height within the PBL: zeta_clip = clamp(z_agl / max(zi, 1.0), 0, 1). Dimensionless stability coordinate (0 at surface, 1 at PBL top). Corresponds to Julia local variable zeta_clip.

$$ sigma_{w2} = \mathrm{ifelse}\left(\mathrm{>}\left(wthv_{num}, 0.0\right), \mathrm{ifelse}\left(\mathrm{<}\left(zeta_{clip}, 0.1\right), 1.74 \cdot USTAR^{2} \cdot \mathrm{max}\left(1.0 + \left(-3.0\right) \cdot \frac{z_{agl}}{\mathrm{ifelse}\left(\mathrm{>}\left(wthv_{num}, 0.0\right), -1.0, 1.0\right) \cdot \mathrm{max}\left(\left|Lmo\right|, 1.0\right)}, 1 \times 10^{-6}\right)^{0.6666666666666666}, wstar^{2} \cdot \mathrm{max}\left(zeta_{clip}, 1 \times 10^{-6}\right)^{0.6666666666666666} \cdot \mathrm{max}\left(1.0 + \left(-1.0\right) \cdot zeta_{clip}, 1 \times 10^{-6}\right)^{0.6666666666666666} \cdot \left(1.0 + 0.5 \cdot R_{inv}^{0.6666666666666666}\right)\right), 1.7 \cdot USTAR^{2} \cdot \mathrm{max}\left(1.0 + \left(-1.0\right) \cdot zeta_{clip}, 0.0\right)^{1.5}\right) $$

Vertical turbulent velocity variance sigma_w^2 (m^2/s^2). Unstable surface layer (Lmo<0, zeta_clip<0.1): 1.74USTAR^2(1-3z_agl/L_eff)^(2/3) (Hanna 1982, ARL-224 Eq.92). Unstable mixed layer: wstar^2zeta^(2/3)(1-zeta)^(2/3)(1+0.5R_inv^(2/3)) (ARL-224 Eq.90). Stable/neutral: 1.7USTAR^2*(1-zeta)^1.5 (ARL-224 Eq.71). Corresponds to Julia sigma_w2_expr.

$$ sigmaw = \sqrt{\mathrm{max}\left(sigma_{w2}, 0.6666666666666666 \cdot TKEMIN\right)} $$

Standard deviation of turbulent vertical velocity sigma_w (m/s). sigmaw = sqrt(max(sigma_w2, (2/3)*TKEMIN)). Enters the Lagrangian time scale TL_v computation.

$$ sigma_{u2} = \mathrm{ifelse}\left(\mathrm{>}\left(wthv_{num}, 0.0\right), 0.36 \cdot wstar^{2}, 4.0 \cdot USTAR^{2} \cdot \mathrm{max}\left(1.0 + \left(-1.0\right) \cdot zeta_{clip}, 0.0\right)^{1.5}\right) $$

Horizontal x-direction turbulent velocity variance (m^2/s^2). Unstable: 0.36wstar^2 (Kantha-Clayson, ARL-224 Eq. 91). Stable/neutral: 4.0USTAR^2*(1-zeta)^1.5 (ARL-224 Eq. 72-73). Corresponds to Julia sigma_u2_expr.

$$ sigma_{u} = \sqrt{sigma_{u2}} $$

Standard deviation of turbulent horizontal x-velocity sigma_u (m/s). sigma_u = sqrt(sigma_u2). Consumed by GaussianKC as sigma_u_x when coupled.

$$ sigma_{v2} = \mathrm{ifelse}\left(\mathrm{>}\left(wthv_{num}, 0.0\right), 0.36 \cdot wstar^{2}, 5.0 \cdot USTAR^{2} \cdot \mathrm{max}\left(1.0 + \left(-1.0\right) \cdot zeta_{clip}, 0.0\right)^{1.5}\right) $$

Horizontal y-direction turbulent velocity variance (m^2/s^2). Unstable: 0.36wstar^2 (ARL-224 Eq. 93, same as sigma_u2). Stable/neutral: 5.0USTAR^2*(1-zeta)^1.5 (ARL-224 Eq. 75-76, coefficient 5.0 vs 4.0 for x). Corresponds to Julia sigma_v2_expr.

$$ sigma_{v} = \sqrt{sigma_{v2}} $$

Standard deviation of turbulent horizontal y-velocity sigma_v (m/s). sigma_v = sqrt(sigma_v2). Consumed by GaussianKC as sigma_u_y when coupled.

$$ TL_{v} = \mathrm{max}\left(TL_{v\,min}, \mathrm{min}\left(TL_{v\,max}, \mathrm{ifelse}\left(\mathrm{>}\left(wthv_{num}, 0.0\right), 0.15 \cdot \frac{zi}{sigmaw} \cdot \left(1.0 + \left(-1.0\right) \cdot e^{\left(-5.0\right) \cdot zeta_{clip}}\right), 0.1 \cdot \frac{zi}{sigmaw} \cdot zeta_{clip}^{0.8}\right)\right)\right) $$

Lagrangian time scale for vertical velocity (s), clamped to [TL_v_min, TL_v_max]. Unstable: 0.15zi/sigmaw(1-exp(-5zeta_clip)); Stable: 0.1zi/sigmaw*zeta_clip^0.8. Hanna (1982), ARL-224 Eqs. 63, 66.

$$ wprime_{ref} = \mathrm{ifelse}\left(\mathrm{<=}\left(lev, lev_{gnd} + 0.05\right), \left(1.0 + \left(-1.0\right) \cdot w_{damp}\right) \cdot \left|wprime\right|, \mathrm{ifelse}\left(\mathrm{>=}\left(lev, lev_{top} + -0.05\right), \left(-1.0\right) \cdot \left(1.0 + \left(-1.0\right) \cdot w_{damp}\right) \cdot \left|wprime\right|, wprime\right)\right) $$

Reflected turbulent vertical velocity near boundaries (m/s). At ground (lev <= lev_gnd+0.05): wprime_ref = (1-w_damp)|wprime|. At lid (lev >= lev_top-0.05): wprime_ref = -(1-w_damp)|wprime|. Elsewhere: wprime_ref = wprime.

Equations

$$ \mathrm{D}\left(wprime\right) = \left(-1.0\right) \cdot \frac{wprime}{TL_{v}} $$ $$ \mathrm{D}\left(uprime_{x}\right) = \left(-1.0\right) \cdot \frac{uprime_{x}}{TL_{h}} $$ $$ \mathrm{D}\left(uprime_{y}\right) = \left(-1.0\right) \cdot \frac{uprime_{y}}{TL_{h}} $$ $$ \mathrm{D}\left(lon\right) = uprime_{x} \cdot x_{trans} $$ $$ \mathrm{D}\left(lat\right) = uprime_{y} \cdot y_{trans} $$ $$ \mathrm{D}\left(lev\right) = wprime_{ref} \cdot z2lev $$

Analyses

stability_regime_sweep

Boundary-layer turbulence parameters (sigmaw, sigma_u, wstar, TL_v) as a function of surface sensible heat flux HFLUX over -100 to 300 W/m^2. Illustrates the transition from stable (HFLUX < 0, Lmo > 0) to neutral (HFLUX ~ 0) to convective (HFLUX > 0, Lmo < 0) regimes. USTAR=0.3 m/s, PBLH=1000 m, z_agl=50 m.

Time span: 0.0 → 0.0

Swept over: HFLUX

Vertical velocity std dev sigmaw vs surface heat flux.

Horizontal velocity std dev sigma_u vs surface heat flux.

Convective velocity scale wstar vs surface heat flux. Zero for stable/neutral (HFLUX <= 0), grows as (HFLUX)^(1/3) for unstable.

Vertical Lagrangian time scale TL_v vs surface heat flux.

particle_velocity_decay

Time evolution of turbulent velocity components (wprime, uprime_x) and resulting particle level displacement under the deterministic Langevin drift. Initial conditions: wprime=1 m/s, uprime_x=0.5 m/s, lev=36 (interior domain). HFLUX=100 W/m^2 (unstable), z2lev=0.001. Shows exponential decay of velocity fluctuations: TL_v ~ 43 s for vertical, TL_h = 10800 s for horizontal.

Time span: 0.0 → 200.0

Turbulent vertical velocity wprime decaying from 1 m/s with time scale TL_v ~ 43 s.

Zonal turbulent velocity uprime_x decaying from 0.5 m/s with time scale TL_h = 10800 s (very slow over this 200 s window).

Particle level coordinate lev(t) drifting upward from level 36 as wprime drives D(lev)/dt = wprime_ref * z2lev.

Raw .esm

Raw .esm JSON (this component)
{
  "reference": {
    "citation": "Draxler and Rolph (2003). NOAA ARL-224.",
    "notes": "source_ref: EnvironmentalTransport.jl@a48559c4114eb9f06668ecf9f06235aad20f63f0:src/BoundaryLayerMixingKC.jl:15\n\nUpstream BoundaryLayerMixingKC is an MTK @component implementing the Kantha-Clayson & Garratt turbulence closure for HYSPLIT Lagrangian transport. It declares:\n  - 9 @constants (kappa, one, m0, m1, ms0, kms0, kms1e_8, S, offset) used as MTK unit sentinels\n  - 26 @parameters (PBLH, USTAR, HFLUX, EFLUX, PS, T2M, QV2M, z2lev, TL_h, TL_v_min, TL_v_max, Rd, cp, Lv, g, R_inv, KMIX0, TKEMIN, x_trans, y_trans, z_agl, lev_gnd, lev_top, w_damp)\n  - 20 @variables (wprime, sigmaw, sigma_u, sigma_v, TL_v, zi, wprime_ref, Tv2m, rho, wth, wq, wthv, wthv_eff, Lmo, wstar, uprime_x, uprime_y, lon, lat, lev)\n  - 1 @brownians Bw (single Wiener process for all three velocity components; upstream TODO notes intent to replace with separate Bx, By)\n\nMigration approach:\n  The 9 MTK @constants are substituted as literal values in expressions (kappa = 0.4 retained as named parameter for legibility; one=1.0, m0=0.0, m1=1.0, ms0=0.0, kms0=0.0, kms1e_8=1e-8, S=1.0, offset=0.05 inlined directly). All 14 non-ODE @variables (zi, Tv2m, rho, wth, wq, wthv, wthv_eff, Lmo, wstar, sigmaw, sigma_u, sigma_v, TL_v, wprime_ref) are migrated as `observed` type, with their expressions computed from parameters and ODE states. Three additional `observed` intermediates (zeta_clip, sigma_w2, sigma_u2) are added for readability; they correspond to Julia local variables zeta_clip, sigma_w2_expr, sigma_u2_expr. sigma_v2 is the same expression as sigma_u2 in both stable and unstable regimes (both use 0.36*wstar^2 unstable, and differ only by coefficient in stable: 4.0 vs 5.0 for USTAR^2 term). Unicode variable names mapped to ASCII: sigma_u (from Julia sigma_u), sigma_v (from Julia sigma_v).\n\nKey physics:\n  - Stability regime determined by Lmo: unstable (Lmo < 0), stable/neutral (Lmo >= 0)\n  - Within unstable regime: surface layer (zeta_clip < 0.1, uses Hanna 1982 USL / ARL-224 Eq. 92) vs mixed layer (Kantha-Clayson / ARL-224 Eq. 90)\n  - Lagrangian time scale TL_v clamped to [TL_v_min, TL_v_max] to prevent singular behavior\n  - wprime_ref implements boundary reflection: partial absorption at ground (lev <= lev_gnd + 0.05) and lid (lev >= lev_top - 0.05)\n\nTODO_GAP: gt-kuxo — Upstream Julia component uses `@brownians Bw` (single Wiener process) to add diffusion noise to the three velocity Langevin equations:\n  D(wprime)   ~ -(wprime/TL_v)   + sqrt(2*sigmaw^2/TL_v/1s) * Bw\n  D(uprime_x) ~ -(uprime_x/TL_h) + sqrt(2*sigma_u2/TL_h/1s) * Bw\n  D(uprime_y) ~ -(uprime_y/TL_h) + sqrt(2*sigma_v2/TL_h/1s) * Bw\nThe ESM schema v0.4.0 has no `brownian` variable type (gap gt-kuxo). This ESM migrates the deterministic drift only:\n  D(wprime)/dt   = -wprime/TL_v\n  D(uprime_x)/dt = -uprime_x/TL_h\n  D(uprime_y)/dt = -uprime_y/TL_h\nThe upstream source also contains a TODO noting that Bw should be replaced by separate Bx, By for uprime_x and uprime_y. When gt-kuxo is resolved, add three `brownian` variables (Bw, Bx, By) and restore the full SDE diffusion terms.\n\nCoupling: sigma_u and sigma_v are consumed by GaussianKC (as sigma_u_x, sigma_u_y) for Gaussian puff dispersion. When coupled, these are promoted to shared algebraic variables via connector equations.\n\nTolerance rationale (rel=1e-6, abs=1e-10): algebraic outputs are smooth functions of bounded parameters; ODE drift terms are linear in state variables with constant (parameter-derived) coefficients. Relative tolerance 1e-6 gives margin well above roundoff while remaining tight enough to catch sign errors."
  },
  "tolerance": {
    "rel": 1e-06,
    "abs": 1e-10
  },
  "variables": {
    "kappa": {
      "type": "parameter",
      "units": "1",
      "default": 0.4,
      "description": "von Karman constant (dimensionless). Appears in the Monin-Obukhov length formula (ARL-224 Eq. 27). Upstream Julia @constants kappa = 0.4."
    },
    "PBLH": {
      "type": "parameter",
      "units": "m",
      "default": 1000.0,
      "description": "Planetary boundary layer height (m). Input meteorological field; sets the upper bound of the boundary layer used in stability calculations."
    },
    "USTAR": {
      "type": "parameter",
      "units": "m/s",
      "default": 0.3,
      "description": "Surface friction velocity u* (m/s). Input meteorological field; drives stable/neutral turbulence intensity (ARL-224 Eqs. 71-76)."
    },
    "HFLUX": {
      "type": "parameter",
      "units": "W/m^2",
      "default": 0.0,
      "description": "Surface sensible heat flux H (W/m^2). Positive upward (daytime unstable); negative downward (nighttime stable). Drives convective turbulence intensity."
    },
    "EFLUX": {
      "type": "parameter",
      "units": "W/m^2",
      "default": 0.0,
      "description": "Surface latent heat flux LE (W/m^2). Positive upward. Contributes to the virtual temperature flux (moisture buoyancy correction)."
    },
    "PS": {
      "type": "parameter",
      "units": "Pa",
      "default": 101325.0,
      "description": "Surface air pressure (Pa). Used with T2M and Rd to compute surface air density rho."
    },
    "T2M": {
      "type": "parameter",
      "units": "K",
      "default": 293.15,
      "description": "2-metre air temperature (K). Used to compute virtual temperature Tv2m, air density rho, and wstar."
    },
    "QV2M": {
      "type": "parameter",
      "units": "1",
      "default": 0.009,
      "description": "2-metre specific humidity (dimensionless, kg/kg). Moisture correction in virtual temperature: Tv2m = T2M * (1 + 0.61 * QV2M)."
    },
    "z2lev": {
      "type": "parameter",
      "units": "1/m",
      "default": 0.0,
      "description": "Vertical coordinate gradient d(lev)/dz (1/m). Converts vertical turbulent velocity wprime_ref to level-coordinate velocity D(lev)/dt."
    },
    "TL_h": {
      "type": "parameter",
      "units": "s",
      "default": 10800.0,
      "description": "Lagrangian time scale for horizontal velocity fluctuations (s). Default 3 h. Governs decorrelation of uprime_x, uprime_y: D(u)/dt = -u/TL_h + noise."
    },
    "TL_v_min": {
      "type": "parameter",
      "units": "s",
      "default": 5.0,
      "description": "Minimum Lagrangian time scale for vertical turbulence (s). Clamps TL_v from below to prevent singular Langevin dynamics near the surface."
    },
    "TL_v_max": {
      "type": "parameter",
      "units": "s",
      "default": 1000.0,
      "description": "Maximum Lagrangian time scale for vertical turbulence (s). Clamps TL_v from above to prevent unrealistically slow decorrelation."
    },
    "Rd": {
      "type": "parameter",
      "units": "J/(kg*K)",
      "default": 287.05,
      "description": "Specific gas constant for dry air (J/(kg K)). Used to compute surface air density rho = PS / (Rd * Tv2m)."
    },
    "cp": {
      "type": "parameter",
      "units": "J/(kg*K)",
      "default": 1004.0,
      "description": "Specific heat capacity of dry air at constant pressure near 300 K (J/(kg K)). Converts surface sensible heat flux to kinematic form: wth = HFLUX / (rho * cp)."
    },
    "Lv": {
      "type": "parameter",
      "units": "J/kg",
      "default": 2500000.0,
      "description": "Latent heat of vaporization of water (J/kg). Converts surface latent heat flux to kinematic form: wq = EFLUX / (rho * Lv)."
    },
    "g": {
      "type": "parameter",
      "units": "m/s^2",
      "default": 9.80665,
      "description": "Gravitational acceleration (m/s^2). Appears in Monin-Obukhov length (ARL-224 Eq. 27) and convective velocity scale wstar (ARL-224 Eq. 28)."
    },
    "R_inv": {
      "type": "parameter",
      "units": "1",
      "default": 0.2,
      "description": "Heat-flux ratio at the inversion (dimensionless). Appears in the unstable mixed-layer sigma_w formula (ARL-224 Eq. 90): sigma_w2_UML = wstar^2 * zeta^(2/3) * (1-zeta)^(2/3) * (1 + 0.5*R_inv^(2/3))."
    },
    "KMIX0": {
      "type": "parameter",
      "units": "m",
      "default": 150.0,
      "description": "Minimum mixing depth (m). The effective PBL height zi = max(PBLH, KMIX0) ensures a minimum well-mixed layer."
    },
    "TKEMIN": {
      "type": "parameter",
      "units": "m^2/s^2",
      "default": 0.001,
      "description": "Minimum turbulent kinetic energy (m^2/s^2). Sets a floor on the vertical velocity variance: sigma_w2_min = (2/3) * TKEMIN (isotropic TKE partition)."
    },
    "x_trans": {
      "type": "parameter",
      "units": "1/m",
      "default": 0.01,
      "description": "Longitude scaling: d(lon)/d(x) in radians per meter (1/m). Converts zonal turbulent velocity uprime_x to longitude rate: D(lon)/dt = uprime_x * x_trans."
    },
    "y_trans": {
      "type": "parameter",
      "units": "1/m",
      "default": 0.01,
      "description": "Latitude scaling: d(lat)/d(y) in radians per meter (1/m). Converts meridional turbulent velocity uprime_y to latitude rate: D(lat)/dt = uprime_y * y_trans."
    },
    "z_agl": {
      "type": "parameter",
      "units": "m",
      "default": 50.0,
      "description": "Particle height above ground level (m). Used to compute the normalized height zeta_clip = z_agl / zi, which determines the stability sub-regime."
    },
    "lev_gnd": {
      "type": "parameter",
      "units": "1",
      "default": 1.0,
      "description": "Level index of the ground surface (dimensionless). Boundary reflection condition triggers when lev <= lev_gnd + 0.05."
    },
    "lev_top": {
      "type": "parameter",
      "units": "1",
      "default": 72.0,
      "description": "Top level index of the domain (dimensionless). Boundary reflection condition triggers when lev >= lev_top - 0.05."
    },
    "w_damp": {
      "type": "parameter",
      "units": "1",
      "default": 1.0,
      "description": "Fraction of vertical velocity absorbed on boundary impact (dimensionless). 0.0 = perfect elastic bounce; 1.0 = complete absorption. wprime_ref = (1 - w_damp) * |wprime| at boundaries."
    },
    "wprime": {
      "type": "unknown",
      "units": "m/s",
      "default": 0.0,
      "description": "Turbulent vertical velocity fluctuation w' (m/s). ODE state governed by Langevin equation D(wprime)/dt = -wprime/TL_v (deterministic drift; Brownian noise term omitted — see TODO_GAP gt-kuxo)."
    },
    "uprime_x": {
      "type": "unknown",
      "units": "m/s",
      "default": 0.0,
      "description": "Turbulent horizontal velocity fluctuation in x (eastward, m/s). ODE state: D(uprime_x)/dt = -uprime_x/TL_h (deterministic drift; Brownian noise omitted)."
    },
    "uprime_y": {
      "type": "unknown",
      "units": "m/s",
      "default": 0.0,
      "description": "Turbulent horizontal velocity fluctuation in y (northward, m/s). ODE state: D(uprime_y)/dt = -uprime_y/TL_h (deterministic drift; Brownian noise omitted)."
    },
    "lon": {
      "type": "unknown",
      "units": "1",
      "default": 0.0,
      "description": "Lagrangian particle longitude coordinate (radians, dimensionless). ODE state: D(lon)/dt = uprime_x * x_trans."
    },
    "lat": {
      "type": "unknown",
      "units": "1",
      "default": 0.0,
      "description": "Lagrangian particle latitude coordinate (radians, dimensionless). ODE state: D(lat)/dt = uprime_y * y_trans."
    },
    "lev": {
      "type": "unknown",
      "units": "1",
      "default": 1.0,
      "description": "Lagrangian particle level coordinate (dimensionless level index). ODE state: D(lev)/dt = wprime_ref * z2lev. Boundary conditions applied via wprime_ref reflection."
    },
    "zi": {
      "type": "unknown",
      "units": "m",
      "description": "Effective planetary boundary layer depth (m). zi = max(PBLH, KMIX0). Provides a minimum mixing depth floor."
    },
    "Tv2m": {
      "type": "unknown",
      "units": "K",
      "description": "2-metre virtual temperature (K). Tv2m = T2M * (1 + 0.61 * QV2M). Moisture-corrected temperature for density and buoyancy calculations."
    },
    "rho": {
      "type": "unknown",
      "units": "kg/m^3",
      "description": "Surface air density (kg/m^3). rho = PS / (Rd * Tv2m). Used to convert heat and moisture fluxes from energy to kinematic units."
    },
    "wth": {
      "type": "unknown",
      "units": "K*m/s",
      "description": "Kinematic sensible heat flux w'theta' (K m/s). wth = HFLUX / (rho * cp). ARL-224 Eq. 27."
    },
    "wq": {
      "type": "unknown",
      "units": "m/s",
      "description": "Kinematic moisture flux w'q' (m/s). wq = EFLUX / (rho * Lv). Units m/s since q is dimensionless kg/kg."
    },
    "wthv": {
      "type": "unknown",
      "units": "K*m/s",
      "description": "Kinematic virtual potential temperature flux w'theta_v' (K m/s). wthv = wth*(1+0.61*QV2M) + 0.61*T2M*wq. Combines sensible and moisture buoyancy contributions."
    },
    "wthv_num": {
      "type": "unknown",
      "units": "K*m/s",
      "description": "Sign proxy for wthv: wthv_num = HFLUX*(1+0.61*QV2M)/cp + 0.61*T2M*EFLUX/Lv = rho*wthv. Since rho>0, sign(wthv_num)==sign(wthv), so wthv_num>0 iff Lmo<0 (unstable). Uses only multiplication/addition of parameters — no division by rho — so evaluates to 0 (not NaN) when SymPy substitutes 0 for all free symbols during Piecewise-condition simplification."
    },
    "wthv_eff": {
      "type": "unknown",
      "units": "K*m/s",
      "description": "Effective virtual temperature flux (K m/s), bounded away from zero to prevent singular Lmo. wthv_eff = ifelse(wthv>=0, max(wthv,1e-8), min(wthv,-1e-8)). Stability branch uses wthv_num>=0 (sign proxy without rho division) to avoid NaN when SymPy substitutes 0 for all free symbols."
    },
    "Lmo": {
      "type": "unknown",
      "units": "m",
      "description": "Monin-Obukhov length L (m). Lmo = -USTAR^3 * Tv2m / (kappa * g * wthv_eff). Negative in unstable conditions (upward heat flux), positive in stable conditions. ARL-224 Eq. 27."
    },
    "wstar": {
      "type": "unknown",
      "units": "m/s",
      "description": "Deardorff convective velocity scale w* (m/s). wstar = (g/Tv2m * wthv * zi)^(1/3) when wthv > 0, else 0. ARL-224 Eq. 28. Stability condition uses wthv_num>0 (sign proxy, no rho division)."
    },
    "zeta_clip": {
      "type": "unknown",
      "units": "1",
      "description": "Normalized height within the PBL: zeta_clip = clamp(z_agl / max(zi, 1.0), 0, 1). Dimensionless stability coordinate (0 at surface, 1 at PBL top). Corresponds to Julia local variable zeta_clip."
    },
    "sigma_w2": {
      "type": "unknown",
      "units": "m^2/s^2",
      "description": "Vertical turbulent velocity variance sigma_w^2 (m^2/s^2). Unstable surface layer (Lmo<0, zeta_clip<0.1): 1.74*USTAR^2*(1-3*z_agl/L_eff)^(2/3) (Hanna 1982, ARL-224 Eq.92). Unstable mixed layer: wstar^2*zeta^(2/3)*(1-zeta)^(2/3)*(1+0.5*R_inv^(2/3)) (ARL-224 Eq.90). Stable/neutral: 1.7*USTAR^2*(1-zeta)^1.5 (ARL-224 Eq.71). Corresponds to Julia sigma_w2_expr."
    },
    "sigmaw": {
      "type": "unknown",
      "units": "m/s",
      "description": "Standard deviation of turbulent vertical velocity sigma_w (m/s). sigmaw = sqrt(max(sigma_w2, (2/3)*TKEMIN)). Enters the Lagrangian time scale TL_v computation."
    },
    "sigma_u2": {
      "type": "unknown",
      "units": "m^2/s^2",
      "description": "Horizontal x-direction turbulent velocity variance (m^2/s^2). Unstable: 0.36*wstar^2 (Kantha-Clayson, ARL-224 Eq. 91). Stable/neutral: 4.0*USTAR^2*(1-zeta)^1.5 (ARL-224 Eq. 72-73). Corresponds to Julia sigma_u2_expr."
    },
    "sigma_u": {
      "type": "unknown",
      "units": "m/s",
      "description": "Standard deviation of turbulent horizontal x-velocity sigma_u (m/s). sigma_u = sqrt(sigma_u2). Consumed by GaussianKC as sigma_u_x when coupled."
    },
    "sigma_v2": {
      "type": "unknown",
      "units": "m^2/s^2",
      "description": "Horizontal y-direction turbulent velocity variance (m^2/s^2). Unstable: 0.36*wstar^2 (ARL-224 Eq. 93, same as sigma_u2). Stable/neutral: 5.0*USTAR^2*(1-zeta)^1.5 (ARL-224 Eq. 75-76, coefficient 5.0 vs 4.0 for x). Corresponds to Julia sigma_v2_expr."
    },
    "sigma_v": {
      "type": "unknown",
      "units": "m/s",
      "description": "Standard deviation of turbulent horizontal y-velocity sigma_v (m/s). sigma_v = sqrt(sigma_v2). Consumed by GaussianKC as sigma_u_y when coupled."
    },
    "TL_v": {
      "type": "unknown",
      "units": "s",
      "description": "Lagrangian time scale for vertical velocity (s), clamped to [TL_v_min, TL_v_max]. Unstable: 0.15*zi/sigmaw*(1-exp(-5*zeta_clip)); Stable: 0.1*zi/sigmaw*zeta_clip^0.8. Hanna (1982), ARL-224 Eqs. 63, 66."
    },
    "wprime_ref": {
      "type": "unknown",
      "units": "m/s",
      "description": "Reflected turbulent vertical velocity near boundaries (m/s). At ground (lev <= lev_gnd+0.05): wprime_ref = (1-w_damp)*|wprime|. At lid (lev >= lev_top-0.05): wprime_ref = -(1-w_damp)*|wprime|. Elsewhere: wprime_ref = wprime."
    }
  },
  "equations": [
    {
      "lhs": {
        "op": "D",
        "wrt": "t",
        "args": [
          "wprime"
        ]
      },
      "rhs": {
        "op": "*",
        "args": [
          -1.0,
          {
            "op": "/",
            "args": [
              "wprime",
              "TL_v"
            ]
          }
        ]
      }
    },
    {
      "lhs": {
        "op": "D",
        "wrt": "t",
        "args": [
          "uprime_x"
        ]
      },
      "rhs": {
        "op": "*",
        "args": [
          -1.0,
          {
            "op": "/",
            "args": [
              "uprime_x",
              "TL_h"
            ]
          }
        ]
      }
    },
    {
      "lhs": {
        "op": "D",
        "wrt": "t",
        "args": [
          "uprime_y"
        ]
      },
      "rhs": {
        "op": "*",
        "args": [
          -1.0,
          {
            "op": "/",
            "args": [
              "uprime_y",
              "TL_h"
            ]
          }
        ]
      }
    },
    {
      "lhs": {
        "op": "D",
        "wrt": "t",
        "args": [
          "lon"
        ]
      },
      "rhs": {
        "op": "*",
        "args": [
          "uprime_x",
          "x_trans"
        ]
      }
    },
    {
      "lhs": {
        "op": "D",
        "wrt": "t",
        "args": [
          "lat"
        ]
      },
      "rhs": {
        "op": "*",
        "args": [
          "uprime_y",
          "y_trans"
        ]
      }
    },
    {
      "lhs": {
        "op": "D",
        "wrt": "t",
        "args": [
          "lev"
        ]
      },
      "rhs": {
        "op": "*",
        "args": [
          "wprime_ref",
          "z2lev"
        ]
      }
    },
    {
      "lhs": "zi",
      "rhs": {
        "op": "max",
        "args": [
          "PBLH",
          "KMIX0"
        ]
      }
    },
    {
      "lhs": "Tv2m",
      "rhs": {
        "op": "*",
        "args": [
          "T2M",
          {
            "op": "+",
            "args": [
              1.0,
              {
                "op": "*",
                "args": [
                  0.61,
                  "QV2M"
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "lhs": "rho",
      "rhs": {
        "op": "/",
        "args": [
          "PS",
          {
            "op": "*",
            "args": [
              "Rd",
              "Tv2m"
            ]
          }
        ]
      }
    },
    {
      "lhs": "wth",
      "rhs": {
        "op": "/",
        "args": [
          "HFLUX",
          {
            "op": "*",
            "args": [
              "rho",
              "cp"
            ]
          }
        ]
      }
    },
    {
      "lhs": "wq",
      "rhs": {
        "op": "/",
        "args": [
          "EFLUX",
          {
            "op": "*",
            "args": [
              "rho",
              "Lv"
            ]
          }
        ]
      }
    },
    {
      "lhs": "wthv",
      "rhs": {
        "op": "+",
        "args": [
          {
            "op": "*",
            "args": [
              "wth",
              {
                "op": "+",
                "args": [
                  1.0,
                  {
                    "op": "*",
                    "args": [
                      0.61,
                      "QV2M"
                    ]
                  }
                ]
              }
            ]
          },
          {
            "op": "*",
            "args": [
              0.61,
              "T2M",
              "wq"
            ]
          }
        ]
      }
    },
    {
      "lhs": "wthv_num",
      "rhs": {
        "op": "+",
        "args": [
          {
            "op": "*",
            "args": [
              "HFLUX",
              {
                "op": "+",
                "args": [
                  1.0,
                  {
                    "op": "*",
                    "args": [
                      0.61,
                      "QV2M"
                    ]
                  }
                ]
              },
              {
                "op": "/",
                "args": [
                  1.0,
                  "cp"
                ]
              }
            ]
          },
          {
            "op": "*",
            "args": [
              0.61,
              "T2M",
              "EFLUX",
              {
                "op": "/",
                "args": [
                  1.0,
                  "Lv"
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "lhs": "wthv_eff",
      "rhs": {
        "op": "ifelse",
        "args": [
          {
            "op": ">=",
            "args": [
              "wthv_num",
              0.0
            ]
          },
          {
            "op": "max",
            "args": [
              "wthv",
              1e-08
            ]
          },
          {
            "op": "min",
            "args": [
              "wthv",
              -1e-08
            ]
          }
        ]
      }
    },
    {
      "lhs": "Lmo",
      "rhs": {
        "op": "*",
        "args": [
          -1.0,
          {
            "op": "/",
            "args": [
              {
                "op": "*",
                "args": [
                  {
                    "op": "^",
                    "args": [
                      "USTAR",
                      3
                    ]
                  },
                  "Tv2m"
                ]
              },
              {
                "op": "*",
                "args": [
                  "kappa",
                  "g",
                  "wthv_eff"
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "lhs": "wstar",
      "rhs": {
        "op": "ifelse",
        "args": [
          {
            "op": ">",
            "args": [
              "wthv_num",
              0.0
            ]
          },
          {
            "op": "^",
            "args": [
              {
                "op": "*",
                "args": [
                  {
                    "op": "/",
                    "args": [
                      "g",
                      "Tv2m"
                    ]
                  },
                  "wthv",
                  "zi"
                ]
              },
              0.3333333333333333
            ]
          },
          0.0
        ]
      }
    },
    {
      "lhs": "zeta_clip",
      "rhs": {
        "op": "max",
        "args": [
          {
            "op": "min",
            "args": [
              {
                "op": "/",
                "args": [
                  "z_agl",
                  {
                    "op": "max",
                    "args": [
                      "zi",
                      1.0
                    ]
                  }
                ]
              },
              1.0
            ]
          },
          0.0
        ]
      }
    },
    {
      "lhs": "sigma_w2",
      "rhs": {
        "op": "ifelse",
        "args": [
          {
            "op": ">",
            "args": [
              "wthv_num",
              0.0
            ]
          },
          {
            "op": "ifelse",
            "args": [
              {
                "op": "<",
                "args": [
                  "zeta_clip",
                  0.1
                ]
              },
              {
                "op": "*",
                "args": [
                  1.74,
                  {
                    "op": "^",
                    "args": [
                      "USTAR",
                      2
                    ]
                  },
                  {
                    "op": "^",
                    "args": [
                      {
                        "op": "max",
                        "args": [
                          {
                            "op": "+",
                            "args": [
                              1.0,
                              {
                                "op": "*",
                                "args": [
                                  -3.0,
                                  {
                                    "op": "/",
                                    "args": [
                                      "z_agl",
                                      {
                                        "op": "*",
                                        "args": [
                                          {
                                            "op": "ifelse",
                                            "args": [
                                              {
                                                "op": ">",
                                                "args": [
                                                  "wthv_num",
                                                  0.0
                                                ]
                                              },
                                              -1.0,
                                              1.0
                                            ]
                                          },
                                          {
                                            "op": "max",
                                            "args": [
                                              {
                                                "op": "abs",
                                                "args": [
                                                  "Lmo"
                                                ]
                                              },
                                              1.0
                                            ]
                                          }
                                        ]
                                      }
                                    ]
                                  }
                                ]
                              }
                            ]
                          },
                          1e-06
                        ]
                      },
                      0.6666666666666666
                    ]
                  }
                ]
              },
              {
                "op": "*",
                "args": [
                  {
                    "op": "^",
                    "args": [
                      "wstar",
                      2
                    ]
                  },
                  {
                    "op": "^",
                    "args": [
                      {
                        "op": "max",
                        "args": [
                          "zeta_clip",
                          1e-06
                        ]
                      },
                      0.6666666666666666
                    ]
                  },
                  {
                    "op": "^",
                    "args": [
                      {
                        "op": "max",
                        "args": [
                          {
                            "op": "+",
                            "args": [
                              1.0,
                              {
                                "op": "*",
                                "args": [
                                  -1.0,
                                  "zeta_clip"
                                ]
                              }
                            ]
                          },
                          1e-06
                        ]
                      },
                      0.6666666666666666
                    ]
                  },
                  {
                    "op": "+",
                    "args": [
                      1.0,
                      {
                        "op": "*",
                        "args": [
                          0.5,
                          {
                            "op": "^",
                            "args": [
                              "R_inv",
                              0.6666666666666666
                            ]
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "op": "*",
            "args": [
              1.7,
              {
                "op": "^",
                "args": [
                  "USTAR",
                  2
                ]
              },
              {
                "op": "^",
                "args": [
                  {
                    "op": "max",
                    "args": [
                      {
                        "op": "+",
                        "args": [
                          1.0,
                          {
                            "op": "*",
                            "args": [
                              -1.0,
                              "zeta_clip"
                            ]
                          }
                        ]
                      },
                      0.0
                    ]
                  },
                  1.5
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "lhs": "sigmaw",
      "rhs": {
        "op": "sqrt",
        "args": [
          {
            "op": "max",
            "args": [
              "sigma_w2",
              {
                "op": "*",
                "args": [
                  0.6666666666666666,
                  "TKEMIN"
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "lhs": "sigma_u2",
      "rhs": {
        "op": "ifelse",
        "args": [
          {
            "op": ">",
            "args": [
              "wthv_num",
              0.0
            ]
          },
          {
            "op": "*",
            "args": [
              0.36,
              {
                "op": "^",
                "args": [
                  "wstar",
                  2
                ]
              }
            ]
          },
          {
            "op": "*",
            "args": [
              4.0,
              {
                "op": "^",
                "args": [
                  "USTAR",
                  2
                ]
              },
              {
                "op": "^",
                "args": [
                  {
                    "op": "max",
                    "args": [
                      {
                        "op": "+",
                        "args": [
                          1.0,
                          {
                            "op": "*",
                            "args": [
                              -1.0,
                              "zeta_clip"
                            ]
                          }
                        ]
                      },
                      0.0
                    ]
                  },
                  1.5
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "lhs": "sigma_u",
      "rhs": {
        "op": "sqrt",
        "args": [
          "sigma_u2"
        ]
      }
    },
    {
      "lhs": "sigma_v2",
      "rhs": {
        "op": "ifelse",
        "args": [
          {
            "op": ">",
            "args": [
              "wthv_num",
              0.0
            ]
          },
          {
            "op": "*",
            "args": [
              0.36,
              {
                "op": "^",
                "args": [
                  "wstar",
                  2
                ]
              }
            ]
          },
          {
            "op": "*",
            "args": [
              5.0,
              {
                "op": "^",
                "args": [
                  "USTAR",
                  2
                ]
              },
              {
                "op": "^",
                "args": [
                  {
                    "op": "max",
                    "args": [
                      {
                        "op": "+",
                        "args": [
                          1.0,
                          {
                            "op": "*",
                            "args": [
                              -1.0,
                              "zeta_clip"
                            ]
                          }
                        ]
                      },
                      0.0
                    ]
                  },
                  1.5
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "lhs": "sigma_v",
      "rhs": {
        "op": "sqrt",
        "args": [
          "sigma_v2"
        ]
      }
    },
    {
      "lhs": "TL_v",
      "rhs": {
        "op": "max",
        "args": [
          "TL_v_min",
          {
            "op": "min",
            "args": [
              "TL_v_max",
              {
                "op": "ifelse",
                "args": [
                  {
                    "op": ">",
                    "args": [
                      "wthv_num",
                      0.0
                    ]
                  },
                  {
                    "op": "*",
                    "args": [
                      0.15,
                      {
                        "op": "/",
                        "args": [
                          "zi",
                          "sigmaw"
                        ]
                      },
                      {
                        "op": "+",
                        "args": [
                          1.0,
                          {
                            "op": "*",
                            "args": [
                              -1.0,
                              {
                                "op": "exp",
                                "args": [
                                  {
                                    "op": "*",
                                    "args": [
                                      -5.0,
                                      "zeta_clip"
                                    ]
                                  }
                                ]
                              }
                            ]
                          }
                        ]
                      }
                    ]
                  },
                  {
                    "op": "*",
                    "args": [
                      0.1,
                      {
                        "op": "/",
                        "args": [
                          "zi",
                          "sigmaw"
                        ]
                      },
                      {
                        "op": "^",
                        "args": [
                          "zeta_clip",
                          0.8
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    },
    {
      "lhs": "wprime_ref",
      "rhs": {
        "op": "ifelse",
        "args": [
          {
            "op": "<=",
            "args": [
              "lev",
              {
                "op": "+",
                "args": [
                  "lev_gnd",
                  0.05
                ]
              }
            ]
          },
          {
            "op": "*",
            "args": [
              {
                "op": "+",
                "args": [
                  1.0,
                  {
                    "op": "*",
                    "args": [
                      -1.0,
                      "w_damp"
                    ]
                  }
                ]
              },
              {
                "op": "abs",
                "args": [
                  "wprime"
                ]
              }
            ]
          },
          {
            "op": "ifelse",
            "args": [
              {
                "op": ">=",
                "args": [
                  "lev",
                  {
                    "op": "+",
                    "args": [
                      "lev_top",
                      -0.05
                    ]
                  }
                ]
              },
              {
                "op": "*",
                "args": [
                  -1.0,
                  {
                    "op": "+",
                    "args": [
                      1.0,
                      {
                        "op": "*",
                        "args": [
                          -1.0,
                          "w_damp"
                        ]
                      }
                    ]
                  },
                  {
                    "op": "abs",
                    "args": [
                      "wprime"
                    ]
                  }
                ]
              },
              "wprime"
            ]
          }
        ]
      }
    }
  ],
  "tests": [
    {
      "id": "t0-unstable-thermodynamics",
      "description": "At t=0 with HFLUX=100 W/m^2 (daytime unstable), verify thermodynamic chain: Tv2m from T2M/QV2M, rho from ideal gas, wthv from heat and moisture fluxes, Lmo negative (unstable), wstar from buoyancy flux, zi from max(PBLH,KMIX0), zeta_clip = z_agl/zi = 0.05 (surface layer), sigmaw from Hanna (1982) USL formula (ARL-224 Eq. 92), sigma_u = sigma_v from Kantha-Clayson (ARL-224 Eq. 91), TL_v from Hanna convective form (ARL-224 Eq. 66).",
      "parameter_overrides": {
        "HFLUX": 100.0,
        "EFLUX": 0.0
      },
      "time_span": {
        "start": 0,
        "end": 0
      },
      "assertions": [
        {
          "variable": "Tv2m",
          "time": 0,
          "expected": 294.76,
          "tolerance": {
            "rel": 0.001
          }
        },
        {
          "variable": "rho",
          "time": 0,
          "expected": 1.1976,
          "tolerance": {
            "rel": 0.001
          }
        },
        {
          "variable": "wthv",
          "time": 0,
          "expected": 0.08363,
          "tolerance": {
            "rel": 0.01
          }
        },
        {
          "variable": "Lmo",
          "time": 0,
          "expected": -24.27,
          "tolerance": {
            "rel": 0.02
          }
        },
        {
          "variable": "wstar",
          "time": 0,
          "expected": 1.406,
          "tolerance": {
            "rel": 0.01
          }
        },
        {
          "variable": "zi",
          "time": 0,
          "expected": 1000.0,
          "tolerance": {
            "abs": 1e-10
          }
        },
        {
          "variable": "zeta_clip",
          "time": 0,
          "expected": 0.05,
          "tolerance": {
            "abs": 1e-10
          }
        },
        {
          "variable": "sigmaw",
          "time": 0,
          "expected": 0.7635,
          "tolerance": {
            "rel": 0.02
          }
        },
        {
          "variable": "sigma_u",
          "time": 0,
          "expected": 0.8437,
          "tolerance": {
            "rel": 0.02
          }
        },
        {
          "variable": "sigma_v",
          "time": 0,
          "expected": 0.8437,
          "tolerance": {
            "rel": 0.02
          }
        },
        {
          "variable": "TL_v",
          "time": 0,
          "expected": 43.46,
          "tolerance": {
            "rel": 0.03
          }
        }
      ]
    },
    {
      "id": "t0-stable-thermodynamics",
      "description": "At t=0 with HFLUX=-50 W/m^2 (nighttime stable), verify Lmo positive (stable/neutral regime), and sigma_w/sigma_u/sigma_v/TL_v using stable/neutral parameterizations (ARL-224 Eqs. 71-76 and Hanna 1982 Eq. 63).",
      "parameter_overrides": {
        "HFLUX": -50.0,
        "EFLUX": 0.0
      },
      "time_span": {
        "start": 0,
        "end": 0
      },
      "assertions": [
        {
          "variable": "Lmo",
          "time": 0,
          "expected": 48.51,
          "tolerance": {
            "rel": 0.02
          }
        },
        {
          "variable": "sigmaw",
          "time": 0,
          "expected": 0.3764,
          "tolerance": {
            "rel": 0.02
          }
        },
        {
          "variable": "sigma_u",
          "time": 0,
          "expected": 0.5774,
          "tolerance": {
            "rel": 0.02
          }
        },
        {
          "variable": "sigma_v",
          "time": 0,
          "expected": 0.6455,
          "tolerance": {
            "rel": 0.02
          }
        },
        {
          "variable": "TL_v",
          "time": 0,
          "expected": 24.15,
          "tolerance": {
            "rel": 0.03
          }
        }
      ]
    },
    {
      "id": "wprime-langevin-decay",
      "description": "With HFLUX=100 W/m^2 and initial wprime=1.0 m/s, the deterministic Langevin equation D(wprime)/dt = -wprime/TL_v gives exponential decay wprime(t) = exp(-t/TL_v). At t~TL_v the solution is ~e^(-1) = 0.3679. TL_v ~ 43.46 s from the unstable USL parameterization; at t=43.5 s, wprime ~ exp(-43.5/43.46) ~ 0.3676.",
      "initial_conditions": {
        "wprime": 1.0,
        "lev": 36.0
      },
      "parameter_overrides": {
        "HFLUX": 100.0,
        "EFLUX": 0.0
      },
      "time_span": {
        "start": 0,
        "end": 43.5
      },
      "assertions": [
        {
          "variable": "wprime",
          "time": 43.5,
          "expected": 0.3676,
          "tolerance": {
            "rel": 0.03
          }
        }
      ]
    },
    {
      "id": "uprime-x-decay-and-lon-drift",
      "description": "With HFLUX=100 W/m^2 and initial uprime_x=1.0 m/s, D(uprime_x)/dt = -uprime_x/TL_h gives uprime_x(t)=exp(-t/TL_h). D(lon)/dt = uprime_x*x_trans, so lon(t) = x_trans*TL_h*(1-exp(-t/TL_h)) = 108*(1-exp(-t/10800)). At t=100 s: uprime_x=exp(-100/10800)~0.9908, lon~108*(1-exp(-0.009259))~0.9958.",
      "initial_conditions": {
        "uprime_x": 1.0,
        "lev": 36.0
      },
      "parameter_overrides": {
        "HFLUX": 100.0,
        "EFLUX": 0.0
      },
      "time_span": {
        "start": 0,
        "end": 100.0
      },
      "assertions": [
        {
          "variable": "uprime_x",
          "time": 100.0,
          "expected": 0.9908,
          "tolerance": {
            "rel": 0.005
          }
        },
        {
          "variable": "lon",
          "time": 100.0,
          "expected": 0.9958,
          "tolerance": {
            "rel": 0.01
          }
        }
      ]
    }
  ],
  "analyses": [
    {
      "id": "stability_regime_sweep",
      "description": "Boundary-layer turbulence parameters (sigmaw, sigma_u, wstar, TL_v) as a function of surface sensible heat flux HFLUX over -100 to 300 W/m^2. Illustrates the transition from stable (HFLUX < 0, Lmo > 0) to neutral (HFLUX ~ 0) to convective (HFLUX > 0, Lmo < 0) regimes. USTAR=0.3 m/s, PBLH=1000 m, z_agl=50 m.",
      "time_span": {
        "start": 0.0,
        "end": 0.0
      },
      "parameter_sweep": {
        "type": "cartesian",
        "dimensions": [
          {
            "parameter": "HFLUX",
            "range": {
              "start": -100.0,
              "stop": 300.0,
              "count": 40,
              "scale": "linear"
            }
          }
        ]
      },
      "plots": [
        {
          "id": "sigmaw_vs_hflux",
          "type": "line",
          "description": "Vertical velocity std dev sigmaw vs surface heat flux.",
          "x": {
            "variable": "HFLUX",
            "label": "HFLUX (W/m^2)"
          },
          "y": {
            "variable": "sigmaw",
            "label": "sigmaw (m/s)"
          }
        },
        {
          "id": "sigma_u_vs_hflux",
          "type": "line",
          "description": "Horizontal velocity std dev sigma_u vs surface heat flux.",
          "x": {
            "variable": "HFLUX",
            "label": "HFLUX (W/m^2)"
          },
          "y": {
            "variable": "sigma_u",
            "label": "sigma_u (m/s)"
          }
        },
        {
          "id": "wstar_vs_hflux",
          "type": "line",
          "description": "Convective velocity scale wstar vs surface heat flux. Zero for stable/neutral (HFLUX <= 0), grows as (HFLUX)^(1/3) for unstable.",
          "x": {
            "variable": "HFLUX",
            "label": "HFLUX (W/m^2)"
          },
          "y": {
            "variable": "wstar",
            "label": "wstar (m/s)"
          }
        },
        {
          "id": "TL_v_vs_hflux",
          "type": "line",
          "description": "Vertical Lagrangian time scale TL_v vs surface heat flux.",
          "x": {
            "variable": "HFLUX",
            "label": "HFLUX (W/m^2)"
          },
          "y": {
            "variable": "TL_v",
            "label": "TL_v (s)"
          }
        }
      ]
    },
    {
      "id": "particle_velocity_decay",
      "description": "Time evolution of turbulent velocity components (wprime, uprime_x) and resulting particle level displacement under the deterministic Langevin drift. Initial conditions: wprime=1 m/s, uprime_x=0.5 m/s, lev=36 (interior domain). HFLUX=100 W/m^2 (unstable), z2lev=0.001. Shows exponential decay of velocity fluctuations: TL_v ~ 43 s for vertical, TL_h = 10800 s for horizontal.",
      "initial_state": {
        "wprime": 1.0,
        "uprime_x": 0.5,
        "lev": 36.0
      },
      "parameters": {
        "HFLUX": 100.0,
        "EFLUX": 0.0,
        "z2lev": 0.001
      },
      "time_span": {
        "start": 0.0,
        "end": 200.0
      },
      "plots": [
        {
          "id": "wprime_decay",
          "type": "line",
          "description": "Turbulent vertical velocity wprime decaying from 1 m/s with time scale TL_v ~ 43 s.",
          "x": {
            "variable": "t",
            "label": "t (s)"
          },
          "y": {
            "variable": "wprime",
            "label": "wprime (m/s)"
          }
        },
        {
          "id": "uprime_x_decay",
          "type": "line",
          "description": "Zonal turbulent velocity uprime_x decaying from 0.5 m/s with time scale TL_h = 10800 s (very slow over this 200 s window).",
          "x": {
            "variable": "t",
            "label": "t (s)"
          },
          "y": {
            "variable": "uprime_x",
            "label": "uprime_x (m/s)"
          }
        },
        {
          "id": "lev_trajectory",
          "type": "line",
          "description": "Particle level coordinate lev(t) drifting upward from level 36 as wprime drives D(lev)/dt = wprime_ref * z2lev.",
          "x": {
            "variable": "t",
            "label": "t (s)"
          },
          "y": {
            "variable": "lev",
            "label": "lev (-)"
          }
        }
      ]
    }
  ]
}