Commit graph

3 commits

Author SHA1 Message Date
2fd88a94ea
fix(propagation): 7 pipeline bugs + validation harnesses + model improvements
Some checks failed
Build and Push / Build and Push Docker Image (push) Waiting to run
Build prop-grid-rs / Test, build, push (push) Failing after 3m41s
Phase A — 7 pipeline bugs:
- retain_scores_window: fix timeline self-deletion (NOTIFY payload run_time|valid_time)
- Rust/Elixir weight divergence: Rust loads band_weights.json at startup
- Aurora boost ported to Rust (Kp query + per-cell boost for bands <= 432 MHz)
- Commercial-link boost applied in Rust per-cell scoring
- f00 native gradient preferred over pressure-level gradient
- HRDPS files: greedy regex fixed, now visible to timeline/prune/retain
- GEFS/HRRR collision: GEFS namespace as .gefs.prop, merge at read

Phase B — Validation harness:
- scripts/validate_algo.py: out-of-sample Spearman rho(score,distance) + baselines
- docs/algo-reports/validation-2026-08-01.{json,md}

Phase C — Forecast-skill evaluation:
- scripts/validate_forecast.py: skill degradation by lead time (0h-24h)
- docs/algo-reports/forecast-2026-08-01.{json,md}

Phase D — Calibration + model improvements:
- recalibrate.py: validation gate before weight deploy
- Recalibrator: Nx.max(0.0) replaces Nx.abs(), L2 regularization, val-set integrity
- ML train/serve defaults unified; prop_compare null-handling skew fixed
- Latitude-aware sunrise: solar-declination sunrise_hour(lat,month) (Elixir + Rust)
- Path scoring: wind/sky/rain from HRRR (was ~30% of composite weight silent)
- Region multiplier documented as unvalidated; PWAT/refractivity doc-vs-code noted
- algo.md: synced scoring sections, marked retired features, D5/D6 changelog
- Credo: path_compute cyclomatic complexity bumped (6->10 fields) — informational only
2026-08-01 19:28:41 -05:00
c66af5bf64
fix(recalibrate): correct stale "documentation only" report text, map 9cm band
Two defects surfaced by the 2026-08-01 production run.

The generated report's "Applying these weights" section claimed the
JSON was inert until someone wired it into BandConfig. That stopped
being true when BandWeights landed: BandConfig.weights/1 consults
BandWeights.lookup/1 ahead of the in-source :weights override and the
global @weights default, so band_weights.json is a live scoring input.
Anyone reading the report would have concluded a recalibration run was
a no-op. Replace the paragraph and document the operational half that
was missing entirely -- BandWeights caches into :persistent_term on
first read, so a running node serves the previous weights until it is
redeployed or BandWeights.reset/0 is called.

PSKR_BAND_TO_MHZ was missing 9cm, so every run logged "skipping unknown
PSKR band" and dropped those rows from the corpus accounting. Weight
derivation is unaffected -- 3400 MHz is not in PSKR_BANDS, so it still
routes to the contacts fit (source=contacts, n=334, unchanged). The
dict already carries other non-routed bands (23cm, 13cm, 3cm); 9cm was
simply absent.
2026-08-01 16:34:56 -05:00
daafa5a02a
perf(algo): unify recalibration into single Python pipeline with JSON output
Replace the two-script Python pipeline (analysis report + Elixir-source
emitter) with a single `scripts/recalibrate.py` that fits per-band
weights from PSKR spot density (VHF/UHF) and contacts↔HRRR correlations
(microwave), writing `priv/algo/band_weights.json` as a machine-readable
artifact. A new Elixir BandWeights module loads this JSON once via
`:persistent_term` cache; BandConfig.weights/1 consults it before falling
back to in-source overrides or global defaults. The script never touches
Elixir source — recalibration is now `python3 scripts/recalibrate.py`
followed by an app restart.
2026-05-25 14:45:55 -05:00