Metadata-Version: 2.4
Name: st-insar
Version: 0.1.1
Summary: Multi-Modal Spatio-Temporal Deformation Forecasting via a physics-informed ST-GNN (Terzaghi effective stress) fusing InSAR, GRACE/ERA5/SMAP hydrology, and optical foundation-model embeddings.
Author: st-insar contributors
License: MIT
Project-URL: Homepage, https://github.com/st-insar/st-insar
Project-URL: Documentation, https://st-insar.readthedocs.io
Project-URL: Repository, https://github.com/st-insar/st-insar
Project-URL: Issues, https://github.com/st-insar/st-insar/issues
Keywords: InSAR,land-subsidence,graph-neural-network,spatio-temporal-forecasting,physics-informed-machine-learning,hydrogeology,earth-observation
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: torch<3.0,>=2.2
Requires-Dist: torch-geometric>=2.5
Requires-Dist: xarray>=2024.3
Requires-Dist: rasterio>=1.3
Requires-Dist: geopandas>=0.14
Requires-Dist: shapely>=2.0
Requires-Dist: numpy>=1.26
Requires-Dist: pandas>=2.2
Requires-Dist: scikit-learn>=1.4
Requires-Dist: scipy>=1.12
Requires-Dist: shap>=0.45
Requires-Dist: plotly>=5.20
Requires-Dist: jinja2>=3.1
Requires-Dist: onnx>=1.15
Requires-Dist: onnxruntime>=1.17
Requires-Dist: folium>=0.16
Requires-Dist: branca>=0.7
Requires-Dist: netCDF4>=1.6
Requires-Dist: tensorboard>=2.16
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: pytest-mock>=3.14; extra == "dev"
Requires-Dist: black>=24.3; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: mypy>=1.9; extra == "dev"
Provides-Extra: gpu
Requires-Dist: torch-scatter; extra == "gpu"
Requires-Dist: torch-sparse; extra == "gpu"
Dynamic: license-file

<div align="center">

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="assets/logo-dark.svg">
  <img src="assets/logo.svg" alt="st-insar" width="520">
</picture>

<br>

## ⚠️ 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.

[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)](pyproject.toml)
[![Tests](https://github.com/st-insar/st-insar/actions/workflows/ci.yml/badge.svg)](.github/workflows/ci.yml)
[![Coverage](https://img.shields.io/badge/coverage-76%25-yellowgreen)](tests/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

</div>

---

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 `TerzaghiPhysicsLoss` penalizes 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

```bash
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):

```bash
pip install -e ".[gpu]"
```

## Quickstart

```python
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")
```

```python
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:

```bash
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

```bash
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

- [x] Package scaffold, `pyproject.toml`, multi-modal harmonizer
- [x] Modality encoders, GATv2 spatial coupling, temporal attention, MDN forecast head
- [x] `TerzaghiPhysicsLoss`, scenario "what-if" forecasting API, ONNX export
- [x] Spatial-block + temporal-roll-forward CV, two-phase curriculum, training CLI
- [x] Temporal SHAP, spatial-attention coupling maps, spatio-temporal Moran's I, calibration
- [x] Animated maps, 3D space-time cubes, automated HTML reporting
- [x] Unit, model, leakage, ONNX, and integration test suite; CI
- [ ] Pretrained checkpoints for all three validation sites
- [ ] Direct `pygeofetch` ingestion (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 identification
- **`pyunwrap`** — AI-based InSAR phase unwrapping
- **`pygeofetch`** — multi-source Earth observation ingestion (planned integration)

## Citation

If st-insar is useful in your research, please cite it:

```bibtex
@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](LICENSE).
