climate-indices
Pure-Rust numerical kernels for climate and drought indices: the Standardized Precipitation Index (SPI), the Standardized Precipitation Evapotranspiration Index (SPEI), the Evaporative Demand Drought Index (EDDI), potential evapotranspiration (PET), the Palmer family, fire-weather indices, and flood indices.
The crate's only dependency is ndarray: no I/O, logging, or metadata layer. It
provides kernels, not
complete index workflows: the caller prepares calendars, selects the calibration period,
validates physical ranges, and applies any aggregation and final index transformation.
Usage
[]
= "0.1"
= "0.17"
Array inputs borrow ndarray views and array outputs own their data. A daily block has
shape (days, cells), so one series and a grid use the same call:
use ;
use array;
The crate documentation on docs.rs states the shape, calendar, unit, and error conventions for each kernel.
Examples
The crate ships runnable programs that build an index from plain arrays, with no file input and no orchestration crate:
Each example performs the caller-side steps the kernels document. SPI and SPEI prepare the calendar, accumulate the Timescale window, select the Calibration Period, estimate a discrete zero mass where the distribution has one, and apply the standard-normal transform; the PET example prepares temperatures, latitudes, and leap-year flags for a single kernel call.
Numerical reference
The kernels originate in the climate_indices
project, whose reference implementations are the numerical parity oracle: the project's
parity suite holds every kernel to rtol = atol = 1e-10 against them.
Versioning
The crate follows its own 0.x version track, independent of the climate_indices
distribution. Before 1.0, a minor version can make breaking changes. ndarray is
a public dependency, so an incompatible ndarray upgrade is a breaking release.
The minimum supported Rust version is 1.85.
Kernel benchmarks
Run the Criterion suite from the workspace root:
# Compile and check every fixture and kernel once, without collecting timings:
# Measure one kernel in both cell configurations:
The suite needs only Rust. Criterion is a development dependency; its plotting and parallel-analysis features are disabled. Version 0.7 supports the crate's Rust 1.85 minimum. Cargo uses the optimized bench profile and the workspace's thin LTO and single codegen unit. No custom CPU flags are required.
benches/kernels.rs generates small deterministic fixtures without files, downloads,
or a random-number dependency. Its base sample is
((step * 37 + cell * 17 + 11) % 101) / 100.0. Monthly cases use 40 years
(1980–2019), 12 months, and either 1 or 32 cells. Rain includes zeros; the
log-logistic fit uses signed synthetic water-balance values. Fits use all 40 years.
Thornthwaite uses positive temperatures, 40° latitude, and Gregorian leap flags.
EDDI ranks only January's PET against its 40-year sample, without leading pads.
Palmer receives rain, PET, and a five-inch available water capacity in inches.
Daily cases use 1,830 consecutive positions and either 1 or 32 cells. FFMC starts
at 85, records all history, and has valid weather with no gaps or seasonal mask.
Effective precipitation uses a 30-day window, whose first 29 outputs are missing.
These synthetic fixtures are measurement inputs, not scientific reference data.
Fixture generation, fit parameters for the probability benchmark, unit conversions,
and output assertions run outside the timed loops. Each timed iteration calls one
kernel with borrowed views and consumes its output through std::hint::black_box.
Kernel allocations and output destruction are included. Each recurrence starts
from the same initial state. The smoke command checks fit validity, probability
ranges, PET, water-balance and FFMC outputs, and effective-precipitation padding.
This is a representative subset, not complete workflow coverage: Gamma fit and probabilities, log-logistic fit, EDDI ranking, Thornthwaite PET, Palmer water balance, FFMC, and effective precipitation. It excludes other PET, distribution, Palmer, fire, and flood kernels, final index transformations, I/O, and parallel execution. It does not measure complete SPI, SPEI, EDDI, or PDSI workflows.
Representative measurements
Measured on 2026-10-19 with an Apple M5 (10 physical/logical cores), 32 GiB RAM,
macOS 27.0.1 arm64, Rust/Cargo 1.99.0, and Criterion 0.7.0. The suite calls the
kernels serially. The command above used Criterion's defaults: a three-second
warm-up, five-second measurement target, and 100 samples per case. The recorded
run added -- --noplot; it used no custom RUSTFLAGS.
Times and throughput below are Criterion's central estimates, not minimum times. Throughput counts millions of input time-cell values per second (Melem/s), not output arrays or arithmetic operations. EDDI counts 40 values per cell; the other monthly kernels count 480. Daily kernels count 1,830, including window padding.
| Kernel | 1 cell, µs/call | 1 cell, Melem/s | 32 cells, µs/call | 32 cells, Melem/s |
|---|---|---|---|---|
gamma_parameters |
1.049 | 457.46 | 76.779 | 200.05 |
gamma_probabilities |
31.092 | 15.44 | 2425.5 | 6.33 |
loglogistic_parameters |
3.929 | 122.17 | 155.45 | 98.81 |
thornthwaite |
9.912 | 48.43 | 238.77 | 64.33 |
tukey_probabilities |
0.464 | 86.27 | 19.538 | 65.52 |
water_balance |
2.588 | 185.49 | 417.12 | 36.82 |
ffmc |
79.187 | 23.11 | 2349.0 | 24.93 |
effective_precipitation |
6.506 | 281.28 | 282.19 | 207.52 |
The raw output preserves the 95% confidence
intervals and outlier counts. The 32-cell Gamma cases have wide intervals;
repeat measurements before making performance decisions. Criterion also writes
local estimates under target/criterion/. These machine-specific results are not
speedup claims or CI thresholds. Re-running reproduces the inputs and protocol,
not identical elapsed times.
License
BSD-3-Clause. The LICENSE file includes the third-party notices for the
ported Cephes and NumPy routines.