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.
57 lines
1.8 KiB
Elixir
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
|