defmodule Microwaveprop.Weather.FrontalAnalysisTest do use ExUnit.Case, async: true alias Microwaveprop.Weather.FrontalAnalysis @grid_spec %{lat_start: 30.0, lat_step: 0.5, lon_start: -100.0, lon_step: 0.5} describe "detect_fronts/3" do test "finds no fronts in a uniform temperature field" do # 10x10 grid, all same temperature and pressure temp = Nx.broadcast(295.0, {10, 10}) pres = Nx.broadcast(101_325.0, {10, 10}) result = FrontalAnalysis.detect_fronts(temp, pres, @grid_spec) assert result.front_points == [] end test "detects a front at a sharp east-west temperature boundary" do # North half cold (280K), south half warm (300K) → sharp gradient mid-grid cold = Nx.broadcast(280.0, {5, 10}) warm = Nx.broadcast(300.0, {5, 10}) temp = Nx.concatenate([cold, warm], axis: 0) pres = Nx.broadcast(101_325.0, {10, 10}) result = FrontalAnalysis.detect_fronts(temp, pres, @grid_spec) # Should find front points near the boundary (rows 4-5) assert result.front_points != [] # All front points should be near the middle rows Enum.each(result.front_points, fn {lat, _lon, _bearing} -> assert lat >= 31.5 and lat <= 34.0 end) end end describe "nearest_front/3" do test "returns the closest front point with distance and bearing" do front_points = [ {33.0, -97.0, 90.0}, {35.0, -97.0, 45.0} ] result = FrontalAnalysis.nearest_front(front_points, 33.1, -97.1) assert result.distance_km < 20.0 assert result.front_bearing_deg == 90.0 end test "returns nil for empty front_points" do assert FrontalAnalysis.nearest_front([], 33.0, -97.0) == nil end end describe "path_front_angle/2" do test "parallel paths return 0" do assert FrontalAnalysis.path_front_angle(90.0, 90.0) == 0.0 end test "perpendicular paths return 90" do assert FrontalAnalysis.path_front_angle(0.0, 90.0) == 90.0 end test "anti-parallel paths return 0" do assert FrontalAnalysis.path_front_angle(180.0, 0.0) == 0.0 end test "45-degree offset returns 45" do assert_in_delta FrontalAnalysis.path_front_angle(45.0, 90.0), 45.0, 0.01 end end end