Skip to content

Fetch PMP obs4MIPs reference data from ESGF and drop unused ERA datasets#799

Merged
lewisjared merged 5 commits into
mainfrom
obs4mips-fetch-and-era-cleanup
Jul 8, 2026
Merged

Fetch PMP obs4MIPs reference data from ESGF and drop unused ERA datasets#799
lewisjared merged 5 commits into
mainfrom
obs4mips-fetch-and-era-cleanup

Conversation

@lewisjared

@lewisjared lewisjared commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Description

Sources the obs4MIPs reference data that PMP diagnostics need from ESGF, and removes two reference datasets that nothing uses.

Fetch PMP reference data from ESGF

Five datasets requested by the PMP diagnostics are published on ESGF under obs4MIPs but were only reachable via the obs4REF registry. scripts/fetch-esgf.py now has a request for each. There is one request per source_id because the ESGF search intersects source_id and variable_id, so a combined request would ask each source for variables it does not have.

Request source_id Variables Files Size
pmp-enso-gpcp-obs4mips GPCP-Monthly-3-2 pr 41 370 MB
pmp-enso-tropflux-obs4mips TropFlux-1-0 hfls hfss tauu ts 4 90 MB
pmp-enso-ceres-obs4mips CERES-EBAF-4-2 rlds rlus rsds rsus 8 271 MB
pmp-hadisst-obs4mips HadISST-1-1 ts 1 165 MB
pmp-modes-20cr-obs4mips 20CR-V2 psl 1 39 MB

Every request was run end to end. Each file was opened with xarray to confirm the expected variable and a sane time axis (TropFlux 463 months, CERES 283, HadISST 1861, 20CR 1704).

20CR is published on ESGF as 20CR-V2. Fetching it makes the data available, but the five psl modes-of-variability diagnostics still filter on 20CR and will keep resolving from the obs4REF registry. Wiring them up means changing the filter and re-minting those baselines, since reference_source_id is in that diagnostic's facets tuple. Left for a follow-up.

Remove unused ERA-20C and ERA-Interim

No diagnostic requests ERA-20C or ERA-INT under any source type — checked by enumerating the obs4MIPs and PMPClimatology source_id filters across every registered diagnostic in all four providers, not by grep. Neither could migrate to obs4MIPs anyway: ERA-20C is not on ESGF at all, and the ERA-interim record there is a single 2016-era dataset holding only ua and va.

That removes 22 entries from each of the obs4REF and sample-data registries, the now-unreachable ERA-INT entry from the ENSO observation name mapping, and 1496 lines from three catalog regression fixtures, which were regenerated against a freshly fetched sample-data tree.

ERA-5 is untouched — it is a different product and is still required by four ESMValTool diagnostics.

ERA-INT was the only obs4REF sample carrying pressure levels (vertical_levels: 19), but the plev branch of read_vertical_levels has direct synthetic-file coverage in test_netcdf_utils.py, so no parser coverage is lost.

Note for reviewers

fetch_all_files does not prune files that have been dropped from a registry, and fetch_sample_data only wipes its output directory when SAMPLE_DATA_VERSION changes — and that version is a tag of the upstream ref-sample-data repo, so it cannot be bumped here to force a clean re-fetch. Anyone with an existing tests/test-data/sample-data/ will still have the 22 ERA files on disk, and the obs4mips_catalog_local regression will fail for them while CI passes. Run this once:

from climate_ref.testing import fetch_sample_data; fetch_sample_data(force_cleanup=True)

Worth fixing durably at some point, either by having fetch_all_files prune, or by keying the cleanup on a hash of the registry contents rather than the version tag.

Fail the fetch when a request could not be retrieved

Both fetch methods wrapped their search and download in a bare except Exception and returned {}, so run_request printed 0 datasets and main() exited 0. A transient ORNL THREDDS 504 during verification produced exactly that, and a retry downloaded the file fine. In CI or an unattended bulk fetch, a genuine failure was indistinguishable from a dataset that legitimately has no results.

The fetch methods now raise, and run_request classifies the outcome:

  • NoSearchResults — the search matched nothing. Reported, but not a failure.
  • DatasetLoadError, StalledDownload, GlobusTransferError — the datasets exist but could not be retrieved. Retried with an exponential backoff (--max-attempts, --retry-delay), then failed.
  • Anything else — failed immediately, no retry.

main() logs a summary and exits non-zero if any request failed.

Covered by tests/scripts/test_fetch_esgf.py (10 tests, no network). Each test was checked against a deliberately mutated script to confirm it fails when the fix is removed: dropping the non-zero exit breaks 2, making NoSearchResults retryable breaks 1, and removing the retry loop breaks 2.

Checklist

Please confirm that this pull request has done the following:

  • Tests added — tests/scripts/test_fetch_esgf.py (10 tests). Three catalog regression fixtures were also regenerated (1237 passed in climate-ref, 55 in climate-ref-pmp).
  • Documentation added (where applicable) — no doc changes. docs/getting-started/02-download-datasets.md points at the obs4REF registry, whose contents are updated in place.
  • Changelog item added to changelog/799.feature.md, 799.improvement.md and 799.fix.md.

Summary by CodeRabbit

  • New Features

    • The ESGF fetch command now retries transient download/search failures and reports clear success, empty-result, or failure outcomes.
    • Added support for fetching additional reference datasets through the command-line workflow.
  • Bug Fixes

    • The command now exits with a non-zero status when any requested dataset fails.
    • Removed several ECMWF reanalysis entries from reference listings and sample data, so they no longer appear in local catalog results.

Five reference datasets used by the PMP diagnostics are published on ESGF
under obs4MIPs, but were only reachable via the obs4REF registry.
Add a fetch request for each so they can be sourced from ESGF directly.

One request per source_id: the ESGF search intersects source_id and
variable_id, so a combined request would ask each source for variables
it does not have.

20CR is published on ESGF as 20CR-V2.
Fetching it here makes the data available but does not on its own satisfy
the psl modes-of-variability requirements, which still filter on 20CR.
No diagnostic requests ERA-20C or ERA-INT under any source type,
so the 22 files each registry carried for them were never fetched for a solve.
ERA-20C is not published on ESGF at all,
and the ERA-interim record there holds only ua and va,
so neither could migrate to obs4MIPs.

Remove them from the obs4REF and sample-data registries,
drop the now-unreachable ERA-INT entry from the ENSO observation name mapping,
and regenerate the affected catalog regression fixtures.

ERA-5 is untouched and is still required by four ESMValTool diagnostics.

Developers with an existing tests/test-data/sample-data directory need
`fetch_sample_data(force_cleanup=True)`: fetch_all_files does not prune
files dropped from the registry, and SAMPLE_DATA_VERSION is an upstream
tag so it cannot be bumped to force a clean re-fetch.
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@lewisjared, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 21 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d1aed9dd-c2d3-4e3b-9261-2d02d5373b60

📥 Commits

Reviewing files that changed from the base of the PR and between a460a86 and c166238.

📒 Files selected for processing (2)
  • packages/climate-ref-core/tests/unit/test_dataset_registry/test_dataset_registry.py
  • packages/climate-ref-core/tests/unit/test_datasets/dataset_collection_obs4mips_hash.yml
📝 Walkthrough

Walkthrough

ECMWF ERA-20C and ERA-INT entries are removed from reference registries, sample data, and test catalog fixtures. The ENSO remapping is updated, and the ESGF fetch script adds new Obs4MIPs requests, retry handling, outcome reporting, and tests.

Changes

ERA-INT/ERA-20C removal and new obs4MIPs requests

Layer / File(s) Summary
Reference dataset removals
packages/climate-ref-pmp/src/climate_ref_pmp/drivers/enso_driver.py, packages/climate-ref/src/climate_ref/dataset_registry/obs4ref_reference.txt, packages/climate-ref/src/climate_ref/dataset_registry/sample_data.txt, packages/climate-ref/tests/unit/datasets/test_obs4mips/obs4mips_catalog_db.yml, packages/climate-ref/tests/unit/datasets/test_obs4mips/obs4mips_catalog_local.yml, packages/climate-ref/tests/unit/datasets/test_pmp_climatology/pmp_catalog_local.yml, changelog/799.improvement.md
ERA-20C and ERA-INT registry and fixture entries are removed, and the ENSO observation remapping entry for ERA-INT is dropped.
ESGF fetch retry flow
scripts/fetch-esgf.py, changelog/799.feature.md, changelog/799.fix.md
fetch-esgf.py now returns path dictionaries, retries transient ESGF failures, classifies request outcomes, adds new Obs4MIPs request entries, and updates the changelog entries for the fetch behaviour.
Fetch script tests
tests/scripts/test_fetch_esgf.py
New tests cover run_request retry and outcome handling, plus CLI exit codes for success, empty results, failure, mixed runs, and unknown request IDs.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarises the main change: fetching PMP obs4MIPs data from ESGF and removing unused ERA datasets.
Description check ✅ Passed The description follows the template and includes a detailed summary plus the checklist items for tests, docs, and changelog.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch obs4mips-fetch-and-era-cleanup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Both fetch methods wrapped their search and download in a bare `except
Exception` and returned an empty dict, so run_request printed "0 datasets"
and main exited 0.
A transient ORNL THREDDS 504 was therefore indistinguishable from a request
that legitimately matched nothing.

Let the fetch methods raise, and classify the result in run_request.
NoSearchResults means the search matched nothing, which is reported but is
not a failure.
Anything else means the datasets exist but could not be retrieved, which
fails the run.

Retry DatasetLoadError, StalledDownload and GlobusTransferError with an
exponential backoff, since ESGF data nodes return intermittent 5xx responses.
main now exits non-zero if any request failed, and logs a summary of what
succeeded, matched nothing, and failed.
Derive NUM_OBS4REF_FILES from dataset_registry_manager["obs4ref"].registry
instead of hardcoding it, so the count can't drift out of sync with
obs4ref_reference.txt again.
The ERA-20C/ERA-INT removal trimmed the registry from 83 to 61 entries.

Regenerate the obs4MIPs ts dataset hash regression fixture.
ERA-20C and ERA-INT both provided ts, so removing them legitimately
changes the hash of the ts dataset collection in the sample data catalog.
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
core 92.39% <ø> (-0.07%) ⬇️
providers 86.97% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.
see 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lewisjared lewisjared merged commit 817e5f4 into main Jul 8, 2026
28 checks passed
@lewisjared lewisjared deleted the obs4mips-fetch-and-era-cleanup branch July 8, 2026 12:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant