You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to #806, as discussed in #811. #811 fixes the north–south mirror of the geopotential cube; this issue tracks the smaller residual item acknowledged there, so it doesn't get lost when #806 is closed by the merge.
The defect
_load_model_level passes z[0] — the lowest full model level after the axis fix — as z_surface to _calculategeoh, whose contract is the true 2D surface geopotential. The hydrostatic integration therefore starts from the lowest-full-level height instead of the terrain surface.
Measured effect (same harness, raw file, and frozen environment as the #806 / #811 measurements, RAiDER at #811 head 919d6b6): every reconstructed column carries a near-uniform vertical offset of +9.44 to +10.20 m (mean +9.81 m) against a true-surface reference — the height of the lowest full level above ground. Within-column drift is ≤ 0.43 m (std), so the error is essentially a rigid upward shift of each column. Details: reports/ml_offset_regression_pr811.md.
Why the loader can't do better today
Since f81225b (#751), the fetch step recomputes the full-level z cube with calcgeoh and overwrites the downloaded surface z in the stored file. The true surface field is discarded at fetch time, so no loader-side change alone can recover it for freshly fetched files.
Proposed fix
Fetch: keep the downloaded surface geopotential as its own variable (e.g. z_surface) alongside the recomputed full-level cube, instead of overwriting it.
Test: extend test/test_ecmwf_levels.py with a fixture carrying a distinct z_surface, asserting the surface height matches the terrain to well under the ~10 m lowest-level offset.
The reworked ML fetch in #805 (checked at its current head 6586c94) also replaces the downloaded surface z with the recomputed cube, in both its single-request and batch paths. So whichever fetch implementation lands first, the acceptance criterion is the same: fetched model-level files retain the true surface geopotential as a separate variable, and the loader prefers it. If #805 merges first, item 1 above moves into that fetch path.
I'm happy to submit a PR for this once #811 is merged.
Context
Follow-up to #806, as discussed in #811. #811 fixes the north–south mirror of the geopotential cube; this issue tracks the smaller residual item acknowledged there, so it doesn't get lost when #806 is closed by the merge.
The defect
_load_model_levelpassesz[0]— the lowest full model level after the axis fix — asz_surfaceto_calculategeoh, whose contract is the true 2D surface geopotential. The hydrostatic integration therefore starts from the lowest-full-level height instead of the terrain surface.Measured effect (same harness, raw file, and frozen environment as the #806 / #811 measurements, RAiDER at #811 head 919d6b6): every reconstructed column carries a near-uniform vertical offset of +9.44 to +10.20 m (mean +9.81 m) against a true-surface reference — the height of the lowest full level above ground. Within-column drift is ≤ 0.43 m (std), so the error is essentially a rigid upward shift of each column. Details:
reports/ml_offset_regression_pr811.md.Why the loader can't do better today
Since f81225b (#751), the fetch step recomputes the full-level z cube with
calcgeohand overwrites the downloaded surface z in the stored file. The true surface field is discarded at fetch time, so no loader-side change alone can recover it for freshly fetched files.Proposed fix
z_surface) alongside the recomputed full-level cube, instead of overwriting it.z_surfacewhen present; fall back to the current post-Fixes a north–south flip of the geopotential cube in the ECMWF/ERA-5 model-level reader. #811 behavior (z[0], lowest full level) for existing cached files, so nothing breaks.test/test_ecmwf_levels.pywith a fixture carrying a distinctz_surface, asserting the surface height matches the terrain to well under the ~10 m lowest-level offset.Interaction with #805
The reworked ML fetch in #805 (checked at its current head 6586c94) also replaces the downloaded surface z with the recomputed cube, in both its single-request and batch paths. So whichever fetch implementation lands first, the acceptance criterion is the same: fetched model-level files retain the true surface geopotential as a separate variable, and the loader prefers it. If #805 merges first, item 1 above moves into that fetch path.
I'm happy to submit a PR for this once #811 is merged.