aihydro-lsh
Global large-sample hydrology — CAMELS-like catchment attributes for globally routed watersheds, with source-specific caveats.
A recipe layer over aihydro-data and aihydro-watershed that computes 101 static scalar attributes (8 families) plus daily meteorological forcing timeseries and temporal-tier extensions (trends, annual dynamics, drought/flood events) for basin geometries through region-aware product routing. The camels-attrs CONUS reference implementation is used to validate recipes against USGS gauge data; use aihydro_lsh.reliability.reliability_matrix() for the family/region caveats that qualify global interpretation.
Quick start
import geopandas as gpd
from shapely.geometry import box
import aihydro_lsh
# Any watershed polygon (EPSG:4326)
geom = gpd.GeoDataFrame(geometry=[box(-79.5, 38.5, -77.5, 39.5)], crs="EPSG:4326")
result = aihydro_lsh.attributes(geom, "1990-01-01", "2020-12-31")
print(result.attrs["p_mean"]) # mean daily precip (mm/day)
print(result.attrs["elev_mean"]) # mean elevation (m)
print(result.attrs["geol_porosity"]) # GLHYMPS porosity
df = result.to_dataframe() # single-row DataFrame, 101 columns
# Full provenance report (agent-relay verbatim — package is the authority)
print(result.to_report())
# Fully JSON-serializable dict (NaN → null)
d = result.to_json()
print(d["meta"]["content_hash"]) # SHA-256 fingerprint of attrs
# USGS gauge — auto-delineate then extract (outlet coords wired automatically)
from aihydro_watershed.delineation.router import delineate_from_point
ws = delineate_from_point(39.27, -77.54) # Potomac Point of Rocks
result = aihydro_lsh.attributes(ws, "1990-01-01", "2020-12-31",
gauge_id="01638500")
# ws is a HydroResult — outlet_lat/outlet_lon extracted automatically for geomorphic attrs
# Daily forcing timeseries (for ML workflows — LSTM, HBV training)
df = aihydro_lsh.forcing(geom, "1990-01-01", "2020-12-31")
print(df.columns.tolist())
# ['prcp_mm', 'tmin_C', 'tmax_C', 'tavg_C', 'pet_mm', 'srad_Wm2', 'wind_ms', 'sph_kgkg']
# Combined: scalar attrs + daily forcing in one call
result = aihydro_lsh.attributes(geom, "1990-01-01", "2020-12-31", include_forcing=True)
print(result.forcing) # pd.DataFrame with DatetimeIndex
# Temporal extensions: Mann-Kendall trend scalars (T2.5)
result = aihydro_lsh.attributes(geom, "1990-01-01", "2020-12-31", include_trends=True)
print(result.trends) # pd.DataFrame — 16 slope/p-value scalars
# Temporal extensions: annual dynamic panels (T3)
result = aihydro_lsh.attributes(geom, "1990-01-01", "2020-12-31", include_dynamic=True)
print(result.dynamic) # pd.DataFrame (year × 22 variables)
# Temporal extensions: drought and flood event inventory (T3)
result = aihydro_lsh.attributes(geom, "1990-01-01", "2020-12-31", include_events=True)
print(result.events["drought"]) # DataFrame of SPI-drought episodes
print(result.events["flood"]) # DataFrame of POT-flood exceedances
# Everything at once
result = aihydro_lsh.attributes(
geom, "1990-01-01", "2020-12-31",
include_forcing=True,
include_trends=True,
include_dynamic=True,
include_events=True,
)
# Batch extraction (list of pour-points → single DataFrame)
df, errors = aihydro_lsh.batch_attributes(
[{"id": "01638500", "lat": 39.27, "lon": -77.54},
{"id": "02108000", "lat": 35.53, "lon": -79.09}],
start="1990-01-01", end="2020-12-31",
)
df.to_csv("camels_global.csv", index=False)
Installation
pip install -e . # editable (ecosystem tree)
pip install -e .[validate] # + camels-attrs CONUS reference
pip install -e .[mcp] # + MCP server tools
Attribute families (101 static scalars, 8 families + 1 timeseries family)
| Family | Attrs | Data source | Notes |
|---|---|---|---|
| watershed | 5 | GDF geometry | area, perimeter, compactness, elongation, form_factor |
| topography | 7 | GLO30 / DEM3DEP / SRTM | elev stats, slope stats, area |
| climate | 14 | GRIDMET / ERA5-Land / CHIRPS | aridity, seasonality, snow frac, high/low precip stats |
| soil | 9 | POLARIS / SoilGrids | texture, Ksat; porosity/AWC/FC from Saxton-Rawls PTF |
| geomorphic | 28 | 3DEP/GLO30 + NLCD/ESA WorldCover | shape, relief, stream network, CN, sinks, land cover % |
| vegetation | 13 | MODIS LAI/NDVI + ESA WorldCover | LAI/GVF seasonal stats, LC fractions, root depth |
| geology | 9 | pygeoglim (GLiM + GLHYMPS) | lithology classes, carbonate frac, permeability, porosity; global path is permission/tile-gated |
| hydrology | 17 | NWIS (CONUS) / GEOGLOWS (global) | q_mean, BFI, FDC slope, half-flow date, … |
| forcing | timeseries | GRIDMET (CONUS) / ERA5-Land (global) | Daily: prcp_mm, tmin_C, tmax_C, tavg_C, pet_mm [+ srad, wind, sph] |
geomorphic runs after soil so clay_frac is available for NRCS Curve Number soil group estimation.
forcing is opt-in (not in the default RECIPE_ORDER) — request via include_forcing=True or families=["forcing"].
Temporal-tier extensions
The static families above are T1 (permanent) and T2 (window-mean over the caller's date range). Three optional tiers extend the attribute landscape:
| Tier | Flag | Output | Description |
|---|---|---|---|
| T2.5 — Trends | include_trends=True |
result.trends — up to 16 slope/p-value scalars |
Mann–Kendall p-value and Sen slope for eight derived series: precipitation mean, temperature mean, PET mean, snow fraction, LAI maximum, GVF mean, discharge mean and runoff ratio. At least ten valid annual values are required. P-values do not yet adjust for serial dependence or multiple comparisons. |
| T3 — Dynamic | include_dynamic=True |
result.dynamic — year × 22-column schema |
Annual climate, vegetation and streamflow summaries. Three annual land-cover columns are currently unavailable and filled with NaN. Other columns can also be missing by source/year; this is a schema, not 22 measured values in every year. Annual q5 is high flow (5% exceedance) and q95 is low flow. |
| T3 — Events | include_events=True |
result.events["drought"] + result.events["flood"] |
SPI-based drought episodes (McKee et al. 1993) and peaks-over-threshold flood exceedances (Coles 2001). Each row is a discrete event with start/end date, duration, severity/magnitude. |
The temporal extensions are opt-in so standard attributes() calls remain fast. All three can be combined with include_forcing=True in a single call.
Geometry input forms
attributes() accepts any of:
HydroResultfromdelineate_from_point()/delineate_watershed()— outlet coords auto-wiredGeoDataFrame(EPSG:4326, single or multi-row)- Shapely Polygon / MultiPolygon
(lat, lon)tuple — wrapped as 0.001° buffer (use full delineation for accurate basin-mean attrs)(minx, miny, maxx, maxy)bbox tuple
Provenance + reproducibility
Every AttributeResult carries:
.provenance— per-familyAttrProvenance(source, product, region, resolution_m, is_observed).meta—ResultMetawith package versions, bbox, timing, and a SHA-256 content hash.to_report()— human-readable report; agents relay this verbatim (package is the authority).to_json()— fully JSON-serializable dict (NaN → null)
The is_observed flag distinguishes gauge data (NWIS, is_observed=True) from modelled or
reanalysis products (GEOGLOWS, ERA5-Land, is_observed=False) — critical for uncertainty-aware
interpretation by agents. For hydrology specifically, gauge-less/global streamflow signatures
carry quality_flags including modelled_streamflow; AttributeResult.to_report() emits a
mandatory MODELLED STREAMFLOW CAVEAT so BFI/FDC/flood-frequency/timing metrics are not
mistaken for observed catchment properties.
Layering
aihydro-core ← aihydro-data ← aihydro-lsh
aihydro-watershed ↗
aihydro-lsh may depend DOWN on core/data/watershed, never UP on aihydro-tools.
Enforced by the AST guard in tests/test_layering.py and import-linter.
MCP tools (C5)
When aihydro-lsh[mcp] is installed alongside aihydro-tools, ten tools are
auto-registered on the shared server:
| Tool | Description |
|---|---|
lsh_attributes |
All 101 static scalar attrs for a polygon or pour-point |
lsh_forcing |
Daily basin-averaged meteorological timeseries (LSTM/HBV) |
lsh_dynamic_attributes |
Annual dynamic panels (T3) — year × 22 variables |
lsh_events |
Drought (SPI) and flood (POT) event inventories (T3) |
lsh_families |
List families + attribute keys |
lsh_attribute_info |
Unit, source, family, formula, CAMELS equivalent, and caveat for one attribute |
lsh_batch |
Multi-basin extraction from JSON array |
lsh_compare_to_ref |
lsh vs camels-attrs side-by-side (CONUS) |
lsh_recipe_status |
Check which recipes can run (auth/deps) |
lsh_reliability_matrix |
Family/region availability status and interpretation caveats |
For comparative studies, keep product chains explicit. The helper
aihydro_lsh.batch.summarize_product_homogeneity(results) reports whether each
attribute family used one product across a result set or mixed products such as
CHIRPS and ERA5-Land. Mixed products are valid but should be reported as a
source of uncertainty rather than interpreted as purely hydrologic contrast.
Reliability matrix
aihydro_lsh.reliability.reliability_matrix() returns a code-readable status
for each attribute family by region (reliable, available_with_caveats, or
not_available). It is intentionally static and import-light: it does not prove
live network/data availability, but it prevents binary “global” claims from
dropping caveats such as modelled streamflow, product-switching uncertainty, and
permission/tile availability for geology. Geology provenance includes
global_tiles_status=... from pygeoglim.global_tiles_status() where available.
CONUS parity validation (C3)
# CI gate — offline, reads the pre-generated fixture, no network required
pytest tests/test_conus_parity.py -k Offline --tb=short
# Regenerate the fixture (network + GEE + pygeoglim HF token required;
# several minutes per gauge)
python scripts/gen_conus_parity_fixture.py
# Live spot-check against a fresh live run instead of the cached fixture
pytest tests/test_conus_parity.py -m live --tb=short
The parity suite is parameterized by tests/fixtures/conus_parity_panel.json
— inspect that file for the current basin panel and how each gauge was
selected (stratified by aridity x snow fraction x relief using
pygeohydro.get_camels()'s real 671-station CAMELS metadata). It checks:
- Topography: ±10 % of camels-attrs reference
- Climate: ±10 %
- Geomorphic: shape ratios within geodesic tolerance
- Geology: ±5 % (same pygeoglim backend)
- NaN guard: ≤5 NaN numeric attrs for a healthy extraction (geomorphic sinks/CN may NaN on global path)
What this does and does not establish: this validates that aihydro-lsh
reproduces camels-attrs on CONUS — it does not validate the global attribute
claim (no non-CONUS ground truth exists in this chain, and gauge-less
hydrology signatures are computed from modelled streamflow with no observed
reference to check against anywhere). See papers/DECISIONS.md PD-4 for the
claim-scoping decision this follows, and
scripts/gen_parity_tolerance_table.py for the per-attribute divergence
table generated from the fixture.
Citations
Attribute-level citations are returned in AttributeResult.citations.
Key references:
- Saxton & Rawls (2006) — soil PTF
- Hartmann & Moosdorf (2012) — GLiM lithology
- Gleeson et al. (2014) — GLHYMPS permeability
- Gutman & Ignatov (1998) — NDVI→GVF conversion
- Jackson et al. (1996) — rooting-depth profiles
- Addor et al. (2017) — CAMELS attribute definitions
- Mann (1945); Kendall (1975) — Mann-Kendall trend test
- Sen (1968) — Sen's slope estimator
- McKee, Doesken & Kleist (1993) — Standardised Precipitation Index (SPI)
- Coles (2001) — peaks-over-threshold extreme value analysis
Metadata
Release files for aihydro-lsh 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| aihydro_lsh-0.2.1.tar.gz | 115.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aihydro_lsh-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 208.7 kB
Release files / aihydro_lsh-0.2.1.tar.gz
| Download URL | aihydro_lsh-0.2.1.tar.gz |
|---|---|
| Size | 115.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
baff6f99e01538c45a2fa2d22bed3c09be5637fca4dfafffe14b0b908743aef1
|
|
BLAKE2b-256 checksum How to use checksums |
a5a236aca1942b963a1992933ad3fc43f775757820a6ab1a51b7a229998e003c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.2
|
Release files / aihydro_lsh-0.2.1-py3-none-any.whl
| Download URL | aihydro_lsh-0.2.1-py3-none-any.whl |
|---|---|
| Size | 93.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
39e9002e107a2403f31a189ceddde3d15b9867547f4d4370f5d063a147d931f1
|
|
BLAKE2b-256 checksum How to use checksums |
2aea7b7447929bb10b5d376c395a78b99f7007e2d032e77b4cdf66a252a85588
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.2
|
Security for aihydro-lsh 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.