prop/test/microwaveprop/weather/theta_e_test.exs
Graham McIntire 864a91fc5c Phase 2 tasks 2.1-2.4: BL turbulence feature computations
Inversion detection module (Propagation.Inversion):
- find_inversion_top/1 walks the native profile to locate the first
  temperature inversion (surface-based or elevated)
- bulk_richardson/3 computes the Richardson number across the
  inversion layer (Ri < 0.25 = turbulent, > 1 = laminar/good)
- shear_magnitude/3 computes the wind shear vector magnitude
- potential_temperature/2 for θ = T*(P0/P)^0.286

Theta-e module (Weather.ThetaE):
- Bolton (1980) equivalent potential temperature
- dewpoint_from_spfh/2 via Magnus-Tetens inversion
- theta_e_jump/3 for the thermodynamic decoupling metric

mix hrrr_native_derive_fields populates inversion_top_m,
bulk_richardson, theta_e_jump_k, and shear_at_top_ms on existing
hrrr_native_profiles rows.

First real data: 2022-08-20 12Z TX profile shows inversion at
186 m, Ri = 0.16 (turbulent), θ_e jump = 0.33 K — consistent with
marginal propagation conditions at that hour.
2026-04-10 08:21:23 -05:00

57 lines
1.8 KiB
Elixir

defmodule Microwaveprop.Weather.ThetaETest do
use ExUnit.Case, async: true
alias Microwaveprop.Weather.ThetaE
describe "compute/3" do
test "returns a plausible theta-e for standard summer conditions" do
# T=300K, q=0.015, P=101325 Pa → theta-e should be ~340-360 K
theta_e = ThetaE.compute(300.0, 0.015, 101_325.0)
assert theta_e > 330.0
assert theta_e < 370.0
end
test "dry air has theta-e close to potential temperature" do
# q ≈ 0 → theta-e ≈ theta ≈ T * (100000/P)^0.286
theta_e = ThetaE.compute(300.0, 0.0001, 100_000.0)
assert_in_delta theta_e, 300.0, 5.0
end
test "theta-e increases with moisture at constant T and P" do
dry = ThetaE.compute(295.0, 0.005, 100_000.0)
moist = ThetaE.compute(295.0, 0.015, 100_000.0)
assert moist > dry
end
end
describe "dewpoint_from_spfh/2" do
test "returns a plausible dewpoint for typical specific humidity" do
# q=0.01, P=101325 Pa → Td should be around 14°C (287 K)
td = ThetaE.dewpoint_from_spfh(0.01, 101_325.0)
assert td > 280.0
assert td < 295.0
end
test "very dry air has a low dewpoint" do
td = ThetaE.dewpoint_from_spfh(0.001, 100_000.0)
assert td < 265.0
end
end
describe "theta_e_jump/3" do
test "returns the difference in theta-e across two levels" do
profile = %{
temp_k: [295.0, 300.0],
spfh: [0.015, 0.005],
pressure_pa: [101_000.0, 95_000.0]
}
jump = ThetaE.theta_e_jump(profile, 0, 1)
assert is_float(jump)
# The dry upper level should have lower theta-e despite higher T
# because moisture dominates. So the jump could be negative.
# Just verify it's computed.
assert abs(jump) > 0.0
end
end
end