Derive upper-air weather layers from HRRR profiles in weather grid queries
This commit is contained in:
parent
489a886b0e
commit
9cbf8b91f4
2 changed files with 153 additions and 37 deletions
|
|
@ -11,6 +11,7 @@ defmodule Microwaveprop.Weather do
|
||||||
alias Microwaveprop.Weather.Sounding
|
alias Microwaveprop.Weather.Sounding
|
||||||
alias Microwaveprop.Weather.Station
|
alias Microwaveprop.Weather.Station
|
||||||
alias Microwaveprop.Weather.SurfaceObservation
|
alias Microwaveprop.Weather.SurfaceObservation
|
||||||
|
alias Microwaveprop.Weather.WeatherLayers
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
|
@ -218,28 +219,33 @@ defmodule Microwaveprop.Weather do
|
||||||
[]
|
[]
|
||||||
|
|
||||||
latest_vt ->
|
latest_vt ->
|
||||||
Repo.all(
|
from(h in HrrrProfile,
|
||||||
from(h in HrrrProfile,
|
where:
|
||||||
where:
|
h.valid_time == ^latest_vt and
|
||||||
h.valid_time == ^latest_vt and
|
h.lat >= ^bounds["south"] and h.lat <= ^bounds["north"] and
|
||||||
h.lat >= ^bounds["south"] and h.lat <= ^bounds["north"] and
|
h.lon >= ^bounds["west"] and h.lon <= ^bounds["east"] and
|
||||||
h.lon >= ^bounds["west"] and h.lon <= ^bounds["east"] and
|
fragment("(? * 1000)::int % 125 = 0", h.lat) and
|
||||||
fragment("(? * 1000)::int % 125 = 0", h.lat) and
|
fragment("(? * 1000)::int % 125 = 0", h.lon),
|
||||||
fragment("(? * 1000)::int % 125 = 0", h.lon),
|
select: %{
|
||||||
select: %{
|
lat: h.lat,
|
||||||
lat: h.lat,
|
lon: h.lon,
|
||||||
lon: h.lon,
|
valid_time: h.valid_time,
|
||||||
valid_time: h.valid_time,
|
temperature: h.surface_temp_c,
|
||||||
temperature: h.surface_temp_c,
|
dewpoint_depression: fragment("? - ?", h.surface_temp_c, h.surface_dewpoint_c),
|
||||||
dewpoint_depression: fragment("? - ?", h.surface_temp_c, h.surface_dewpoint_c),
|
bl_height: h.hpbl_m,
|
||||||
bl_height: h.hpbl_m,
|
pwat: h.pwat_mm,
|
||||||
pwat: h.pwat_mm,
|
refractivity_gradient: h.min_refractivity_gradient,
|
||||||
refractivity_gradient: h.min_refractivity_gradient,
|
ducting: h.ducting_detected,
|
||||||
ducting: h.ducting_detected,
|
surface_pressure_mb: h.surface_pressure_mb,
|
||||||
surface_pressure_mb: h.surface_pressure_mb
|
surface_temp_c: h.surface_temp_c,
|
||||||
}
|
surface_dewpoint_c: h.surface_dewpoint_c,
|
||||||
)
|
surface_refractivity: h.surface_refractivity,
|
||||||
|
profile: h.profile,
|
||||||
|
duct_characteristics: h.duct_characteristics
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.map(&derive_and_clean/1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -248,23 +254,39 @@ defmodule Microwaveprop.Weather do
|
||||||
snapped_lat = Float.round(Float.round(lat / step) * step, 3)
|
snapped_lat = Float.round(Float.round(lat / step) * step, 3)
|
||||||
snapped_lon = Float.round(Float.round(lon / step) * step, 3)
|
snapped_lon = Float.round(Float.round(lon / step) * step, 3)
|
||||||
|
|
||||||
Repo.one(
|
from(h in HrrrProfile,
|
||||||
from(h in HrrrProfile,
|
where: h.lat == ^snapped_lat and h.lon == ^snapped_lon and h.valid_time == ^valid_time,
|
||||||
where: h.lat == ^snapped_lat and h.lon == ^snapped_lon and h.valid_time == ^valid_time,
|
select: %{
|
||||||
select: %{
|
lat: h.lat,
|
||||||
lat: h.lat,
|
lon: h.lon,
|
||||||
lon: h.lon,
|
valid_time: h.valid_time,
|
||||||
valid_time: h.valid_time,
|
temperature: h.surface_temp_c,
|
||||||
temperature: h.surface_temp_c,
|
dewpoint_depression: fragment("? - ?", h.surface_temp_c, h.surface_dewpoint_c),
|
||||||
dewpoint_depression: fragment("? - ?", h.surface_temp_c, h.surface_dewpoint_c),
|
bl_height: h.hpbl_m,
|
||||||
bl_height: h.hpbl_m,
|
pwat: h.pwat_mm,
|
||||||
pwat: h.pwat_mm,
|
refractivity_gradient: h.min_refractivity_gradient,
|
||||||
refractivity_gradient: h.min_refractivity_gradient,
|
ducting: h.ducting_detected,
|
||||||
ducting: h.ducting_detected,
|
surface_pressure_mb: h.surface_pressure_mb,
|
||||||
surface_pressure_mb: h.surface_pressure_mb
|
surface_temp_c: h.surface_temp_c,
|
||||||
}
|
surface_dewpoint_c: h.surface_dewpoint_c,
|
||||||
)
|
surface_refractivity: h.surface_refractivity,
|
||||||
|
profile: h.profile,
|
||||||
|
duct_characteristics: h.duct_characteristics
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|> Repo.one()
|
||||||
|
|> then(fn
|
||||||
|
nil -> nil
|
||||||
|
row -> derive_and_clean(row)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp derive_and_clean(row) do
|
||||||
|
derived = WeatherLayers.derive(row)
|
||||||
|
|
||||||
|
row
|
||||||
|
|> Map.merge(derived)
|
||||||
|
|> Map.drop([:profile, :duct_characteristics, :surface_temp_c, :surface_dewpoint_c])
|
||||||
end
|
end
|
||||||
|
|
||||||
def upsert_hrrr_profile(attrs) do
|
def upsert_hrrr_profile(attrs) do
|
||||||
|
|
|
||||||
|
|
@ -116,6 +116,57 @@ defmodule Microwaveprop.WeatherGridTest do
|
||||||
assert length(result) == 1
|
assert length(result) == 1
|
||||||
assert hd(result).temperature == 28.0
|
assert hd(result).temperature == 28.0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "includes derived upper-air fields and strips raw profile data" do
|
||||||
|
now = DateTime.truncate(DateTime.utc_now(), :second)
|
||||||
|
|
||||||
|
profile = [
|
||||||
|
%{"pres" => 1000.0, "tmpc" => 25.0, "dwpc" => 18.0, "hght" => 100.0},
|
||||||
|
%{"pres" => 925.0, "tmpc" => 20.0, "dwpc" => 14.0, "hght" => 800.0},
|
||||||
|
%{"pres" => 850.0, "tmpc" => 15.0, "dwpc" => 10.0, "hght" => 1500.0},
|
||||||
|
%{"pres" => 700.0, "tmpc" => 5.0, "dwpc" => -2.0, "hght" => 3100.0},
|
||||||
|
%{"pres" => 500.0, "tmpc" => -15.0, "dwpc" => -25.0, "hght" => 5600.0}
|
||||||
|
]
|
||||||
|
|
||||||
|
duct_chars = [
|
||||||
|
%{"base" => 200.0, "strength" => 50.0},
|
||||||
|
%{"base" => 800.0, "strength" => 30.0}
|
||||||
|
]
|
||||||
|
|
||||||
|
insert_hrrr_profile(%{
|
||||||
|
lat: 32.125,
|
||||||
|
lon: -97.375,
|
||||||
|
valid_time: now,
|
||||||
|
surface_temp_c: 25.0,
|
||||||
|
surface_dewpoint_c: 18.0,
|
||||||
|
surface_pressure_mb: 1013.0,
|
||||||
|
surface_refractivity: 320.0,
|
||||||
|
hpbl_m: 500.0,
|
||||||
|
pwat_mm: 30.0,
|
||||||
|
min_refractivity_gradient: -280.0,
|
||||||
|
ducting_detected: true,
|
||||||
|
profile: profile,
|
||||||
|
duct_characteristics: duct_chars
|
||||||
|
})
|
||||||
|
|
||||||
|
bounds = %{"south" => 32.0, "north" => 33.0, "west" => -98.0, "east" => -97.0}
|
||||||
|
[point] = Weather.latest_weather_grid(bounds)
|
||||||
|
|
||||||
|
# Derived fields should be present
|
||||||
|
assert is_float(point.surface_rh)
|
||||||
|
assert point.surface_refractivity == 320.0
|
||||||
|
assert point.temp_850mb == 15.0
|
||||||
|
assert point.dewpoint_850mb == 10.0
|
||||||
|
assert is_float(point.lapse_rate)
|
||||||
|
assert point.duct_base_m == 200.0
|
||||||
|
assert point.duct_strength == 50.0
|
||||||
|
|
||||||
|
# Raw bulky fields should be stripped
|
||||||
|
refute Map.has_key?(point, :profile)
|
||||||
|
refute Map.has_key?(point, :duct_characteristics)
|
||||||
|
refute Map.has_key?(point, :surface_temp_c)
|
||||||
|
refute Map.has_key?(point, :surface_dewpoint_c)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "weather_point_detail/3" do
|
describe "weather_point_detail/3" do
|
||||||
|
|
@ -146,6 +197,49 @@ defmodule Microwaveprop.WeatherGridTest do
|
||||||
now = DateTime.truncate(DateTime.utc_now(), :second)
|
now = DateTime.truncate(DateTime.utc_now(), :second)
|
||||||
assert Weather.weather_point_detail(32.15, -97.40, now) == nil
|
assert Weather.weather_point_detail(32.15, -97.40, now) == nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "includes derived upper-air fields and strips raw profile data" do
|
||||||
|
now = DateTime.truncate(DateTime.utc_now(), :second)
|
||||||
|
|
||||||
|
profile = [
|
||||||
|
%{"pres" => 1000.0, "tmpc" => 25.0, "dwpc" => 18.0, "hght" => 100.0},
|
||||||
|
%{"pres" => 850.0, "tmpc" => 15.0, "dwpc" => 10.0, "hght" => 1500.0},
|
||||||
|
%{"pres" => 500.0, "tmpc" => -15.0, "dwpc" => -25.0, "hght" => 5600.0}
|
||||||
|
]
|
||||||
|
|
||||||
|
duct_chars = [%{"base" => 300.0, "strength" => 40.0}]
|
||||||
|
|
||||||
|
insert_hrrr_profile(%{
|
||||||
|
lat: 32.125,
|
||||||
|
lon: -97.375,
|
||||||
|
valid_time: now,
|
||||||
|
surface_temp_c: 25.0,
|
||||||
|
surface_dewpoint_c: 18.0,
|
||||||
|
surface_pressure_mb: 1013.0,
|
||||||
|
surface_refractivity: 320.0,
|
||||||
|
hpbl_m: 500.0,
|
||||||
|
pwat_mm: 30.0,
|
||||||
|
min_refractivity_gradient: -280.0,
|
||||||
|
ducting_detected: true,
|
||||||
|
profile: profile,
|
||||||
|
duct_characteristics: duct_chars
|
||||||
|
})
|
||||||
|
|
||||||
|
result = Weather.weather_point_detail(32.15, -97.40, now)
|
||||||
|
|
||||||
|
# Derived fields
|
||||||
|
assert is_float(result.surface_rh)
|
||||||
|
assert result.surface_refractivity == 320.0
|
||||||
|
assert result.temp_850mb == 15.0
|
||||||
|
assert result.duct_base_m == 300.0
|
||||||
|
assert result.duct_strength == 40.0
|
||||||
|
|
||||||
|
# Raw fields stripped
|
||||||
|
refute Map.has_key?(result, :profile)
|
||||||
|
refute Map.has_key?(result, :duct_characteristics)
|
||||||
|
refute Map.has_key?(result, :surface_temp_c)
|
||||||
|
refute Map.has_key?(result, :surface_dewpoint_c)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp insert_hrrr_profile(attrs) do
|
defp insert_hrrr_profile(attrs) do
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue