⚠️ 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.
Contents
- Why this exists
- How it works
- Validation sites
- Installation
- Quickstart
- Project layout
- Testing
- Roadmap
- Related projects
- Citation
- Contributing
- License
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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file st_insar-0.1.0.tar.gz.
File metadata
- Download URL: st_insar-0.1.0.tar.gz
- Upload date:
- Size: 78.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f92a1376551fbc6681a6fc8dd9d88b789f474d81c48f22a91d001363d326af3e
|
|
| MD5 |
4dd99befe24c89eeaca102b078cb4ce8
|
|
| BLAKE2b-256 |
ed7f912b101725163fda7a27c886ead435325f997876f16a5b7b9425875d3c8f
|
File details
Details for the file st_insar-0.1.0-py3-none-any.whl.
File metadata
- Download URL: st_insar-0.1.0-py3-none-any.whl
- Upload date:
- Size: 70.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
313e07e09b4f0fe408d1013e526733e9c796c38d33340e139d49266de76ec56a
|
|
| MD5 |
0b4bcc2dc6563511dd89add266969f74
|
|
| BLAKE2b-256 |
a3a3d95d89a16f2a133d4567aee0d714098807e5a6bb5c4fe4eccc38bd9b2f38
|