Imports in this page are from the original geo_toolz layout
These design docs were adapted from the geo_toolz design study.
Code snippets use the original feature-based import paths
(geo_toolz.<module>). In xrtoolz, the domain-agnostic
operations live under xrtoolz.geo.<module>; physics-specific
operations live under xrtoolz.ocn / xrtoolz.atm /
xrtoolz.rs. See xrtoolz/__init__.py and
xrtoolz/geo/__init__.py for the current export surface.
Boundaries and Ecosystem¶
Overview¶
geo_toolz owns the xarray geoprocessing operator layer: preprocessing, inference wrapping, and evaluation. It delegates compute to numpy/scipy/sklearn, framework-specific inference to user-installed backends, and domain modeling to downstream packages.
Ownership Map¶
| Concern | Owner | Notes |
|---|---|---|
| Coordinate validation, harmonization | geo_toolz | validation submodule |
| Spatial/temporal subsetting | geo_toolz | subset submodule |
| Regridding (scipy-based) | geo_toolz | No ESMF dependency |
| Land/ocean/country masks | geo_toolz | Via regionmask |
| Climatology, anomalies, detrending | geo_toolz | detrend submodule |
| Spectral analysis (PSD, cross-spectrum) | geo_toolz | Via xrft |
| Evaluation metrics (RMSE, PSD score, etc.) | geo_toolz | Via xskillscore + custom |
| Kinematics (oceanography, remote sensing) | geo_toolz | kinematics submodule |
| Operator/Sequential/Graph abstraction | geo_toolz | core submodule |
| Model wrapping (ModelOp) | geo_toolz | Framework-agnostic via duck typing |
| sklearn xarray bridge | xarray_sklearn | Optional companion; SklearnOp can delegate |
| Patch-wise tiling | xrpatcher | Optional companion |
| Data assimilation (EnKF, 4DVar) | ekalmX / vardax | Consumes geo_toolz for pre/post-processing |
| GP models | pyrox_gp | Independent; may consume geo_toolz features |
| Ocean/atmosphere forward models | somax / diffrax | Independent |
| Structured linear algebra | gaussx | Independent |
Decision Table¶
| Scenario | Recommendation |
|---|---|
| Preprocess satellite data for ML | Sequential([ValidateCoords(), Regrid(...), RemoveClimatology(...)]) |
| Run a trained sklearn model on xarray | SklearnModelOp(model, sample_dim="time") in a Sequential |
| Run a JAX model on xarray | JaxModelOp(model, sample_dim="time", jit=True) |
| Evaluate predictions vs reference | RMSE(...), PSDScore(...) — or Graph for multi-metric |
| Multi-input pipeline (preprocess + evaluate) | Graph(inputs=..., outputs=...) |
| Patch-wise inference on large grids | xrpatcher + Sequential per patch |
| Hydra-configurable pipeline | hydra_zen.builds(Sequential, [...]) |
| Conservative regridding (ESMF) | Use xesmf directly — not in geo_toolz scope |
| Distributed/dask processing | Use xarray+dask directly — deferred in geo_toolz |
Ecosystem Interactions¶
| External Package | Integration Point | Pattern |
|---|---|---|
| xarray | Interface layer for all operators | Dataset → Dataset everywhere |
| scipy | Regridding, interpolation, spectral | Compute backend for L0 functions |
| sklearn | Preprocessing transforms, models | Via SklearnOp / SklearnModelOp |
| regionmask | Land/ocean/country masks | masks submodule wraps regionmask |
| xrft | Fourier transforms | spectral submodule wraps xrft |
| xskillscore | Verification metrics | metrics submodule wraps xskillscore |
| hydra-zen | Config generation from operators | get_config() + builds() |
| xarray_sklearn | Full sklearn bridge with NaN policies | Optional, SklearnOp can delegate |
| xrpatcher | Patch-wise tiling for large grids | Optional, operators applied per patch |
| ekalmX | DA pre/post-processing | geo_toolz upstream (preprocess) and downstream (evaluate) |
Scope¶
In Scope¶
- Coordinate validation and harmonization
- Spatial/temporal subsetting and masking
- Regridding (scipy-based: linear, nearest, coarsen)
- Climatology, anomalies, detrending, filtering
- Coordinate encodings (cyclical, Fourier features)
- Binning and gridding (unstructured → gridded)
- Spectral analysis (PSD, cross-spectrum, coherence)
- Evaluation metrics (pixel-level, spectral, multiscale)
- Physical kinematics (oceanography, remote sensing, atmospheric)
- Operator/Sequential/Graph pipeline abstraction
- Framework-agnostic model wrapping (ModelOp)
- sklearn interop (SklearnOp)
Out of Scope¶
- Data assimilation algorithms — ekalmX / vardax
- GP models, kernel learning — pyrox_gp
- Forward models (ocean, atmosphere) — somax / diffrax
- Structured linear algebra — gaussx
- Conservative regridding (ESMF) — xesmf
- Distributed computation (dask-native) — deferred to v0.4+
- Non-Earth geospatial (urban, indoor, planetary) — out of scope
Testing Strategy¶
Fixtures¶
# conftest.py
@pytest.fixture
def ds_global():
"""Global 1° dataset: (time=100, lat=180, lon=360), with coords and attrs."""
@pytest.fixture
def ds_regional():
"""Mediterranean subset: (time=50, lat=40, lon=75)."""
@pytest.fixture
def ds_unstructured():
"""Scattered observations: (obs=5000) with lon, lat, time as 1D coords."""
@pytest.fixture
def ds_with_nans(ds_global):
"""ds_global with ~15% NaN (land mask pattern)."""
Test Categories¶
- Operator contract tests: every operator subclass must return the right type, preserve attrs when appropriate, and have a valid
get_config()that round-trips through__repr__. - Numerical correctness tests: for each Layer 0 function, compare against a known analytical result or a reference implementation (scipy, xskillscore, metpy).
- Pipeline integration tests: build realistic multi-step pipelines and verify they run end-to-end without error.
- Round-trip tests:
AddClimatology(clim)(RemoveClimatology(clim)(ds))should recover the original dataset to float precision. - Edge cases: empty datasets, single-point grids, all-NaN slices, non-standard CRS, duplicate time coordinates.
Test Priorities¶
- Operator contract — every operator returns the right type, preserves attrs, has valid
get_config() - Numerical correctness — L0 functions match scipy/xskillscore reference implementations
- Pipeline integration — realistic multi-step pipelines run end-to-end
- Round-trip —
AddClimatology(clim)(RemoveClimatology(clim)(ds))recovers original
Roadmap¶
v0.1 — Foundation (target: 4-6 weeks)¶
core(Operator, Sequential, Input, Node, Graph, Lambda, Identity)validation(full)subset(full)masks(full, wrapping regionmask)detrend(climatology, anomalies, basic filter)regrid(linear, nearest, coarsen via scipy)metrics(RMSE, NRMSE, MAE, Bias, Correlation)- Package scaffolding from
pypackage_template - Tests for all of the above
- Basic mkdocs site with API reference
v0.2 — Analysis Depth + Inference (target: +4 weeks)¶
interpolation(fillnan spatial/temporal, RBF, resample)discretize(binning, points-to-grid)encoders(coordinate transforms, cyclical, Fourier features)spectral(PSD, isotropic PSD, cross-spectrum)metrics(PSD score, resolved scale, multiscale RMSE)extremes(block maxima, POT, point process)inference(ModelOpbase,SklearnModelOp,JaxModelOp, batch support)- End-to-end examples: preprocess → infer → evaluate pipelines
- Tutorial notebooks
v0.3 — Domain Operators (target: +4 weeks)¶
kinematicsoceanography (streamfunction, geostrophic velocity, vorticity, Okubo-Weiss, KE, MLD)kinematicsremote sensing (NDVI, radiance/reflectance, brightness temperature)kinematicsmethane (column averaging kernel, dry air column, mixing ratio)kinematicsatmospheric (potential temperature, wind speed/direction)crs(assign, reproject via rioxarray)sklearn(xarray wrapper viaxarray_sklearn; optionalxrpatchersupport for patch-wise ML)
v0.4+ — Continuous Expansion¶
- Dask integration for chunked computation
- Deeper
xrpatcherintegration (auto-reconstruct, parallel dispatch) - Additional regridding methods (conservative approximation)
- Quality control operators (outlier detection, observation count thresholds)
- More metrics from xskillscore
- More kinematics as needed
- Numba/JAX kernels for performance-critical operations
- xarray accessor:
ds.geo.validate().regrid(grid).detrend(clim)
Open Questions¶
-
xarray accessor? A
ds.geo.validate()accessor would be convenient but adds coupling. Defer to v0.4+ or include from v0.1? -
Dask awareness in v0.1? Should operators check for dask-backed arrays and call
.compute()/.persist()at appropriate points? Or leave dask entirely to the user? The safest v0.1 approach is to document that operators work on in-memory arrays and dask support comes later. -
Unit handling. Some kinematics operations (streamfunction, geostrophic velocities) benefit from
pint-xarrayunit tracking. Should this be opt-in via a flag, or always-on for kinematics operators? -
Grid as an Operator? The
Grid/SpaceTimeGriddataclasses indiscretizecould themselves be operators that generate empty coordinate scaffolds. IsCreateGrid(lon_bnds, lat_bnds, resolution)useful, or is the dataclass sufficient? -
Graph serialization. The
Graphcan serialize its topology (which operators connect to which) viaget_config(). But if someone wants to save a full graph to YAML and reload it (including the operator configs), that requires a class registry to map class names back to classes. Shouldgeo_toolzinclude a lightweight registry (register_operator/get_operator_by_name), or leave that to Hydra-zen which already handles it?