climate_indices
[//]: # ([](https://coveralls.io/github/monocongo/climate_indices?branch=main))
[//]: # ([](https://www.codacy.com/app/monocongo/climate_indices?utm_source=github.com&utm_medium=referral&utm_content=monocongo/climate_indices&utm_campaign=Badge_Grade))
Python library of climate indices for drought, wildfire, and flood monitoring
climate_indices provides reference implementations of climate index algorithms that give a
geographical and temporal picture of the severity and duration of precipitation, temperature,
evaporative-demand, fire-weather, and wet-extreme anomalies for climate monitoring and research.
It began as a drought library; it now covers drought, wildfire, flood potential, and
climate-driven energy supply and demand.
Indices
Drought and moisture balance
- SPI,
zero_handling)
- SPEI,
- SPAI, Standardized Precipitation
- Standardized index (
indices.standardized_index()), the SPI fitting pipeline for any
- PNP,
- PCI, Precipitation Concentration Index
- EDDI, Evaporative Demand Drought Index
- Palmer indices,
Evapotranspiration
- PET,
Wildfire (climate_indices.fire)
climate_indices.flood)
- Effective Precipitation (PE) and its derived
flood_events()andflood_event_summary(), which extract events with onset, duration,
Energy (climate_indices.energy)
The degree-day, wind-profile, and standardized energy index APIs have stable
xarray.DataArray support from 3.1.0. The DataArray route for
energy_drought_events() remains Beta. Available functions include:
- Heating, cooling, and energy degree days (HDD / CDD / EDD). Validation: exact reference for
- Hub-height wind-speed profile. Validation: independent-implementation comparison on
- Wind capacity factor (NumPy-only). Validation: regression only; specification-level
- Temperature-derated PV capacity factor (NumPy-only). Validation: independent-implementation
- Standardized energy production and demand indices: SREPI and a standardized demand index,
SEI 0.2.0
- Run-theory energy-drought events (onset, duration, severity). Validation: regression only;
- A documented recipe for a capacity-weighted precipitation proxy for hydropower
- These indices describe the climate-driven component of energy supply and demand, not
Compound hazards (climate_indices.compound)
- Declared-hazard harmonization onto a shared calendar, joint counts and frequencies, the
- The NumPy routes are stable; the Dataset routes and the eager event catalogs are Beta
- These statistics describe co-occurrence; they make no causal or significance claim
- Run theory (
climate_indices.runs): runs above or below a threshold with duration,
- Distribution fit diagnostics (
fit_diagnostics()): the fitted parameters, probability of
- Probability-scale output (
output_scale): standardized indices as normal scores, fitted
[-1, 1] scale
Highlights
- Rust backend. An optional compiled extension (
climate_indices._native)
rtol = atol = 1e-10 with matching NaN
positions, not bit-for-bit equality; see the parity contract.
Speedups vary by kernel: the flood family
measured 1.2x to 6.7x faster than its Python path and Thornthwaite PET 1.4x, while
Hargreaves measured at parity and Penman-Monteith slightly slower (0.89x) at the benchmark
size; see the measurements.
- Vectorized gridded computation. SPI, SPEI, EDDI, PNP, PET, and PDSI fit or rank a
(time, *cells) block in one call instead of looping over cells. On a 38x87-cell,
40-year reference grid that is 4.4x faster for SPI, 3.6x for SPEI, 53x for Thornthwaite
PET, 342x for EDDI, and 111x for gridded PDSI. On fully populated monthly grids, gamma
SPI/SPEI, EDDI, and PNP are bit-identical to per-cell computation; Thornthwaite PET and
successful Pearson fits agree within 1e-12. Pearson SPI's block-wide gamma fallback is
not per-cell equivalent and can depend on chunk layout; daily and missing-data bounds
are documented separately.
- xarray, Dask, and Zarr. Supported xarray entry points accept
xarray.DataArrayinput,
indices.standardized_index() and scPDSI remain NumPy-only.
See the compatibility matrix,
writing NetCDF and Zarr outputs, the
Zarr/Dask notebook, and
chunking and scheduler guidance.
- Validation. Documented evidence distinguishes external checks against NOAA, NRCan,
- Command-line interface.
climate_indices --index
Project goals
This Python implementation of the above climate index algorithms is being developed with the following goals in mind:
- to provide an open source software package to compute a suite of
- to provide a central, open location for participation and collaboration
- to facilitate standardization and consensus on best-of-breed
- to provide transparency into the operational code used for climate
- to incorporate modern software engineering principles and scientific programming
This is a developmental/forked version of code that was originally developed by NIDIS/NCEI/NOAA. See drought.gov.
- __Documentation__
- Climate Indices for Wildfire Applications
- Climate Indices for Flood and Wet-Extreme Applications
- __Validation status__
- __Changelog__
- __License__
- __Disclaimer__
Installation
This README describes 3.2.0, which is not yet published on PyPI. The published 3.0.0
release added the fire, flood, and runs modules and the optional Rust backend.
Relative to 3.0.0, 3.1.0 adds the compound module and the energy family, promotes the
DataArray index APIs to Stable, and adds optional Icechunk I/O. Relative to 3.1.1, 3.2.0
adds SPAI and its pooled-anomaly fit.
Install the current published release from PyPI:
pip install "climate-indices>=3.0"
uv users can run uv pip install "climate-indices>=3.0". See
Supported Python Versions for interpreter support.
Starting with 3.0.0, wheels for Linux (x86-64 and aarch64, manylinux_2_28, so glibc 2.28 or newer), macOS (Apple silicon and Intel), and Windows (x86-64) include the optional Rust acceleration backend; every other platform, including a Linux system with older glibc, installs the pure-Python wheel. Both carry the same version and the same public API; results agree within the cross-backend parity tolerance, not necessarily bit for bit. Calling code need not depend on which one pip selected. To see which one you have:
python -c "import climate_indices._native" # succeeds when the extension is installed
A missing extension is not an error: climate_indices imports, and every computation
runs the Python implementation. Installing from source needs no Rust toolchain either —
pip install --no-binary climate_indices climate_indices installs from the sdist, which
builds the pure-Python wheel. Developers who
want the extension from a checkout build it with uv run maturin develop --release; see
the Rust backend section of the architecture notes.
Starting with 3.1.0, the optional icechunk extra adds Icechunk-backed reads and
writes (icechunk>=1.1.21,<2). It needs Python 3.11 or later; the core install is
unchanged. Until 3.1.0 is published, install the extra from a 3.1.0 source checkout:
pip install ".[icechunk]"
After 3.1.0 is published, install it from PyPI:
pip install "climate_indices[icechunk]>=3.1.0"
See Write NetCDF, Zarr, and Icechunk outputs and the Icechunk SPI/SPEI notebook.
Rust crate
The numerical kernels are also available in the standalone climate-indices
crate's 0.1.0 release
(crates.io,
docs.rs):
cargo add climate-indices. The earlier 0.0.0 package reserved the name and
contains no algorithms. The crate is pure Rust: ndarray is its only
dependency, and it has no PyO3 or Python bindings. It provides kernels, not complete
index workflows, so the caller prepares calendars, selects the Calibration Period,
validates physical ranges, and applies the final transform.
The crate follows its own 0.x version track, independent of the Python distribution
version, and needs Rust 1.85 or later. Before 1.0, a minor version can make breaking
changes. See crates/climate-indices/README.md for
usage, examples, and benchmarks, and
crates/climate-indices/CHANGELOG.md for the
published versions.
Developer Workflow
This project uses trunk-based development. main is the trunk and should always
be releasable.
- Start from current trunk:
git switch main && git pull --ff-only origin main
- Create a short-lived branch:
git switch -c feature/
- Make focused changes with tests.
- Run validation:
uv run ruff check src/ tests/
uv run ruff format --check src/ tests/
uv run mypy src/ tests/test_type_checking.py
uv run pytest
- Open a PR into
main. - The maintainer merges the PR after review and passing CI; agents never merge.
feature/, fix/, docs/, chore/,
perf/, refactor/, test/, ci/, or
hotfix/ branch names. Release branches are avoided; use maintenance
branches only for approved older-version support.
The methodology behind this workflow — parallel agent sessions, worktree isolation, and the evidence each change must carry — is documented in AI-assisted development.
Release Recipe
Releases are tag-based. The Git tag, package version, GitHub Release, and PyPI version must match.
- Git tag:
v1.2.3 - Package version:
1.2.3 - GitHub Release:
v1.2.3 - PyPI release:
1.2.3
- Prepare a release PR that updates
pyproject.toml,CHANGELOG.md,
- Confirm
mainis green. - Create an annotated tag from
main. - Push the tag. The release workflow builds, validates, publishes to PyPI, and
Tag creation and publishing require maintainer approval. See
docs/release-process.md for the full checklist.
Maintainer Quick Commands
Read-only preflight:
git status --short
git branch --show-current
git log --oneline --decorate -5
uv run pytest tests/test_release_integrity.py
Safe PR branch setup:
git switch main
git pull --ff-only origin main
git switch -c chore/issue-667-release-docs
Approval-required release tag commands:
git switch main
git pull --ff-only origin main
git tag -a vX.Y.Z -m "Release vX.Y.Z"
git push origin vX.Y.Z
Supported Python Versions
Supports Python 3.11–3.14.
Python 3.10 support ends in v3.1.1. This source checkout already requires Python 3.11 or later.
All supported versions are tested on Linux (ubuntu-latest) for every pull request and push
to main. Python 3.14 is additionally tested on macOS on every event, and 3.11 on macOS on
pushes to main, merge groups, the weekly schedule, and manual dispatch. Both latest and
minimum declared dependency versions are tested in CI.
Version Support Policy
This project provides 12 months notice before dropping support for a Python version. When a version approaches end-of-life, removal will be announced via the CHANGELOG and a GitHub issue, and implemented no sooner than 12 months after announcement with a version bump. The Python 3.10 removal in v3.1.1 is an exception requested in #1511.
Python 3.9 support was dropped in v2.2.0 (August 2025) due to scipy>=1.15.3 requiring 3.10+.
API Stability
| API Surface | Status | Guarantee |
|:------------|:------:|:----------|
| NumPy array functions (indices.spi, indices.spei, indices.pet, the fire, flood, energy, and compound NumPy routes) | Stable | No breaking changes in minor versions |
| xarray index entry points accepting xr.DataArray (including Dataset-returning index and diagnostic functions) | Stable from 3.1.0 | No breaking changes in minor versions |
| Compound xarray Dataset routes and eager event catalogs | Beta | No breaking changes in patch versions |
| SPAI (indices.spai and climate_indices.spai, NumPy and xr.DataArray) | Beta | No breaking changes in patch versions |
Stable API: The NumPy computation functions and xarray index APIs follow strict semantic versioning. The xarray adapter provides automatic parameter inference, coordinate preservation, CF metadata, and Dask support. Its interface is stable from 3.1.0, after the 3.0.0 calendar-alignment release (ADR-0012). Results follow the documented numerical-equivalence bounds. This promotion changes no numerical algorithm or calendar behavior.
Beta API: The compound xarray Dataset routes and eager event catalogs remain Beta under
ADR-0022.
Their interfaces may change in minor releases, but not patch releases.
They continue to emit BetaFeatureWarning; its public imports and warning filters remain supported.
SPAI is Beta on both its NumPy and DataArray routes and emits BetaFeatureWarning once per call.
Stable DataArray index APIs do not emit this warning.
See docs/xarray_compatibility.md for the compatibility matrix, including
Dask chunking constraints, metadata behavior, and the Palmer xarray adapter.
See docs/performance.md for a runnable parallel SPI/SPEI example and
the measured speedups behind the chunk and scheduler guidance.
Validation
VALIDATION.md classifies the evidence for the indices in its per-index
inventory, separating independent external validation from regression coverage and recording
their known gaps. Fosberg Fire Weather Index and Haines Index are not yet classified there.
uv run pytest -m validation runs the marked reference-fixture and specification checks.
FAO-56 worked-example checks run in the core suite via uv run pytest tests/test_pm_eto.py.
The evidence includes:
- EDDI: NOAA PSL monthly reference ET/EDDI for 1-, 3-, and 6-month timescales
2.43e-6.
- SPI: characterized against NOAA NCEI's operational climate-divisional SPI for all 344
SEI and SCI packages.
- SPEI: reproduces SPEIbase v2.11 from its own CRU TS inputs (correlation
SPEI package's log-logistic fit within 1e-6.
- Standard Palmer (PDSI, PHDI, PMDI, Z-Index): qualified external validation against
- scPDSI: cross-validated against Wells-lineage reference fixtures (
atol=5e-5). - CFFWIS: all seven components match the NRCan
cffdrsreference within1e-9. - KBDI: reproduces the Keetch & Byram (1968) worked example.
- HDW: reproduces event timing and tracks the daily series in the Srock et al. (2018)
- PET: selected FAO-56 helper examples and the daily end-to-end Example 18, plus
- Flood family: specification-level and regression coverage only; no external numeric
The Rust backend has its own gate: the parity registry in tests/parity_registry.py runs
each ported entry point through the extension and through the Python implementation on
property-generated inputs, and fails if a kernel is added without an entry.
Upgrading to 3.0.0
3.0.0 changes some computed values and exception types without a deprecation period, most
notably corrected daily calendar alignment in the xarray adapter, the PCI February fix, and
the gamma probability of zero now taken from the Calibration Period. It also removes the spi
console script in favor of climate_indices --index spi. Each change, how to detect it, and
what to change is in docs/deprecations/api-changes.md;
the full list is in the CHANGELOG. Upgrading from 2.1.x or earlier also
crosses the 2.2.0 switch from None return tuples to exceptions.
Citation
You can citeclimate_indices in your projects and research papers via the BibTeX
entry below.
@misc {climate_indices,
author = "James Adams",
title = "climate_indices, an open source Python library providing reference implementations of commonly used climate indices",
url = "https://github.com/monocongo/climate_indices",
month = "may",
year = "2017--"
}