acmadDL 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. acmaddl-0.2.0/.github/workflows/add-to-kanban.yml +14 -0
  2. acmaddl-0.2.0/.github/workflows/publish-testpypi.yml +18 -0
  3. acmaddl-0.2.0/.github/workflows/publish.yml +47 -0
  4. acmaddl-0.2.0/.github/workflows/test.yml +22 -0
  5. acmaddl-0.2.0/.gitignore +51 -0
  6. acmaddl-0.2.0/AGENTS.md +34 -0
  7. acmaddl-0.2.0/LICENSE +21 -0
  8. acmaddl-0.2.0/PKG-INFO +338 -0
  9. acmaddl-0.2.0/README.md +291 -0
  10. acmaddl-0.2.0/docs/specs/2026-07-09-issuance-keyed-forecasts-design.md +91 -0
  11. acmaddl-0.2.0/docs/specs/2026-07-09-opendap-truncation-and-zonal-design.md +77 -0
  12. acmaddl-0.2.0/packaging/accord-rosetta/README.md +19 -0
  13. acmaddl-0.2.0/packaging/accord-rosetta/pyproject.toml +41 -0
  14. acmaddl-0.2.0/pyproject.toml +98 -0
  15. acmaddl-0.2.0/scripts/check_push_creds.sh +67 -0
  16. acmaddl-0.2.0/scripts/demo.ipynb +591 -0
  17. acmaddl-0.2.0/scripts/demo_shapefile_region.py +117 -0
  18. acmaddl-0.2.0/scripts/download_nmme_hindcasts.py +217 -0
  19. acmaddl-0.2.0/scripts/fetch_country_shapefiles.py +68 -0
  20. acmaddl-0.2.0/scripts/validate_against_iri.py +830 -0
  21. acmaddl-0.2.0/skills/acmaddl/README.md +54 -0
  22. acmaddl-0.2.0/skills/acmaddl/SKILL.md +225 -0
  23. acmaddl-0.2.0/skills/acmaddl/examples/basic_fetch.py +67 -0
  24. acmaddl-0.2.0/skills/acmaddl/examples/chirps_gefs_issuance.py +45 -0
  25. acmaddl-0.2.0/skills/acmaddl/examples/multi_model_assemble.py +40 -0
  26. acmaddl-0.2.0/skills/acmaddl/examples/quick_look.py +47 -0
  27. acmaddl-0.2.0/skills/acmaddl/examples/s2s_fetch.py +47 -0
  28. acmaddl-0.2.0/skills/acmaddl/examples/shapefile_region.py +43 -0
  29. acmaddl-0.2.0/skills/acmaddl/examples/zonal_districts.py +51 -0
  30. acmaddl-0.2.0/skills/acmaddl/references/api.md +198 -0
  31. acmaddl-0.2.0/skills/acmaddl/references/data-conventions.md +84 -0
  32. acmaddl-0.2.0/skills/acmaddl/references/plotting.md +102 -0
  33. acmaddl-0.2.0/skills/acmaddl/references/products.md +130 -0
  34. acmaddl-0.2.0/skills/acmaddl/references/troubleshooting.md +82 -0
  35. acmaddl-0.2.0/src/acmaddl/__init__.py +61 -0
  36. acmaddl-0.2.0/src/acmaddl/_paths.py +51 -0
  37. acmaddl-0.2.0/src/acmaddl/adapters/__init__.py +34 -0
  38. acmaddl-0.2.0/src/acmaddl/adapters/_issuance.py +124 -0
  39. acmaddl-0.2.0/src/acmaddl/adapters/_robust.py +152 -0
  40. acmaddl-0.2.0/src/acmaddl/adapters/base.py +60 -0
  41. acmaddl-0.2.0/src/acmaddl/adapters/ccsr.py +239 -0
  42. acmaddl-0.2.0/src/acmaddl/adapters/cds.py +270 -0
  43. acmaddl-0.2.0/src/acmaddl/adapters/cpc_binary.py +102 -0
  44. acmaddl-0.2.0/src/acmaddl/adapters/http.py +465 -0
  45. acmaddl-0.2.0/src/acmaddl/adapters/icechunk.py +172 -0
  46. acmaddl-0.2.0/src/acmaddl/adapters/iridl.py +141 -0
  47. acmaddl-0.2.0/src/acmaddl/adapters/mars.py +124 -0
  48. acmaddl-0.2.0/src/acmaddl/adapters/ncei.py +209 -0
  49. acmaddl-0.2.0/src/acmaddl/adapters/opendap.py +272 -0
  50. acmaddl-0.2.0/src/acmaddl/adapters/s3.py +159 -0
  51. acmaddl-0.2.0/src/acmaddl/adapters/sheerwater.py +175 -0
  52. acmaddl-0.2.0/src/acmaddl/assemble.py +93 -0
  53. acmaddl-0.2.0/src/acmaddl/catalog.py +90 -0
  54. acmaddl-0.2.0/src/acmaddl/catalog.yaml +1613 -0
  55. acmaddl-0.2.0/src/acmaddl/cli.py +36 -0
  56. acmaddl-0.2.0/src/acmaddl/cpc_nmme.py +123 -0
  57. acmaddl-0.2.0/src/acmaddl/errors.py +33 -0
  58. acmaddl-0.2.0/src/acmaddl/fetch.py +573 -0
  59. acmaddl-0.2.0/src/acmaddl/health.py +88 -0
  60. acmaddl-0.2.0/src/acmaddl/normalize.py +438 -0
  61. acmaddl-0.2.0/src/acmaddl/nuthatch.toml +22 -0
  62. acmaddl-0.2.0/src/acmaddl/region.py +111 -0
  63. acmaddl-0.2.0/src/acmaddl/storage.py +79 -0
  64. acmaddl-0.2.0/src/acmaddl/validate.py +423 -0
  65. acmaddl-0.2.0/src/acmaddl/zonal.py +265 -0
  66. acmaddl-0.2.0/src/rosetta/__init__.py +65 -0
  67. acmaddl-0.2.0/tests/__init__.py +0 -0
  68. acmaddl-0.2.0/tests/conftest.py +127 -0
  69. acmaddl-0.2.0/tests/test_acmaddl.py +1284 -0
  70. acmaddl-0.2.0/tests/test_adapter_registry.py +25 -0
  71. acmaddl-0.2.0/tests/test_assemble.py +63 -0
  72. acmaddl-0.2.0/tests/test_base_adapter.py +38 -0
  73. acmaddl-0.2.0/tests/test_cache.py +486 -0
  74. acmaddl-0.2.0/tests/test_catalog_alias.py +56 -0
  75. acmaddl-0.2.0/tests/test_catalog_integrity.py +232 -0
  76. acmaddl-0.2.0/tests/test_ccsr_adapter.py +333 -0
  77. acmaddl-0.2.0/tests/test_cfsv2_members.py +164 -0
  78. acmaddl-0.2.0/tests/test_chirps_catalog.py +158 -0
  79. acmaddl-0.2.0/tests/test_cmap_tamsat_catalog.py +31 -0
  80. acmaddl-0.2.0/tests/test_cmcc_catalog.py +130 -0
  81. acmaddl-0.2.0/tests/test_cpc_nmme_cache.py +47 -0
  82. acmaddl-0.2.0/tests/test_degenerate_response.py +92 -0
  83. acmaddl-0.2.0/tests/test_era5_land_catalog.py +120 -0
  84. acmaddl-0.2.0/tests/test_ersst_catalog.py +225 -0
  85. acmaddl-0.2.0/tests/test_fetch_enrichment.py +109 -0
  86. acmaddl-0.2.0/tests/test_integration.py +1017 -0
  87. acmaddl-0.2.0/tests/test_issuance.py +523 -0
  88. acmaddl-0.2.0/tests/test_lon_convention.py +101 -0
  89. acmaddl-0.2.0/tests/test_lon_convention_interp.py +26 -0
  90. acmaddl-0.2.0/tests/test_mars_adapter.py +118 -0
  91. acmaddl-0.2.0/tests/test_normalize_name_skew.py +39 -0
  92. acmaddl-0.2.0/tests/test_obs_predictor.py +64 -0
  93. acmaddl-0.2.0/tests/test_opendap_adapter.py +250 -0
  94. acmaddl-0.2.0/tests/test_opendap_retry.py +163 -0
  95. acmaddl-0.2.0/tests/test_region.py +406 -0
  96. acmaddl-0.2.0/tests/test_rename_compat.py +129 -0
  97. acmaddl-0.2.0/tests/test_s2s_adapter.py +331 -0
  98. acmaddl-0.2.0/tests/test_seasonal_precip_units.py +66 -0
  99. acmaddl-0.2.0/tests/test_seasons.py +44 -0
  100. acmaddl-0.2.0/tests/test_sheerwater.py +177 -0
  101. acmaddl-0.2.0/tests/test_sheerwater_catalog.py +85 -0
  102. acmaddl-0.2.0/tests/test_sheerwater_integration.py +54 -0
  103. acmaddl-0.2.0/tests/test_spec.md +252 -0
  104. acmaddl-0.2.0/tests/test_validate.py +393 -0
  105. acmaddl-0.2.0/tests/test_variable_not_supported.py +275 -0
  106. acmaddl-0.2.0/tests/test_zonal.py +356 -0
  107. acmaddl-0.2.0/uv.lock +5167 -0
@@ -0,0 +1,14 @@
1
+ name: Add issues to Kanban
2
+
3
+ on:
4
+ issues:
5
+ types: [opened, reopened]
6
+
7
+ jobs:
8
+ add-to-kanban:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/add-to-project@v1.0.2
12
+ with:
13
+ project-url: https://github.com/orgs/accord-research/projects/2
14
+ github-token: ${{ secrets.KANBAN_TOKEN }}
@@ -0,0 +1,18 @@
1
+ name: publish-testpypi
2
+ on:
3
+ workflow_dispatch:
4
+
5
+ permissions:
6
+ id-token: write
7
+
8
+ jobs:
9
+ build-publish-test:
10
+ runs-on: ubuntu-latest
11
+ environment: testpypi
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - uses: astral-sh/setup-uv@v6
15
+ - run: uv build
16
+ - uses: pypa/gh-action-pypi-publish@release/v1
17
+ with:
18
+ repository-url: https://test.pypi.org/legacy/
@@ -0,0 +1,47 @@
1
+ name: publish
2
+
3
+ # PyPI releases are cut MANUALLY, never on a push to main.
4
+ #
5
+ # To release:
6
+ # 1. Bump `version` in pyproject.toml (in a commit to main).
7
+ # 2. Actions tab -> "publish" -> "Run workflow".
8
+ #
9
+ # The job publishes only if that version isn't already on PyPI, so re-running
10
+ # without a version bump is a safe no-op (it fails fast with a clear message).
11
+ # pyproject.toml's `version` is the single source of truth. Publishing uses
12
+ # PyPI Trusted Publishing (no tokens).
13
+
14
+ on:
15
+ workflow_dispatch:
16
+
17
+ jobs:
18
+ test:
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: astral-sh/setup-uv@v6
23
+ with:
24
+ python-version: "3.12"
25
+ - run: uv sync --extra dev
26
+ - run: uv run pytest -m "not network and not integration and not cds" -q
27
+
28
+ publish:
29
+ needs: test
30
+ runs-on: ubuntu-latest
31
+ environment: pypi
32
+ permissions:
33
+ id-token: write
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: astral-sh/setup-uv@v6
37
+ - name: Refuse to republish an existing version
38
+ run: |
39
+ VERSION=$(grep -E '^version = ' pyproject.toml | head -1 | sed -E 's/version = "(.*)"/\1/')
40
+ echo "pyproject.toml version: $VERSION"
41
+ if curl -sf "https://pypi.org/pypi/acmadDL/$VERSION/json" > /dev/null; then
42
+ echo "::error::acmadDL $VERSION is already on PyPI — bump 'version' in pyproject.toml first."
43
+ exit 1
44
+ fi
45
+ echo "$VERSION is new on PyPI; proceeding to publish."
46
+ - run: uv build
47
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,22 @@
1
+ name: Unit tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+
13
+ - name: Install uv
14
+ uses: astral-sh/setup-uv@v4
15
+ with:
16
+ python-version: "3.12"
17
+
18
+ - name: Install dependencies
19
+ run: uv sync --extra dev
20
+
21
+ - name: Run unit tests
22
+ run: uv run pytest tests/test_acmaddl.py tests/test_sheerwater.py tests/test_cache.py tests/test_validate.py -q -m "not network and not integration and not cds"
@@ -0,0 +1,51 @@
1
+ # Python bytecode and caches
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.so
5
+
6
+ # Local virtual environments
7
+ .venv/
8
+ venv/
9
+
10
+ # Test and tooling caches
11
+ .pytest_cache/
12
+ .mypy_cache/
13
+ .ruff_cache/
14
+ .coverage
15
+ .coverage.*
16
+ htmlcov/
17
+
18
+ # Notebook and editor noise
19
+ .ipynb_checkpoints/
20
+ .DS_Store
21
+
22
+ # Local data/artifacts
23
+ data/
24
+ cache/
25
+ tmp/
26
+ # generated demo PNGs + regenerated catalog HTML/text
27
+ output/
28
+ *.nc
29
+ *.zarr/
30
+ *.grib
31
+ *.grb
32
+ *.tif
33
+ *.tiff
34
+
35
+ # Local secrets/config (do not commit credentials)
36
+ .env
37
+ .env.*
38
+ *.pem
39
+ *.key
40
+ *credentials*.json
41
+ .cdsapirc
42
+ .ecmwfapirc
43
+ .netrc
44
+ .dodsrc
45
+
46
+ .claude_env
47
+
48
+ # Git worktrees
49
+ .worktrees/
50
+
51
+ docs/superpowers
@@ -0,0 +1,34 @@
1
+ # Agent instructions for acmaddl
2
+
3
+ Instructions for any AI coding agent (Claude Code, Codex, OpenCode, etc.) working in this repository.
4
+
5
+ ## Before you start
6
+
7
+ Read [`skills/acmaddl/SKILL.md`](skills/acmaddl/SKILL.md) before writing or modifying code that uses acmaddl. It is the authoritative quick reference for the public API, product catalog, data conventions, credentials, and caching behavior, with deeper detail in `skills/acmaddl/references/`. If your harness supports Agent Skills natively, load the skill instead of re-deriving the API from source.
8
+
9
+ ## Keep the docs and skill in sync — this is a hard requirement
10
+
11
+ The skill is a snapshot of the source. Any change that alters observable behavior MUST update the matching documentation **in the same PR**:
12
+
13
+ | If you change... | You must update... |
14
+ |---|---|
15
+ | `fetch()` / `assemble()` signatures, defaults, or behavior | `skills/acmaddl/SKILL.md` + `skills/acmaddl/references/api.md` |
16
+ | `fetch()` params `months`, `degenerate_attempts`, `init` sequence / issuance handling | `skills/acmaddl/references/api.md` (params) + `data-conventions.md` (issuance `init_time`/`lead_time`/`valid_time`) + `SKILL.md` |
17
+ | `zonal.py` (`acmaddl.zonal` signature, stats, weights, gotchas) | `skills/acmaddl/references/api.md` + `SKILL.md` (+ `examples/zonal_districts.py`) |
18
+ | `assemble.py` `obs_predictor` (obs-as-predictor signature/behavior) | `skills/acmaddl/references/api.md` + `SKILL.md` |
19
+ | `catalog.yaml` (add/remove/deprecate products, variables, adapters) | `skills/acmaddl/references/products.md` (+ `SKILL.md` product families if a new family) |
20
+ | Issuance-keyed catalog entries (CHIRPS-GEFS) or OPeNDAP obs entries (ERSST/CMAP, `max_request_years`) | `skills/acmaddl/references/products.md` + `data-conventions.md` (issuance layout) + `troubleshooting.md` (truncation guard) + `examples/chirps_gefs_issuance.py` |
21
+ | `normalize.py` (renames, unit conversions, pipeline order, `select_lon`/`sanitize_for_netcdf`) | `skills/acmaddl/references/data-conventions.md` (+ `plotting.md` if dims/units/orientation guarantees change) |
22
+ | Degenerate-response guard (`_robust.reject_if_degenerate`, `DegenerateResponseError`, opt-in vs always-on) | `skills/acmaddl/references/troubleshooting.md` + `api.md` (`degenerate_attempts`) |
23
+ | Caching (`_CACHE_VERSION`, cache keys, env vars, nuthatch config) | `skills/acmaddl/references/troubleshooting.md` |
24
+ | Credentials/adapters (new source, auth flow, rate limits) | `skills/acmaddl/references/products.md` + `troubleshooting.md` |
25
+ | Anything user-facing | `README.md` if it covers the topic |
26
+
27
+ Also update `skills/acmaddl/examples/` if a change breaks or obsoletes an example. If you are unsure whether a change is documented, grep `skills/` and `README.md` for the function, product id, or parameter you touched — stale docs are treated as bugs.
28
+
29
+ ## Repo conventions
30
+
31
+ - Package layout: `src/acmaddl/` (import name `acmaddl`, distribution `acmadDL`). Python ≥ 3.12, `uv` for dependency management.
32
+ - Tests: `pytest` for the unit suite; markers `integration`, `cds`, `network` gate live-network tests. New behavior needs tests.
33
+ - Caching lives only in `fetch._fetch_raw_cached` — never add `@cache` decorators inside adapters. Bump `_CACHE_VERSION` when adapter logic or normalization changes output.
34
+ - The catalog is declarative: prefer adding/adjusting `catalog.yaml` entries over special-casing code paths.
acmaddl-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 accord-research
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
acmaddl-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,338 @@
1
+ Metadata-Version: 2.5
2
+ Name: acmadDL
3
+ Version: 0.2.0
4
+ Summary: Canonical data acquisition for seasonal climate forecasting
5
+ Project-URL: Homepage, https://github.com/ACMAD-Niamey/acmadDL
6
+ Project-URL: Repository, https://github.com/ACMAD-Niamey/acmadDL
7
+ Author: accord-research
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: cds,chirps,climate,data-acquisition,forecasting,xarray
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
16
+ Requires-Python: >=3.12
17
+ Requires-Dist: cdsapi>=0.7.7
18
+ Requires-Dist: cfgrib>=0.9.15.1
19
+ Requires-Dist: earthaccess>=0.18
20
+ Requires-Dist: eccodeslib>=2.46.2.19
21
+ Requires-Dist: ecmwf-api-client>=1.6
22
+ Requires-Dist: fsspec>=2025.2
23
+ Requires-Dist: netcdf4
24
+ Requires-Dist: numpy
25
+ Requires-Dist: nuthatch>=0.4.9
26
+ Requires-Dist: pyyaml
27
+ Requires-Dist: rioxarray
28
+ Requires-Dist: s3fs>=2025.2
29
+ Requires-Dist: scipy
30
+ Requires-Dist: sheerwater>=0.2.5
31
+ Requires-Dist: tqdm
32
+ Requires-Dist: xarray
33
+ Provides-Extra: demo
34
+ Requires-Dist: cartopy; extra == 'demo'
35
+ Requires-Dist: matplotlib; extra == 'demo'
36
+ Requires-Dist: rasterio; extra == 'demo'
37
+ Provides-Extra: dev
38
+ Requires-Dist: pytest; extra == 'dev'
39
+ Provides-Extra: geo
40
+ Requires-Dist: geopandas; extra == 'geo'
41
+ Requires-Dist: rasterio; extra == 'geo'
42
+ Provides-Extra: icechunk
43
+ Requires-Dist: icechunk>=2; extra == 'icechunk'
44
+ Provides-Extra: s3
45
+ Requires-Dist: boto3; extra == 's3'
46
+ Description-Content-Type: text/markdown
47
+
48
+ # acmadDL
49
+
50
+ An API for fetching climate, environmental, and contextual datasets
51
+
52
+ acmadDL is ACCORD's data adapter layer. A single `fetch()` call retrieves data from many different providers (Copernicus CDS, the ECMWF Data Store, OPeNDAP, HTTP, S3, and others) and returns it as a normalized xarray dataset with canonical names, units, and coordinates. Data stays at the source; acmadDL does not host a central copy of anything. Adding a new dataset or provider is a catalog and adapter change, not a rewrite of your workflow.
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pip install acmadDL
58
+ ```
59
+
60
+ The distribution is published as `acmadDL`; the import name is `acmaddl`. acmadDL requires Python 3.12 or newer.
61
+
62
+ Most CDS-based products (`c3s/*`, `obs/era5`) need CDS or ECMWF Data Store credentials; see [CDS / ECDS setup](#cds--ecds-setup).
63
+
64
+ ## Core API
65
+
66
+ ```python
67
+ import acmaddl
68
+
69
+ ds = acmaddl.fetch(
70
+ product="nmme/cfsv2",
71
+ variable="precip",
72
+ init="2025-02",
73
+ target="MAM",
74
+ region=[-12, 6, 28, 42],
75
+ hindcast=(1993, 2016),
76
+ verbose=True,
77
+ progress=True,
78
+ )
79
+ ```
80
+
81
+ You pass a canonical `product` and `variable` plus optional temporal and spatial selectors. You get back a CF-aligned xarray dataset, ready for analysis or modeling. Results are returned in memory by default, or written to a local path or S3.
82
+
83
+ Verify an install and list what is available:
84
+
85
+ ```python
86
+ import acmaddl
87
+ acmaddl.check_all_products() # config checks for every product
88
+ acmaddl.catalog.list_products() # list product ids
89
+ ```
90
+
91
+ ### Normalized coordinates
92
+
93
+ Every dataset comes back with canonical coordinate names regardless of the upstream source:
94
+
95
+ | Coordinate | Applies to | Description |
96
+ |------------|-----------|-------------|
97
+ | `lat`, `lon` | all products | Spatial axes (latitude ascending) |
98
+ | `time` | observations / reanalysis | Monthly time axis (`datetime64`) |
99
+ | `init_time` | forecasts | Initialization time (`datetime64`) |
100
+ | `lead_time` | forecasts | Lead time (numeric, units vary by source) |
101
+ | `member` | forecasts | Ensemble member index |
102
+
103
+ Numeric time encodings (for example "months since 1960-01-01") are decoded to `datetime64` automatically.
104
+
105
+ ### Region input
106
+
107
+ `region` accepts three forms:
108
+
109
+ | Form | Example | Behaviour |
110
+ |------|---------|-----------|
111
+ | bbox | `region=[-12, 6, 28, 42]` | `[lat_s, lat_n, lon_w, lon_e]` |
112
+ | shapefile | `region="kenya.shp"` | bounding box slices upstream; result masked to the polygon |
113
+ | geometry | `region=gdf.geometry` | shapely geometry or geopandas `GeoSeries`, same masking |
114
+
115
+ Shapefile and geometry inputs need the `geo` extra (`pip install 'acmadDL[geo]'`). The polygon is reprojected to EPSG:4326 and dissolved, so multi-feature files (for example an archipelago) clip correctly. Cells outside the polygon come back as `NaN`.
116
+
117
+ ```python
118
+ import acmaddl
119
+
120
+ # Clip to a country boundary; values outside Kenya become NaN.
121
+ ds = acmaddl.fetch(
122
+ product="nmme/cfsv2",
123
+ variable="precip",
124
+ init="2025-02",
125
+ target="MAM",
126
+ region="kenya.shp",
127
+ hindcast=(1993, 2016),
128
+ )
129
+ ```
130
+
131
+ By default a grid cell is included only if its centre lies inside the region (`boundary="center"`, the xarray/CDO/rasterio convention, and unbiased for area means). Pass `boundary="cover"` to keep every cell the region touches (matches rasterio's `all_touched=True`), which is useful for display or coarse grids where center-based selection can drop a country's thin tips. This applies to bbox and shapefile/geometry inputs alike.
132
+
133
+ ```python
134
+ ds = acmaddl.fetch(..., region="kenya.shp", boundary="cover")
135
+ ```
136
+
137
+ Note: polygons crossing the plus/minus 180 degree antimeridian are not yet handled (the derived bounding box spans the full longitude range). Split such geometries at the antimeridian before passing them in.
138
+
139
+ ### Multiple predictor domains
140
+
141
+ Seasonal forecasting workflows often use two predictor domains from the same model, for example a large sea-surface-temperature domain and a smaller regional precipitation domain. Make one `fetch()` call per domain; there is no combined helper, because the domains differ in extent and variable.
142
+
143
+ ```python
144
+ import acmaddl
145
+
146
+ # SST predictor: large tropical domain
147
+ sst_predictor = acmaddl.fetch(
148
+ product="nmme/geoss2s", variable="sst",
149
+ init="2025-02", target="MAM",
150
+ region=[-20, 20, 30, 180], hindcast=(1993, 2016),
151
+ )
152
+
153
+ # Precipitation predictor: regional domain
154
+ prcp_predictor = acmaddl.fetch(
155
+ product="nmme/geoss2s", variable="precip",
156
+ init="2025-02", target="MAM",
157
+ region=[-20, 20, 10, 75], hindcast=(1993, 2016),
158
+ )
159
+
160
+ # Predictand: observations
161
+ predictand = acmaddl.fetch(
162
+ product="obs/chirps-v2-monthly", variable="precip",
163
+ target="MAM", region=[-12, 15, 22, 52], hindcast=(1993, 2016),
164
+ )
165
+ ```
166
+
167
+ ### Health checks
168
+
169
+ ```python
170
+ import acmaddl
171
+
172
+ acmaddl.check_product("nmme/cfsv2") # one product, config only
173
+ acmaddl.check_all_products() # all products, config only
174
+ acmaddl.check_all_products(probe_remote=True) # also probe the live source
175
+ ```
176
+
177
+ Each result includes `product`, `adapter`, `healthy`, `kind`, `message`, and `checked_at`.
178
+
179
+ ## Available products
180
+
181
+ How to read the tables. Hindcast is each model's fixed reforecast period, not a fetch cap: real-time forecasts run past it to the present. Forecast is the live-verified real-time availability, shown as `year–present` (ongoing) or `start–end (retired)` when the pinned system version was superseded (hindcasts still fetch, but no new forecasts issue). Members (F/H) are the real-time-forecast and reforecast ensemble sizes, which differ. `†` marks a deprecated access route. Full field conventions are documented at the top of [`src/acmaddl/catalog.yaml`](src/acmaddl/catalog.yaml).
182
+
183
+ Several C3S entries pin a system version whose real-time stream has ended (a 2026 forecast init returns no data); they still fetch hindcasts. CMCC's live stream has moved to `c3s/cmcc-sps4`. JMA and UKMO do not yet have an active-forecast entry on their current systems (JMA CPS4, UKMO 605).
184
+
185
+ Collapsed targeted seasonal precipitation (`year_index=True`, including
186
+ `assemble()`) is delivered in `mm` across NMME, C3S/CDS, and IRI products.
187
+ Lead-resolved fetches retain honest per-step units (usually `mm/day`). Monthly
188
+ rates are calendar-weighted and daily amounts are summed. Native ensemble spread is preserved;
189
+ `nmme/cfsv2` returns the 24 populated members and removes four structurally
190
+ empty trailing slots exposed by the upstream endpoint.
191
+
192
+ ### Seasonal forecast, NMME
193
+
194
+ | Product | Model / system | Host | Adapter | Variables | Cadence | Members (F/H) | Hindcast | Forecast |
195
+ |---|---|---|---|---|---|---|---|---|
196
+ | `nmme/cansipsic4` | ECCC CanSIPS-IC4 | Columbia CCSR | `ccsr` | precip, temp, sst | monthly | 40 / 40 | 1990–2024 | 2024–present |
197
+ | `nmme/ccsm4` | NCAR/COLA CCSM4 | Columbia CCSR | `ccsr` | precip, temp, sst | monthly | 10 / 10 | 1982–2026 | 2014–present |
198
+ | `nmme/cesm1` | NCAR CESM1 | Columbia CCSR | `ccsr` | precip, temp, sst | monthly | 10 / 10 | 1982–2026 | 2017–present |
199
+ | `nmme/geoss2s` | NASA GEOS-S2S | Columbia CCSR | `ccsr` | precip, temp, sst | monthly | 10 / 4 | 1981–2017 | 2019–present |
200
+ | `nmme/spear` | GFDL SPEAR | Columbia CCSR | `ccsr` | precip, temp, sst | monthly | 30 / 15 | 1991–2020 | 2021–present |
201
+ | `nmme/spearb` | GFDL SPEARb | Columbia CCSR | `ccsr` | sst | monthly | 30 / 15 | 1991–2020 | 2021–present |
202
+ | `nmme/cfsv2` † | NCEP CFSv2 | IRI Data Library | `opendap` | precip, temp, sst | monthly | 28 / 28 | 1982–2010 | 2011–present |
203
+
204
+ ### Seasonal forecast, C3S
205
+
206
+ | Product | Model / system | Host | Adapter | Variables | Cadence | Members (F/H) | Hindcast | Forecast |
207
+ |---|---|---|---|---|---|---|---|---|
208
+ | `c3s/cmcc` | CMCC SPSv3.5 (sys 35) | Copernicus CDS | `cds` | precip, temp, sst | monthly | 50 / 40 | 1993–2016 | 2020–2025 (retired) |
209
+ | `c3s/cmcc-daily` | CMCC SPSv3.5 (sys 35) | Copernicus CDS | `cds` | precip, temp, sst | daily | 50 / 40 | 1993–2016 | 2020–2025 (retired) |
210
+ | `c3s/cmcc-sps4` | CMCC SPS4 (sys 4) | Copernicus CDS | `cds` | precip, temp, sst | monthly | 50 / 30 | 1993–2024 | 2025–present |
211
+ | `c3s/cmcc-sps4-daily` | CMCC SPS4 (sys 4) | Copernicus CDS | `cds` | precip, temp, sst | daily | 50 / 30 | 1993–2024 | 2025–present |
212
+ | `c3s/dwd` | DWD GCFS2.2 (sys 22) | Copernicus CDS | `cds` | precip, temp, sst | monthly | 50 / 30 | 1993–2023 | 2023–present |
213
+ | `c3s/dwd-daily` | DWD GCFS2.2 (sys 22) | Copernicus CDS | `cds` | precip, temp, sst | daily | 50 / 30 | 1993–2023 | 2023–present |
214
+ | `c3s/dwd-gcfs21` | DWD GCFS2.1 (sys 21) | Copernicus CDS | `cds` | precip, temp, sst | monthly | 50 / 30 | 1993–2019 | 2020–2025 (retired) |
215
+ | `c3s/eccc-cansips` | ECCC GEM5-NEMO (sys 3) | Copernicus CDS | `cds` | precip, temp, sst | monthly | 10 / 10 | 1990–2020 | 2021–2024 (retired) |
216
+ | `c3s/eccc-cansipsv3` | ECCC CanESM5.1 (sys 4) | Copernicus CDS | `cds` | precip, temp, sst | monthly | 20 / 20 | 1980–2023 | 2024–present |
217
+ | `c3s/eccc-daily` | ECCC CanESM5.1 (sys 4) | Copernicus CDS | `cds` | precip, temp, sst | daily | 20 / 20 | 1980–2023 | 2024–present |
218
+ | `c3s/ecmwf` | ECMWF SEAS5 (sys 51) | Copernicus CDS | `cds` | precip, temp, sst | monthly | 51 / 25 | 1981–2016 | 2017–present |
219
+ | `c3s/ecmwf-monthly` | ECMWF SEAS5 (sys 51) | Copernicus CDS | `cds` | precip, temp, sst | monthly | 51 / 25 | 1981–2016 | 2017–present |
220
+ | `c3s/jma` | JMA CPS3 (sys 3) | Copernicus CDS | `cds` | precip, temp, sst | monthly | 155 / 10 | 1991–2020 | 2022–2026 (retired) |
221
+ | `c3s/jma-cps2` | JMA CPS2 (sys 2) | Copernicus CDS | `cds` | precip, temp, sst | monthly | 13 / 10 | 1981–2016 | 2015–2022 (retired) |
222
+ | `c3s/meteofrance` | Météo-France Sys 9 | Copernicus CDS | `cds` | precip, temp, sst | monthly | 51 / 31 | 1993–2024 | 2025–present |
223
+ | `c3s/meteofrance-daily` | Météo-France Sys 9 | Copernicus CDS | `cds` | precip, temp, sst | daily | 51 / 31 | 1993–2024 | 2025–present |
224
+ | `c3s/ukmo` | UKMO GloSea6 GC3.2 (sys 604) | Copernicus CDS | `cds` | precip, temp, sst | monthly | 62 / 28 | 1993–2016 | 2025–2026 (retired) |
225
+ | `c3s/ukmo-daily` | UKMO GloSea6 GC3.2 (sys 604) | Copernicus CDS | `cds` | precip, temp, sst | daily | 7 / 7 | 1993–2016 | 2025–2026 (retired) |
226
+ | `c3s/ecmwf-seas51c` † | - | IRI Data Library | `iridl` | precip, sst | monthly | 51 / 25 | 1993–2016 | - |
227
+
228
+ ### Sub-seasonal forecast
229
+
230
+ | Product | Model / system | Host | Adapter | Variables | Cadence | Members (F/H) | Hindcast | Forecast |
231
+ |---|---|---|---|---|---|---|---|---|
232
+ | `c3s/ecmwf-s2s` | ECMWF S2S | ECMWF Data Store | `cds` | precip, sst | twice weekly | 50 / 11 | - | on-the-fly |
233
+
234
+ `c3s/ecmwf-s2s` is date-keyed: call it with `init="YYYY-MM-DD"` (the issuance date). Its reforecasts are generated on the fly, so there is no fixed hindcast window. It uses the ECMWF Data Store (`ecds.ecmwf.int`), a separate service from the Copernicus CDS; see [ECMWF Data Store (ECDS) setup](#ecmwf-data-store-ecds-setup).
235
+
236
+ ### Reanalysis
237
+
238
+ | Product | Model / system | Host | Adapter | Variables | Cadence | Members (F/H) | Hindcast | Forecast |
239
+ |---|---|---|---|---|---|---|---|---|
240
+ | `obs/era5` | - | Copernicus CDS | `cds` | temp, precip, sst | monthly | - | 1940–2025 | - |
241
+ | `obs/era5-land-monthly` | - | Copernicus CDS | `cds` | precip, temp | monthly | - | 1950–2025 | - |
242
+
243
+ ### Observation
244
+
245
+ | Product | Model / system | Host | Adapter | Variables | Cadence | Members (F/H) | Hindcast | Forecast |
246
+ |---|---|---|---|---|---|---|---|---|
247
+ | `obs/chirps-live-rhiza` | - | Rhiza/Sheerwater | `sheerwater` | precip | daily | - | - | - |
248
+ | `obs/chirps-v2-annual` | - | UCSB CHC | `http` | precip | annual | - | 1981–2024 | - |
249
+ | `obs/chirps-v2-daily` | - | UCSB CHC | `http` | precip | daily | - | 1981–2025 | - |
250
+ | `obs/chirps-v2-dekad` | - | UCSB CHC | `http` | precip | dekad | - | 1981–2025 | - |
251
+ | `obs/chirps-v2-dekadal-rhiza` | - | Rhiza/Sheerwater | `sheerwater` | precip | dekadal-rolling | - | 1997–2024 | - |
252
+ | `obs/chirps-v2-monthly` | - | UCSB CHC | `http` | precip | monthly | - | 1981–2025 | - |
253
+ | `obs/chirps-v2-pentad` | - | UCSB CHC | `http` | precip | pentad | - | 1981–2025 | - |
254
+ | `obs/chirps-v3-annual` | - | UCSB CHC | `http` | precip | annual | - | 1981–2025 | - |
255
+ | `obs/chirps-v3-daily` | - | UCSB CHC | `http` | precip | daily | - | 1998–2025 | - |
256
+ | `obs/chirps-v3-daily-rhiza` | - | Rhiza/Sheerwater | `sheerwater` | precip | daily | - | 2000–2024 | - |
257
+ | `obs/chirps-v3-dekad` | - | UCSB CHC | `http` | precip | dekad | - | 1981–2025 | - |
258
+ | `obs/chirps-v3-monthly` | - | UCSB CHC | `http` | precip | monthly | - | 1981–2025 | - |
259
+ | `obs/chirps-v3-pentad` | - | UCSB CHC | `http` | precip | pentad | - | 1981–2025 | - |
260
+ | `obs/ghcn` | - | Rhiza/Sheerwater | `sheerwater` | precip | monthly | - | 1981–2024 | - |
261
+ | `obs/imerg` | - | Rhiza/Sheerwater | `sheerwater` | precip | monthly | - | 2000–2024 | - |
262
+
263
+ For `nmme/cfsv2 †`, the model is still an active NMME member (live on CPC FTP); only its IRI Data Library access route is deprecated (IRIDL shutdown around October 2026), with no successor yet.
264
+
265
+ Output is always NetCDF (extensible to Zarr and GeoTIFF).
266
+
267
+ ## CDS / ECDS setup
268
+
269
+ acmadDL's `cds` adapter talks to two distinct ECMWF endpoints, which are separate services with separate accounts, API keys, and licence-acceptance flows:
270
+
271
+ - Copernicus Climate Data Store (`cds.climate.copernicus.eu`): most `c3s/*` products and `obs/era5`.
272
+ - ECMWF Data Store (`ecds.ecmwf.int`): the newer service, currently used by `c3s/ecmwf-s2s`.
273
+
274
+ Each product's catalog entry selects its endpoint. You need credentials and accepted licences for each endpoint you fetch from.
275
+
276
+ ### Copernicus CDS setup
277
+
278
+ For Copernicus CDS products (all `c3s/*` except `c3s/ecmwf-s2s`, plus `obs/era5`):
279
+
280
+ 1. Create a CDS account and API key at <https://cds.climate.copernicus.eu>.
281
+ 2. Add credentials to `~/.cdsapirc`:
282
+
283
+ ```bash
284
+ cat > ~/.cdsapirc << 'EOF'
285
+ url: https://cds.climate.copernicus.eu/api
286
+ key: <YOUR-CDS-API-KEY>
287
+ EOF
288
+ ```
289
+
290
+ 3. Accept the required dataset licences in the CDS web UI before your first download. Each dataset page has a "Terms of use" section you tick once per account. If `acmaddl.fetch()` fails with a 403, the error names the dataset(s) still missing acceptance.
291
+
292
+ ### ECMWF Data Store (ECDS) setup
293
+
294
+ Required for `c3s/ecmwf-s2s` (and any future product whose catalog entry uses `cds_url: "https://ecds.ecmwf.int/api"`). ECDS is a separate service; you cannot reuse Copernicus CDS credentials.
295
+
296
+ 1. Create an ECMWF account at <https://www.ecmwf.int/>, log in to <https://ecds.ecmwf.int/>, and copy your API key from your profile settings.
297
+ 2. Point `cdsapi` at the ECDS endpoint. If you only use ECDS, the simplest setup is to replace `~/.cdsapirc`:
298
+
299
+ ```bash
300
+ cat > ~/.cdsapirc << 'EOF'
301
+ url: https://ecds.ecmwf.int/api
302
+ key: <YOUR-ECDS-API-KEY>
303
+ EOF
304
+ ```
305
+
306
+ If you also use the Copernicus CDS, keep one endpoint in `~/.cdsapirc` and pass the other via the `CDSAPI_URL` / `CDSAPI_KEY` environment variables per session.
307
+ 3. Accept both layers of ECDS licences (ECDS returns 403 until both are accepted):
308
+ - The site-wide Terms of Use, accepted once per account from the ECDS profile settings.
309
+ - The dataset licence, ticked on the dataset's own page. For S2S: <https://ecds.ecmwf.int/datasets/s2s-forecasts?tab=download#manage-licences>. Other datasets follow the same `https://ecds.ecmwf.int/datasets/<dataset-id>?tab=download#manage-licences` pattern.
310
+
311
+ As with the Copernicus CDS, a skipped licence produces a 403 that names exactly which licence is missing.
312
+ 4. Test the connection:
313
+
314
+ ```bash
315
+ uv run pytest tests/test_integration.py::test_fetch_c3s_ecmwf_s2s_precip -v
316
+ ```
317
+
318
+ ## Cache configuration
319
+
320
+ acmadDL caches adapter downloads locally using [Nuthatch](https://github.com/rhiza-research/nuthatch). Cache files live in `~/.nuthatch/acmaddl` by default (configured in `pyproject.toml`).
321
+
322
+ ```bash
323
+ acmaddl cache list # inspect the cache
324
+ acmaddl cache clear # clear everything
325
+ acmaddl cache clear --product nmme/cfsv2 # clear one product (with confirmation)
326
+ ```
327
+
328
+ ## Development setup
329
+
330
+ ```bash
331
+ git clone https://github.com/ACMAD-Niamey/acmadDL.git
332
+ cd acmadDL
333
+ uv sync
334
+ ```
335
+
336
+ ## Relationship to DeepScale
337
+
338
+ acmadDL handles ingestion and normalization; [DeepScale](https://github.com/accord-research/deepscale) handles downscaling and skill evaluation. Their interface is standardized xarray.