⚠️ Important Note
This project is actively under development. While the core functionality is production-ready and thoroughly tested, some advanced features are still being refined.
Traditional InSAR processing tells you where the ground has subsided. st-insar tells you where it's going to — by fusing high-resolution Sentinel-1 displacement time-series with coarse hydrological reanalysis and optical foundation-model embeddings on a single hierarchical graph, and forecasting forward with a physics-informed Spatio-Temporal Graph Neural Network that is constrained, by construction, to respect Terzaghi's principle of effective stress.
It exists because groundwater-driven land subsidence — in the Central Valley, in Mexico City, in Jakarta — is one of the few climate-adjacent hazards that is genuinely predictable months in advance, if the model is given the right physics and the right multi-modal signal. Most InSAR tooling stops at the interferogram. st-insar starts there.
Why this exists
Land subsidence from groundwater over-extraction is slow, cumulative, and expensive to reverse — by the time a well field, a rail line, or a coastal levee shows visible damage, the compaction that caused it may already be irreversible. InSAR gives geodesists a precise retrospective record of that compaction, at millimeter precision, every 6–12 days. What it doesn't give them, on its own, is a forecast a water manager can act on before the damage happens.
st-insar closes that gap with three design decisions that most subsidence-modeling pipelines skip:
- No naive resampling. Displacement (PS points, ~5 m) and hydrology (GRACE/ERA5, ~30 km+) live at wildly different resolutions. Instead of upsampling the coarse grid to fake spatial detail it doesn't have, st-insar builds a two-tier hierarchical graph — hydrology cells as super-nodes, PS points as leaf-nodes — and lets graph attention learn the coupling between scales.
- Physics as a loss term, not a post-hoc filter. A
TerzaghiPhysicsLosspenalizes the model, during training, for predicting uplift while groundwater storage is measurably depleting — the one direction effective-stress theory actually constrains. It does not penalize continued subsidence during recharge, since a large fraction of compaction in clay/silt aquitards is inelastic and permanent. - Uncertainty you can act on. Every forecast ships with both aleatoric uncertainty (from a Mixture Density Network head, which can represent genuinely multi-modal futures — "extraction continues" vs. "a moratorium kicks in") and epistemic uncertainty (from Monte Carlo Dropout), reported separately, not collapsed into one number.
How it works
InSAR (ps-gnn / pyunwrap) Optical (Clay / Prithvi) Hydrology (GRACE / ERA5 / SMAP / CHIRPS)
6–12 days · ~5 m ~5 days · ~10 m monthly · 30 km+
│ │ │
└──────────────┬──────────────┴──────────────┬──────────────┘
▼ ▼
hierarchical graph harmonizer (leaf-nodes ↔ parent super-nodes)
│
▼
┌──────────────────────────────────────────────────────────────┐
│ ST-GNN core │
│ TCN / LSTM / frozen-MLP encoders → GATv2 spatial coupling │
│ → explicit temporal attention → Mixture Density Network head │
└──────────────────────────────────────────────────────────────┘
│
trained under TerzaghiPhysicsLoss, spatial-block +
temporal-roll-forward CV, two-phase curriculum
│
▼
scenario "what-if" forecasting → explainability + calibration
(ONNX or native PyTorch, MC Dropout CIs) (temporal SHAP, coupling maps, Moran's I)
│
▼
automated HTML report
Each stage is its own module, independently usable:
| Module | Responsibility |
|---|---|
st_insar.data.harmonizer |
Multi-modal alignment onto a dynamic hierarchical graph; missing-data interpolation |
st_insar.models |
TCN / LSTM / frozen-MLP encoders, GATv2 + temporal attention core, MDN forecast head, TerzaghiPhysicsLoss |
st_insar.training |
Spatial-block + temporal-roll-forward CV, two-phase curriculum, AdamW/cosine training loop, CLI |
st_insar.inference |
Scenario "what-if" forecasting, Monte Carlo Dropout uncertainty, ONNX export/serving |
st_insar.analytics |
Temporal SHAP, spatial-attention coupling maps, spatio-temporal Moran's I, seasonal bias, calibration, HTML reporting |
st_insar.visualization |
Animated subsidence maps, 3D space-time cubes, forecast/SHAP/loss charts |
Validation sites
Three sites ship pre-configured, each with an independent ground-truth source the forecasts are checked against — not just internal cross-validation:
| Site | Ground truth | What makes it hard |
|---|---|---|
| Mexico City | UNAM continuous GNSS network | Some of the fastest subsidence rates on Earth (>300 mm/yr in places), highly non-linear urban extraction |
| Central Valley, California | USGS groundwater monitoring wells | Decades of intermittent, drought-driven pumping cycles; strong seasonal signal to separate from trend |
| Jakarta | Coastal tide gauge network | Subsidence compounding with sea-level rise; land and sea both moving |
Installation
git clone https://github.com/st-insar/st-insar.git
cd st-insar
pip install -e ".[dev]"
GPU-accelerated scatter/sparse ops (recommended for graphs beyond a few thousand nodes):
pip install -e ".[gpu]"
Quickstart
from st_insar.data.harmonizer import MultiModalHarmonizer, VALIDATION_SITES
site = VALIDATION_SITES["central_valley"]
harmonizer = MultiModalHarmonizer(site=site)
harmonizer.register_insar(ps_points_path="data/central_valley_ps.geojson")
harmonizer.register_hydrology(era5_path="data/era5_tws.nc", grace_path="data/grace_tws.nc")
harmonizer.register_optical_embeddings(embeddings_path="data/clay_embeddings.nc")
harmonizer.register_static(topo_path="data/dem.tif", geology_path="data/soil_type.tif")
graphs = harmonizer.build_dynamic_graph_sequence(start="2016-01-01", end="2020-12-31", freq="MS")
from st_insar.inference.forecaster import ScenarioForecaster
forecaster = ScenarioForecaster(schema=harmonizer.schema, checkpoint_path="checkpoints/best.pt")
result = forecaster.forecast(x_seq, edge_index, mc_samples=50) # mean, 95% CI, aleatoric + epistemic variance
# "What if extraction increases 20% over the next 6 months?"
scenario = ScenarioForecaster.scale_hydro_channel(x_seq, harmonizer.schema, factor=1.2, steps=6)
what_if = forecaster.forecast(scenario, edge_index)
Training from raw files, with the full spatial-block CV × curriculum pipeline, runs from the CLI:
st-insar-train \
--site central_valley \
--ps-points data/central_valley_ps.geojson \
--grace data/grace_tws.nc --era5 data/era5.nc \
--history-len 24 --horizon 12 \
--phase1-epochs 20 --phase2-epochs 40
Project layout
st_insar/
├── data/ harmonization: multi-modal alignment onto a dynamic hierarchical graph
├── models/ encoders, ST-GNN core (GAT + temporal attention + MDN), TerzaghiPhysicsLoss
├── training/ spatial-block + temporal-roll-forward CV, curriculum, trainer, CLI
├── inference/ scenario forecasting, MC Dropout uncertainty, ONNX export/serving
├── analytics/ temporal SHAP, coupling maps, Moran's I, calibration, HTML report generator
├── visualization/ animated maps, 3D space-time cubes, forecast/SHAP/loss charts
└── utils/ shared helpers
tests/ unit, model, leakage, ONNX, and end-to-end integration tests
assets/ logo source files
Testing
pytest tests/ -m "not slow" # unit, model, and leakage tests — a few seconds
pytest tests/ # add the full harmonize → train → forecast → report integration test
48 tests, 76% line coverage, zero mypy errors. The suite is unusually paranoid about two things on purpose: the leakage tests construct a mock dataset where the future has a deliberately different distribution than the past and assert, by inspecting actual tensor values, that no training window ever touched it; the physics tests assert the Terzaghi loss's asymmetry directly (penalized: uplift during depletion; not penalized: continued subsidence during recharge).
Roadmap
- Package scaffold,
pyproject.toml, multi-modal harmonizer - Modality encoders, GATv2 spatial coupling, temporal attention, MDN forecast head
-
TerzaghiPhysicsLoss, scenario "what-if" forecasting API, ONNX export - Spatial-block + temporal-roll-forward CV, two-phase curriculum, training CLI
- Temporal SHAP, spatial-attention coupling maps, spatio-temporal Moran's I, calibration
- Animated maps, 3D space-time cubes, automated HTML reporting
- Unit, model, leakage, ONNX, and integration test suite; CI
- Pretrained checkpoints for all three validation sites
- Direct
pygeofetchingestion (currently: bring your own harmonized files) - Multi-GPU / distributed training for continental-scale graphs
Related projects
st-insar is designed to sit downstream of two companion packages and, eventually, feed into a third:
ps-gnn— Persistent Scatterer identificationpyunwrap— AI-based InSAR phase unwrappingpygeofetch— multi-source Earth observation ingestion (planned integration)
Citation
If st-insar is useful in your research, please cite it:
@software{stinsar2026,
title = {st-insar: Physics-Informed Spatio-Temporal Deformation Forecasting},
author = {{st-insar contributors}},
year = {2026},
url = {https://github.com/st-insar/st-insar}
}
Contributing
Issues and pull requests are welcome. Before opening a PR: pytest tests/ -m "not slow", ruff check st_insar tests, and black st_insar tests should all be clean — CI runs the same checks, plus the full slow suite, on every push.
License
MIT — see LICENSE.
Metadata
Release files for st-insar 0.1.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 | |
|---|---|---|---|---|
| st_insar-0.1.1.tar.gz | 78.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| st_insar-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 148.1 kB
Release files / st_insar-0.1.1.tar.gz
| Download URL | st_insar-0.1.1.tar.gz |
|---|---|
| Size | 78.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3e02ef58b391cbb3172148c4405de211d81ef91ae795fa0c2a253b64865d0dfa
|
|
BLAKE2b-256 checksum How to use checksums |
d593fa9ec0c894a5385dfaffa5e1f470328015bc958b50735a88ac56938d91bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / st_insar-0.1.1-py3-none-any.whl
| Download URL | st_insar-0.1.1-py3-none-any.whl |
|---|---|
| Size | 69.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0ec85a74ad8dc518e4ed36095ff90f4ea3264d50cb15045c40f1b4ce67d97300
|
|
BLAKE2b-256 checksum How to use checksums |
3b86551473e3fd3d829de444c5fecd83ff44cde60f58b6780c67ace2b22b17bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Security for st-insar 0.1.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.