Scope freeze — RadNet near-real-time gamma series (radiation_monitor refinement)
Decided 2026-06-27 (Mike). A REFINEMENT of the existing radiation_monitor Locationdex
kind, not a new kind. Mike's call when the URL came in: place it as a second measured
series on the same station slots ("as few dex categories as possible"). The first series is
the RadNet lab assay product (radionuclide_concentration); this adds the near-real-time
gamma monitor.
Source
EPA RadNet near-real-time gamma archive, https://www.epa.gov/radnet/radnet-csv-file-downloads. One ZIP per city, yearly CSVs inside (2007→present). 140 ZIPs / 137 distinct cities. Every CSV row is one roughly-hourly reading at the city's fixed gamma monitor:
DOSE EQUIVALENT RATE (nSv/h)— gamma dose rate. Populated from ~2011 onward; null in the early years.GAMMA COUNT RATE R02..R09 (CPM)— raw counts per energy band. The complete scalar, present across the whole 2007→present record.
This is the "finer radiation source (exact monitor coords, gamma gross-count series)" Mike
flagged in docs/dex-triage-surfaced.md to refine the kind.
Measured reality — IN
A detector counting actual gammas. Direct measurement, no model. Passes the bright line
(feedback_measured_reality_only).
Two series per slot — "data is data"
The lab assay series and the gamma series have different instruments, cadences, and coverage
spans. Both are carried (feedback_data_is_data_partial_coverage); neither is dropped for
covering fewer years than the other. Within the gamma series itself, the dose-rate column is
shorter (≈2011→) than the count-rate bands (2007→); the summary reports each span separately
(n_dose_rate vs n_observations) rather than discarding the early count-only years.
Storage — spine-parquet sidecar, NOT the primary store
The full hourly series is ~21M readings, ≈35× the 591k-row lab feed. A Locationdex slot only
needs a breadth summary, so the hourly series is staged as ONE place-sorted spine-parquet
sidecar — data/location_storehouse/radiation_monitor/gamma_spine.parquet — with a per-station
summary json (gamma_summary.json) that the slot builder reads. This is the same
"large series → spine-parquet, summarize onto the slot" pattern used by drought and the
severe-wx split, and it keeps 21M rows out of the primary observations table. A paper needing
row-level gamma reads the parquet directly.
Built by src/terrapulse/monitor/radiation_monitor_gamma.py; attached to slots by
radiation_monitor_locationdex.py (gamma_series_summary / has_gamma_series /
epa_radnet_gamma added to the slot's sources).
Decisions / gotchas frozen here
- Timestamps are UTC. The EPA page states: "Near-real-time air data results are displayed in Coordinated Universal Time (UTC)." No local-time conversion is applied. (The bulk CSVs carry the raw UTC stamp; the page's "convert to local" widget is a display-only feature.)
- City → loc_num mapping. The gamma archive is keyed by city (
AL: BIRMINGHAM); the slots are keyed by RadNetloc_num. All 137 gamma cities match an existing slot by (state, city). A city with multiple station slots (≈22 of 311 cities have >1 loc_num) shares its one city-level gamma monitor across those slots — the gamma summary is attached to each. - curl, not urllib/httpx — the known sandbox hang.
- Energy-band semantics. R02..R09 are EPA's detector energy windows (gross counts per band), not analyte concentrations. The slot summary keeps them as a count of bands plus the dose-rate sub-summary; it does not invent a single "radiation level" scalar.
Deferred (not in v1)
- Live edge. The bulk CSVs are posted in yearly batches; a near-real-time current-year pull (EPA also exposes a per-station JSON API) is a separate decision, same as the lab feed.
- PG ingest of the hourly series. If a paper needs SQL-level access to individual gamma
readings, register
epa_radnet_gammaas a measured PG feed from the spine-parquet then. - Per-band physical calibration (energy-window → keV mapping, dose reconstruction).
- Exact monitor coordinates. Slots remain city-centroid (geocoded, flagged), inherited from the lab kind; the near-real-time page does not publish per-monitor lat/lon in the CSVs.