MRMS
----
Layer the NOAA MRMS PrecipRate product onto the score grid so rain fade
updates every 2 minutes instead of every hour alongside HRRR. New modules:
- Microwaveprop.Weather.MrmsClient: fetches the latest .grib2.gz off the
NCEP mirror (Req auto-decompresses so no gunzip step), writes the raw
GRIB2 to a temp file, and calls the existing wgrib2 wrapper with the
0.125 propagation grid spec to get interpolated cells. Returns a
%{{lat, lon} => mm_per_hour} map with missing-value sentinels dropped.
- Microwaveprop.Weather.MrmsCache: ETS-backed GenServer mirroring
ScoreCache/GridCache. Caches a single "current" entry keyed by
valid_time with PubSub broadcast so peer nodes stay in sync and only
the Oban leader pays the fetch + regrid cost.
- Microwaveprop.Workers.MrmsFetchWorker: cron every 2 minutes, short-
circuits when the cached valid_time already matches the newest file.
Microwaveprop.Propagation.AsosNudge.compute/4 now takes an optional
rain_grid. When a cell has MRMS rain >= 0.1 mm/hr it gets patched onto
the HRRR profile's `precip_mm` field (the scorer already reads it there)
and the cell is re-scored even with no ASOS station nearby. Cells with
MRMS rain below the threshold aren't touched so dry cells keep their
raw HRRR scores (which have the wind/sky/native-gradient signal that
isn't persisted on HrrrProfile rows and would otherwise be lost).
AsosAdjustmentWorker pulls MrmsCache on every tick and passes the grid
through to AsosNudge.compute/4. Also skips the IemClient error branch
that can never happen and handles the ASOS-empty + MRMS-empty case
explicitly. MrmsCache wired into the supervision tree; MrmsFetchWorker
cron entry added to config.exs and dev.exs.
Four new AsosNudge cases cover MRMS-only re-scoring, threshold gating,
and the wet/dry score delta.
Beacons 500
-----------
Beacon.format_freq/1 and format_mw/1 crashed on whole-number floats
(e.g. 24192.0) because `frac == 0.0` could become false under float
rounding while `trim_trailing_zeros/1` stripped the decimal point,
leaving a 1-element list that couldn't be destructured as [_, frac].
Shared format_number/1 helper handles integer input directly and
pattern-matches both the "int-only" and "int + frac" shapes.
Added stream_data property tests covering the whole microwave range for
both integers and floats to catch this class of bug before prod.
UTC clock flash
---------------
The /weather and /map UTC clocks were empty until the JS hook mounted
post-WebSocket, producing a several-second blank spot on initial load
and a clobber risk on sidebar re-renders. Mount now computes a
server-rendered `initial_utc_clock` string and the template seeds the
element with that plus `phx-update="ignore"` so LiveView morphdom won't
overwrite what the hook writes.
41 lines
1.3 KiB
Elixir
41 lines
1.3 KiB
Elixir
defmodule Microwaveprop.Workers.MrmsFetchWorker do
|
|
@moduledoc """
|
|
Every ~2 minutes, pull the newest NOAA MRMS PrecipRate grid and cache
|
|
the regridded version (0.125° CONUS) in `Microwaveprop.Weather.MrmsCache`.
|
|
`Microwaveprop.Workers.AsosAdjustmentWorker` reads that cache on its
|
|
own tick to overlay real radar rain rates onto the score grid.
|
|
|
|
Runs on the `propagation` queue so it shares the scoring node's crontab
|
|
and isn't competing with the heavier HRRR pipeline for slots.
|
|
"""
|
|
use Oban.Worker, queue: :propagation, max_attempts: 2
|
|
|
|
alias Microwaveprop.Weather.MrmsCache
|
|
alias Microwaveprop.Weather.MrmsClient
|
|
|
|
require Logger
|
|
|
|
@impl Oban.Worker
|
|
def perform(%Oban.Job{}) do
|
|
case MrmsClient.fetch_latest(MrmsCache.valid_time()) do
|
|
{:ok, valid_time, grid} ->
|
|
MrmsCache.broadcast_put(valid_time, grid)
|
|
|
|
Logger.info("MrmsFetch: cached #{map_size(grid)} cells at #{valid_time} (#{count_rainy(grid)} with rain)")
|
|
|
|
:ok
|
|
|
|
{:up_to_date, valid_time} ->
|
|
Logger.debug("MrmsFetch: cache already has #{valid_time}")
|
|
:ok
|
|
|
|
{:error, reason} ->
|
|
Logger.warning("MrmsFetch: skipped — #{inspect(reason)}")
|
|
:ok
|
|
end
|
|
end
|
|
|
|
defp count_rainy(grid) do
|
|
Enum.count(grid, fn {_, v} -> v > 0 end)
|
|
end
|
|
end
|