prop/test/microwaveprop/weather/hrrr_native_profile_test.exs
Graham McIntire e7a7ae073d Phase 9.3, 9.4, and Phase 3 NEXRAD pipeline
Task 9.3 - Weight recalibration via gradient descent:
- Recalibrator module fits logistic regression weights using Nx
- Trains on QSO positives vs random baseline negatives
- Cross-validates by month, normalizes weights to sum to 1.0
- Mix task: mix recalibrate_scorer --sample 5000 --epochs 2000

Task 9.4 - Side-by-side scorer comparison:
- ScorerDiff.compare/3 re-scores grid with old vs new weights
- Reports mean diff, regressions, improvements, per-band breakdown
- Mix task: mix scorer_diff --new-weights '{...}'

Phase 3 - NEXRAD ingestion pipeline:
- NexradClient fetches IEM n0q composite PNGs, extracts per-point
  box statistics (mean/max dBZ, texture variance)
- NexradObservation schema with unique (lat, lon, observed_at)
- NexradWorker on :nexrad queue for background processing
- nexrad_texture backtest feature in Features module
- mix nexrad_backfill --limit 200

All tasks added to AdminTaskWorker and Release for production use.
1116 tests, 0 failures.
2026-04-10 12:48:36 -05:00

59 lines
1.9 KiB
Elixir

defmodule Microwaveprop.Weather.HrrrNativeProfileTest do
use Microwaveprop.DataCase, async: true
alias Microwaveprop.Weather.HrrrNativeProfile
@valid_attrs %{
valid_time: ~U[2026-03-28 18:00:00Z],
run_time: ~U[2026-03-28 18:00:00Z],
lat: 32.9,
lon: -97.0,
level_count: 3,
heights_m: [10.0, 100.0, 500.0],
temp_k: [295.0, 292.0, 285.0],
spfh: [0.010, 0.008, 0.004],
pressure_pa: [101_000.0, 99_800.0, 95_000.0],
u_wind_ms: [2.0, 3.0, 4.0],
v_wind_ms: [1.0, 1.5, 2.0],
tke_m2s2: [0.5, 0.3, 0.1],
surface_temp_k: 295.0,
surface_spfh: 0.010,
surface_pressure_pa: 101_325.0
}
describe "changeset/2" do
test "accepts a full native profile" do
changeset = HrrrNativeProfile.changeset(%HrrrNativeProfile{}, @valid_attrs)
assert changeset.valid?
assert {:ok, profile} = Repo.insert(changeset)
assert profile.level_count == 3
assert profile.heights_m == [10.0, 100.0, 500.0]
end
test "requires valid_time, lat, lon" do
changeset = HrrrNativeProfile.changeset(%HrrrNativeProfile{}, %{})
refute changeset.valid?
assert "can't be blank" in errors_on(changeset).valid_time
assert "can't be blank" in errors_on(changeset).lat
assert "can't be blank" in errors_on(changeset).lon
end
test "rejects mismatched level array lengths" do
attrs = Map.put(@valid_attrs, :temp_k, [295.0, 292.0])
changeset = HrrrNativeProfile.changeset(%HrrrNativeProfile{}, attrs)
refute changeset.valid?
assert errors_on(changeset)[:temp_k]
end
test "enforces (lat, lon, valid_time) uniqueness" do
{:ok, _} = %HrrrNativeProfile{} |> HrrrNativeProfile.changeset(@valid_attrs) |> Repo.insert()
{:error, changeset} =
%HrrrNativeProfile{} |> HrrrNativeProfile.changeset(@valid_attrs) |> Repo.insert()
refute changeset.valid?
assert "has already been taken" in errors_on(changeset).lat
end
end
end