prop/lib/microwaveprop/propagation
Graham McIntire 63f25a9612
Some checks failed
Build prop-grid-rs / Test, build, push (push) Successful in 5m54s
Build and Push / Build and Push Docker Image (push) Failing after 4m44s
perf(grid-rs): dense grid, fused scoring pass, and columnar .pgrid profiles
Reworks the post-fetch half of the propagation pipeline. Fetch and GRIB2
decode were already cheap — measured against a live HRRR cycle, all 39
pressure messages decode via `wgrib2 -lola` in 0.29 s and the 31 MB
byte-range fetch takes ~3 s — so nothing here touches the decoder. All the
cost was downstream.

Also fixes a broken NOTIFY that made every chain step run up to 5 times.

pg_notify
  `NOTIFY propagation_ready, $1` is a Postgres syntax error: NOTIFY is a
  utility statement whose payload must be a literal, so a bind raises
  42601. It shared a transaction with the `status='done'` UPDATE, so every
  successful step rolled back, stayed 'running', and was requeued by
  reclaim_stale_running up to @max_reclaim_attempts times. Elixir's
  NotifyListener never fired either, so ScoreCache warm and the
  "propagation:updated" fan-out were dead.

FieldGrid
  A decoded grid was HashMap<(i32,i32), HashMap<Arc<str>, f32>> — a dense
  rectangular grid stored as ~95k nested hash maps, costing ~4.6M inserts
  on decode, ~3.7M on merge and ~14M lookups across three derivation
  passes. wgrib2 -lola already emits one dense row-major f32 block per
  message, so keep it: dense per-message planes, names hashed once per
  grid into plane ids, NaN as the missing sentinel. This is what forced
  PROP_GRID_RS_PARALLELISM=1 under a 3Gi limit.

Fused pass
  Three 95k-cell derivation passes plus 23 band-major scoring passes over
  a staged Vec<(f64,f64,Conditions,BandInvariants)> (~19MB re-streamed 23
  times) collapse into one pass: levels extracted once per cell, all 23
  bands scored while the cell is hot, scores accumulated cell-major so
  rayon chunks own disjoint slices. Scores land straight in the dense
  score-file body — no ScorePoint scatter.

.pgrid
  The profile artifact was an rmpv tree plus gzip -9, written 30x an hour,
  and ProfilesFile.read_point/3 gunzipped and unpacked the entire 95k-cell
  file to return one cell on every map click and Skew-T load. Replaced
  with a dense cell-major f32 record array carrying a self-describing
  field table. Elixir reads it via :file.pread; .mp.gz and .etf.gz remain
  readable so files written before this drain out of the 48h window.

  Measured on a full CONUS grid (95,073 cells x 48 planes x 23 bands):
    derive + score + build artifacts   0.022 s
    profile write   3.957 s -> 0.006 s (22.0 MB -> 22.4 MB on disk)
    single-cell read   whole-file decode -> 0.5 us
    23 score files     0.003 s

Also
  - hrrr_points: batched UNNEST upsert replacing one awaited INSERT per
    point. Keeps ON CONFLICT DO UPDATE — the PSKR sampler's two-pass loop
    depends on it.
  - fetcher: real semaphore capping in-flight ranges at
    MAX_PARALLEL_RANGES, which the comment claimed but the code did not do
    (it spawned all 27 while the connection pool was sized for 8).
  - metrics: per-stage histogram. Only chain-step and decode durations
    were instrumented, which is why the write cost stayed invisible.
  - profiles_file: parse_valid_time anchors on the known extension set, so
    sibling-suffixed names like <iso>.hrdps.prop no longer parse as
    <iso>.hrdps and vanish from prune and list operations.
  - PROP_GRID_RS_PARALLELISM 1 -> 3. Memory limit held at 3Gi until RSS is
    observed at the new parallelism.
  - cargo fmt over the crate; worker.rs, hrdps_fetcher.rs and nexrad.rs
    were already unformatted at HEAD and the pre-commit hook gates on it.

HRDPS still runs at 0.5 degrees. wgrib2 -lola scales linearly in output
points on rotated lat/lon (12.5 s wall, 202 s CPU for one message at
0.125 degrees) because it has no inverse projection for those grids; a raw
native dump is 0.32 s. The fix is decode-once plus a closed-form
rotated-pole index, left for a follow-up.
2026-08-01 08:23:36 -05:00
..
band_config.ex perf(algo): unify recalibration into single Python pipeline with JSON output 2026-05-25 14:45:55 -05:00
band_weights.ex perf(algo): unify recalibration into single Python pipeline with JSON output 2026-05-25 14:45:55 -05:00
common_volume.ex refactor: deduplicate helpers and simplify Scorer 2026-04-28 14:14:34 -05:00
duct.ex fix: resolve all 281 credo issues across source and test files 2026-07-29 09:04:21 -05:00
eme.ex feat(eme): add /eme Earth-Moon-Earth calculator 2026-04-23 16:18:20 -05:00
freshness_monitor.ex perf+hygiene: batch 1 of system-review fixes 2026-04-21 17:06:07 -05:00
grid.ex Fix all medium findings and split contact_live_test.exs 2026-05-29 15:53:04 -05:00
grid_task.ex fix: resolve 27 security, architecture, test, and performance audit findings 2026-07-27 18:19:37 -05:00
grid_task_enqueuer.ex fix: resolve 18 bugs across LiveViews, schemas, tests, and logic 2026-07-29 07:46:54 -05:00
hf_muf.ex Fix all medium findings and split contact_live_test.exs 2026-05-29 15:53:04 -05:00
inversion.ex Add dialyzer specs and types across the codebase 2026-04-12 08:55:04 -05:00
mechanism_classifier.ex fix: resolve 391 Credo issues across codebase 2026-06-12 13:51:32 -05:00
mode_thresholds.ex fix: resolve all dialyzer type errors across project (46→0 project errors) 2026-06-08 17:51:13 -05:00
moon_ephemeris.ex feat(propagation): per-contact mechanism classification 2026-04-18 10:42:08 -05:00
notify_listener.ex fix: resolve 27 security, architecture, test, and performance audit findings 2026-07-27 18:19:37 -05:00
path_analysis.ex fix: resolve 18 bugs across LiveViews, schemas, tests, and logic 2026-07-29 07:46:54 -05:00
path_compute.ex fix: two test issues causing 37 failures 2026-07-22 16:37:17 -05:00
pgrid.ex perf(grid-rs): dense grid, fused scoring pass, and columnar .pgrid profiles 2026-08-01 08:23:36 -05:00
pipeline_status.ex feat(map): shareable URL params for view/band/time 2026-04-19 08:53:07 -05:00
profiles_file.ex perf(grid-rs): dense grid, fused scoring pass, and columnar .pgrid profiles 2026-08-01 08:23:36 -05:00
rain_scatter.ex refactor: deduplicate helpers and simplify Scorer 2026-04-28 14:14:34 -05:00
rain_scatter_classifier.ex feat(rainscatter): classify QSO propagation mechanism from common-volume radar 2026-04-17 15:57:59 -05:00
recalibrator.ex fix(prod-build): silence Nx.* undefined warnings in dev/test-only modules 2026-04-30 12:19:16 -05:00
region.ex fix: April 2026 codebase review — address 13 bugs across propagation chain 2026-04-25 10:52:51 -05:00
run_timing.ex feat: extend HRRR forecast horizon from 18h to 48h 2026-06-10 11:36:58 -05:00
score_cache.ex fix: resolve 7 pre-existing test failures and ScoreCache DateTime bug 2026-06-10 13:50:24 -05:00
scorer.ex updates 2026-06-16 12:38:08 -05:00
scores_file.ex fix: ensure all unused historical NFS data gets cleaned up 2026-07-20 09:27:36 -05:00
sporadic_e.ex Add 50/144/222 MHz bands, rename 440→432 2026-04-16 12:36:10 -05:00