- skew_t_test: replace length(list) > 0 with list != [] - conn_case: alias DataCase, GridCache, ScoreCache at top
128 lines
4.6 KiB
Elixir
128 lines
4.6 KiB
Elixir
defmodule MicrowavepropWeb.SkewTTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias MicrowavepropWeb.SkewT
|
|
|
|
describe "saturation_vapor_pressure/1 (Magnus)" do
|
|
test "reference values are within 1% of textbook" do
|
|
# At 0°C the Magnus formula gives ≈ 6.112 hPa.
|
|
assert_in_delta SkewT.saturation_vapor_pressure(0.0), 6.112, 0.05
|
|
|
|
# At 20°C es ≈ 23.37 hPa.
|
|
assert_in_delta SkewT.saturation_vapor_pressure(20.0), 23.37, 0.2
|
|
|
|
# At -20°C es ≈ 1.254 hPa.
|
|
assert_in_delta SkewT.saturation_vapor_pressure(-20.0), 1.254, 0.05
|
|
end
|
|
end
|
|
|
|
describe "temperature_from_mixing_ratio/2" do
|
|
test "round-trips mixing ratios" do
|
|
# Pick a pressure and temperature, compute ws, invert, expect the original.
|
|
p = 850.0
|
|
|
|
for t <- [-10.0, 0.0, 15.0, 25.0] do
|
|
es = SkewT.saturation_vapor_pressure(t)
|
|
ws_g_kg = 622.0 * es / (p - es)
|
|
recovered = SkewT.temperature_from_mixing_ratio(ws_g_kg, p)
|
|
assert_in_delta recovered, t, 0.05
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "dry_adiabat_temperature/2" do
|
|
test "θ == T at 1000 mb" do
|
|
# At the reference pressure the potential temperature equals the
|
|
# actual temperature — so θ = 300 K ⇒ T(1000) = 300 - 273.15 ≈ 26.85°C.
|
|
assert_in_delta SkewT.dry_adiabat_temperature(300.0, 1000.0), 26.85, 0.02
|
|
end
|
|
|
|
test "a parcel lifted dry-adiabatically cools" do
|
|
# Lifting from 1000 mb (θ = 290 K, T ≈ 16.85°C) to 700 mb should
|
|
# drop the temperature by ~26°C (roughly 9.8°C/km, ~3 km).
|
|
t_surface = SkewT.dry_adiabat_temperature(290.0, 1000.0)
|
|
t_700 = SkewT.dry_adiabat_temperature(290.0, 700.0)
|
|
assert t_surface - t_700 > 20.0
|
|
end
|
|
end
|
|
|
|
describe "project/3" do
|
|
setup do
|
|
chart = SkewT.chart_config(width: 400, height: 500)
|
|
%{chart: chart}
|
|
end
|
|
|
|
test "p_bot lands at the chart bottom and p_top at the top", %{chart: chart} do
|
|
{_x_bot, y_bot} = SkewT.project(0.0, chart.p_bot, chart)
|
|
{_x_top, y_top} = SkewT.project(0.0, chart.p_top, chart)
|
|
|
|
# SVG y grows downward, so p_bot (bottom) has a larger y than p_top.
|
|
assert y_bot > y_top
|
|
assert_in_delta y_bot, chart.padding_top + chart.plot_height, 0.5
|
|
assert_in_delta y_top, chart.padding_top, 0.5
|
|
end
|
|
|
|
test "isotherms skew right as pressure decreases", %{chart: chart} do
|
|
# Same temperature at two pressures should have a larger x at
|
|
# the lower pressure (top of chart) because of the skew.
|
|
{x_bot, _} = SkewT.project(0.0, chart.p_bot, chart)
|
|
{x_top, _} = SkewT.project(0.0, chart.p_top, chart)
|
|
assert x_top > x_bot
|
|
end
|
|
|
|
test "temperature range spans the bottom of the chart", %{chart: chart} do
|
|
# At p_bot, t_min lands at the left edge of the data area.
|
|
{x_min, _} = SkewT.project(chart.t_min, chart.p_bot, chart)
|
|
{x_max, _} = SkewT.project(chart.t_max, chart.p_bot, chart)
|
|
assert_in_delta x_min, chart.padding_left, 0.5
|
|
assert_in_delta x_max, chart.padding_left + chart.plot_width, 0.5
|
|
end
|
|
end
|
|
|
|
describe "build/2" do
|
|
test "with no profile returns nil" do
|
|
assert SkewT.build(nil) == nil
|
|
assert SkewT.build([]) == nil
|
|
end
|
|
|
|
test "returns a map of rendering primitives for a valid profile" do
|
|
profile = [
|
|
%{"pres" => 1000.0, "hght" => 100.0, "tmpc" => 25.0, "dwpc" => 18.0},
|
|
%{"pres" => 850.0, "hght" => 1500.0, "tmpc" => 15.0, "dwpc" => 10.0},
|
|
%{"pres" => 700.0, "hght" => 3100.0, "tmpc" => 5.0, "dwpc" => -2.0},
|
|
%{"pres" => 500.0, "hght" => 5600.0, "tmpc" => -15.0, "dwpc" => -25.0}
|
|
]
|
|
|
|
chart = SkewT.build(profile)
|
|
|
|
assert is_map(chart)
|
|
assert chart.width > 0
|
|
assert chart.height > 0
|
|
# Background grid
|
|
assert chart.isobars != []
|
|
assert chart.isotherms != []
|
|
assert chart.dry_adiabats != []
|
|
assert chart.mixing_ratio_lines != []
|
|
# Traces
|
|
assert chart.t_points != []
|
|
assert chart.td_points != []
|
|
# The first plotted t_point should correspond to the highest pressure
|
|
# (surface-most) profile entry.
|
|
assert length(chart.t_points) == 4
|
|
end
|
|
|
|
test "filters out profile levels with missing temperature or dewpoint independently" do
|
|
profile = [
|
|
%{"pres" => 1000.0, "tmpc" => 25.0, "dwpc" => 18.0},
|
|
%{"pres" => 900.0, "tmpc" => nil, "dwpc" => 15.0},
|
|
%{"pres" => 850.0, "tmpc" => 15.0, "dwpc" => nil},
|
|
%{"pres" => 700.0, "tmpc" => 5.0, "dwpc" => -2.0}
|
|
]
|
|
|
|
chart = SkewT.build(profile)
|
|
# 3 levels have tmpc, 3 have dwpc — the traces are independent.
|
|
assert length(chart.t_points) == 3
|
|
assert length(chart.td_points) == 3
|
|
end
|
|
end
|
|
end
|