feat(studio): ERA5 climatology — committed product, derived ambient state, stage-1 panel (task 1.1) - #96
Merged
Merged
Conversation
Raised in use: "I do not see ERA5 data being utilized" -- correct. SCIENCE-1 and BLOCKING-5 decided ERA5 zonal-mean monthly; nothing implemented it. Now: The committed product. data/pipelines/era5_zonal_monthly.py fetches ERA5 monthly means (1991-2020 WMO normal, 15 levels 300-5 hPa, 2.5 deg -- ASSUMPTION-9) and reduces 343 MB of raw fields to a 0.1 MB (month x level x lat) npz, committed with a sha256 manifest. Raw ERA5 is never redistributed; the derived product is, with attribution. The loader verifies the checksum once per process and refuses to extrapolate outside 300-5 hPa. Schema 0.4.0. site.dataset (USER | ERA5) with the emission-system structure: entered given_temperature_k / given_h2o_ppmv kept-but-inert, temperature_k / h2o_ppmv DERIVED. Pressure stays entered -- it places the box, so it is the lookup's coordinate rather than its result. Default USER: every existing config resolves byte-identically. Provenance records the dataset id + checksum automatically for ERA5 runs (ProvenanceRecord.datasets, carried empty since Phase 0, now used); the seam refuses an unresolved temperature. The wizard: stage 1 gains the ERA5 temperature-profile panel, box marked on it. The physics tests paid for themselves before the first commit. The acceptance suite asserts the stratosphere looks like the stratosphere (tropical cold point 185-205 K, summer pole warmer than winter, H2O single-digit ppmv). The first cut shipped CDS's DESCENDING pressure levels into an interpolation that assumed ascending: every lookup silently read the 300 hPa row, and T(55 hPa) came back 240 K with 414 ppmv of water -- plausible at a glance, impossible as a stratosphere. Fixed in the pipeline (both axes sorted ascending) and REFUSED in the loader (a non-monotonic axis raises rather than being reordered, because the checksum pins what was read). The headline: ERA5 says 209.42 K at the ensemble's site (30N, June, 55 hPa) where the ensemble typed 210 K -- the paper's number confirmed to half a kelvin by 30 years of reanalysis. H2O is 4.15 ppmv against the typed 6.91 (RH = 3% assumption), consistent with the known reanalysis dry bias (MLS: issue #94). Tropical cold point 191.4 K. 357 Python Tier-A (+11 -- and the climatology tests run in CI, since the product is committed rather than gitignored or a submodule), 55 vitest. Hash 0.3.0 -> 0.4.0 (...7813ae -> ...d35ba6): same default run, three more fields describing it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EaDHnPdiacsybr8Tp5WnqR
aliakherati
force-pushed
the
feat/era5-climatology
branch
from
August 19, 2026 23:52
7d84796 to
040ce9a
Compare
Reported from use: "in the atmosphere the pressure goes down as you go up." The profile panel drew 300 hPa at the TOP of the frame -- an upside-down atmosphere -- while its own caption claimed the opposite. The chart's default y mapping (domain minimum at the bottom) is right for every quantity that grows upward and exactly wrong for pressure as a vertical coordinate. Chart gains yReverse, which swaps the PIXEL range rather than the domain, so tick generation stays on an ascending domain. The profile panel uses it, and its caption now describes what is actually drawn: up on the chart is up in the atmosphere. Chart.test.tsx pins the orientation in both modes by reading marker pixel positions out of the rendered SVG -- the caption asserting one thing while the axis did another is exactly the drift a text-free render test catches. 58 vitest (+3). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EaDHnPdiacsybr8Tp5WnqR
Requested in review: translate the profile into height. Implemented as a second LABELING of the same axis rather than a second scale -- altitude and pressure are the same coordinate related by the product's own geopotential at this latitude and month, so round-kilometre ticks are placed at the pressures where those altitudes actually sit (20 km at 56.8 hPa in June at 30N), and the box marker names its own altitude. The number that makes this worth having: z(30N, June, 55 hPa) = 20.20 km, and the ensemble's site label is literally "30N_20km" -- the product confirms the site name. Deliberately NOT a dual-axis chart: an independent second y-scale is the one chart crime the dataviz rules forbid outright. rightTicks name positions in the PRIMARY domain, so the two labelings cannot disagree; a round-trip test pins each tick to its claimed altitude within the interpolation's own error. 357 -> 360 Python Tier-A, 58 vitest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EaDHnPdiacsybr8Tp5WnqR
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
"I do not see ERA5 data being utilized" — fixed, end to end
The decisions existed (SCIENCE-1, BLOCKING-5); nothing implemented them. This PR is task 1.1
complete: fetch → committed product → derivation → wizard.
The committed product
data/pipelines/era5_zonal_monthly.pyfetches ERA5 monthly means — 1991–2020 (WMO normal),15 levels, 300–5 hPa, 2.5° grid (ASSUMPTION-9) — and reduces 343 MB of raw fields to a 0.1 MB
(month × level × lat)npz, committed atstudio/science/data/with a sha256 manifest. Raw ERA5 isnever redistributed; the derived product is, with attribution. The loader checksum-verifies once per
process and refuses to extrapolate outside 300–5 hPa (ADR-005).
Schema 0.4.0 — the ambient state gets a source
Same structure as the emission system:
dataset = USER(default)dataset = ERA5given_temperature_k,given_h2o_ppmvtemperature_k = given,h2o_ppmv = givenPressure stays entered — it places the box, so it is the lookup's coordinate, not its result.
Default USER: every existing config resolves byte-identically. Provenance records the dataset
id + checksum automatically for ERA5 runs (
ProvenanceRecord.datasets, carried empty sincePhase 0, now used).
The physics tests paid for themselves before the first commit
The acceptance suite asserts the stratosphere looks like the stratosphere: tropical cold point
185–205 K, summer pole warmer than winter pole, H₂O single-digit ppmv. The first cut of the pipeline
shipped CDS's descending pressure levels into an interpolation that assumed ascending — every
lookup silently read the 300 hPa row, and T(55 hPa) came back 240 K with 414 ppmv of water.
Plausible numbers at a glance; impossible as a stratosphere.
Fixed in the pipeline (both axes sorted ascending) and refused in the loader — a non-monotonic
axis raises rather than being reordered, because the checksum pins what was read.
The headline
The paper's site temperature is confirmed to half a kelvin by 30 years of reanalysis.
In the wizard
Stage 1 gains the ERA5 temperature-profile panel (pressure on a log axis, the box marked on it) —
under USER it is context; under ERA5 the marker is the run's temperature. Flip "Ambient state
from" to
era5and T/H₂O derive from latitude, month and pressure, go stale correctly when youmove them, and carry the dataset checksum into provenance.
357 Python Tier-A (+11), 55 vitest. The climatology tests run in CI — the product is
committed, so this is the first substantive science whose tests CI actually executes.