st-insar 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- st_insar-0.1.0/LICENSE +21 -0
- st_insar-0.1.0/PKG-INFO +275 -0
- st_insar-0.1.0/README.md +220 -0
- st_insar-0.1.0/pyproject.toml +107 -0
- st_insar-0.1.0/setup.cfg +4 -0
- st_insar-0.1.0/st_insar/__init__.py +25 -0
- st_insar-0.1.0/st_insar/analytics/__init__.py +28 -0
- st_insar-0.1.0/st_insar/analytics/explainability.py +254 -0
- st_insar-0.1.0/st_insar/analytics/report_generator.py +306 -0
- st_insar-0.1.0/st_insar/analytics/spatio_temporal_stats.py +304 -0
- st_insar-0.1.0/st_insar/data/__init__.py +0 -0
- st_insar-0.1.0/st_insar/data/harmonizer.py +751 -0
- st_insar-0.1.0/st_insar/inference/__init__.py +7 -0
- st_insar-0.1.0/st_insar/inference/forecaster.py +599 -0
- st_insar-0.1.0/st_insar/models/__init__.py +32 -0
- st_insar-0.1.0/st_insar/models/encoders.py +230 -0
- st_insar-0.1.0/st_insar/models/losses.py +213 -0
- st_insar-0.1.0/st_insar/models/st_gnn.py +462 -0
- st_insar-0.1.0/st_insar/training/__init__.py +37 -0
- st_insar-0.1.0/st_insar/training/trainer.py +861 -0
- st_insar-0.1.0/st_insar/utils/__init__.py +0 -0
- st_insar-0.1.0/st_insar/visualization/__init__.py +21 -0
- st_insar-0.1.0/st_insar/visualization/charts.py +130 -0
- st_insar-0.1.0/st_insar/visualization/maps.py +135 -0
- st_insar-0.1.0/st_insar/visualization/space_time_plots.py +107 -0
- st_insar-0.1.0/st_insar.egg-info/PKG-INFO +275 -0
- st_insar-0.1.0/st_insar.egg-info/SOURCES.txt +34 -0
- st_insar-0.1.0/st_insar.egg-info/dependency_links.txt +1 -0
- st_insar-0.1.0/st_insar.egg-info/entry_points.txt +2 -0
- st_insar-0.1.0/st_insar.egg-info/requires.txt +31 -0
- st_insar-0.1.0/st_insar.egg-info/top_level.txt +1 -0
- st_insar-0.1.0/tests/test_harmonizer.py +193 -0
- st_insar-0.1.0/tests/test_leakage.py +206 -0
- st_insar-0.1.0/tests/test_model.py +214 -0
- st_insar-0.1.0/tests/test_onnx.py +247 -0
- st_insar-0.1.0/tests/test_pipeline.py +149 -0
st_insar-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 st-insar contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
st_insar-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: st-insar
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
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.
|
|
5
|
+
Author: st-insar contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/st-insar/st-insar
|
|
8
|
+
Project-URL: Documentation, https://st-insar.readthedocs.io
|
|
9
|
+
Project-URL: Repository, https://github.com/st-insar/st-insar
|
|
10
|
+
Project-URL: Issues, https://github.com/st-insar/st-insar/issues
|
|
11
|
+
Keywords: InSAR,land-subsidence,graph-neural-network,spatio-temporal-forecasting,physics-informed-machine-learning,hydrogeology,earth-observation
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: torch<3.0,>=2.2
|
|
26
|
+
Requires-Dist: torch-geometric>=2.5
|
|
27
|
+
Requires-Dist: xarray>=2024.3
|
|
28
|
+
Requires-Dist: rasterio>=1.3
|
|
29
|
+
Requires-Dist: geopandas>=0.14
|
|
30
|
+
Requires-Dist: shapely>=2.0
|
|
31
|
+
Requires-Dist: numpy>=1.26
|
|
32
|
+
Requires-Dist: pandas>=2.2
|
|
33
|
+
Requires-Dist: scikit-learn>=1.4
|
|
34
|
+
Requires-Dist: scipy>=1.12
|
|
35
|
+
Requires-Dist: shap>=0.45
|
|
36
|
+
Requires-Dist: plotly>=5.20
|
|
37
|
+
Requires-Dist: jinja2>=3.1
|
|
38
|
+
Requires-Dist: onnx>=1.15
|
|
39
|
+
Requires-Dist: onnxruntime>=1.17
|
|
40
|
+
Requires-Dist: folium>=0.16
|
|
41
|
+
Requires-Dist: branca>=0.7
|
|
42
|
+
Requires-Dist: netCDF4>=1.6
|
|
43
|
+
Requires-Dist: tensorboard>=2.16
|
|
44
|
+
Provides-Extra: dev
|
|
45
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
46
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
47
|
+
Requires-Dist: pytest-mock>=3.14; extra == "dev"
|
|
48
|
+
Requires-Dist: black>=24.3; extra == "dev"
|
|
49
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
50
|
+
Requires-Dist: mypy>=1.9; extra == "dev"
|
|
51
|
+
Provides-Extra: gpu
|
|
52
|
+
Requires-Dist: torch-scatter; extra == "gpu"
|
|
53
|
+
Requires-Dist: torch-sparse; extra == "gpu"
|
|
54
|
+
Dynamic: license-file
|
|
55
|
+
|
|
56
|
+
<div align="center">
|
|
57
|
+
|
|
58
|
+
<picture>
|
|
59
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/logo-dark.svg">
|
|
60
|
+
<img src="assets/logo.svg" alt="st-insar" width="520">
|
|
61
|
+
</picture>
|
|
62
|
+
|
|
63
|
+
<br>
|
|
64
|
+
|
|
65
|
+
## ⚠️ Important Note
|
|
66
|
+
|
|
67
|
+
This project is **actively under development**. While the core functionality
|
|
68
|
+
is production-ready and thoroughly tested, some advanced features are still
|
|
69
|
+
being refined.
|
|
70
|
+
|
|
71
|
+
[](LICENSE)
|
|
72
|
+
[](pyproject.toml)
|
|
73
|
+
[](.github/workflows/ci.yml)
|
|
74
|
+
[](tests/)
|
|
75
|
+
[](https://github.com/psf/black)
|
|
76
|
+
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
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.
|
|
82
|
+
|
|
83
|
+
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.
|
|
84
|
+
|
|
85
|
+
## Contents
|
|
86
|
+
|
|
87
|
+
- [Why this exists](#why-this-exists)
|
|
88
|
+
- [How it works](#how-it-works)
|
|
89
|
+
- [Validation sites](#validation-sites)
|
|
90
|
+
- [Installation](#installation)
|
|
91
|
+
- [Quickstart](#quickstart)
|
|
92
|
+
- [Project layout](#project-layout)
|
|
93
|
+
- [Testing](#testing)
|
|
94
|
+
- [Roadmap](#roadmap)
|
|
95
|
+
- [Related projects](#related-projects)
|
|
96
|
+
- [Citation](#citation)
|
|
97
|
+
- [Contributing](#contributing)
|
|
98
|
+
- [License](#license)
|
|
99
|
+
|
|
100
|
+
## Why this exists
|
|
101
|
+
|
|
102
|
+
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.
|
|
103
|
+
|
|
104
|
+
st-insar closes that gap with three design decisions that most subsidence-modeling pipelines skip:
|
|
105
|
+
|
|
106
|
+
- **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.
|
|
107
|
+
- **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.
|
|
108
|
+
- **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.
|
|
109
|
+
|
|
110
|
+
## How it works
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
InSAR (ps-gnn / pyunwrap) Optical (Clay / Prithvi) Hydrology (GRACE / ERA5 / SMAP / CHIRPS)
|
|
114
|
+
6–12 days · ~5 m ~5 days · ~10 m monthly · 30 km+
|
|
115
|
+
│ │ │
|
|
116
|
+
└──────────────┬──────────────┴──────────────┬──────────────┘
|
|
117
|
+
▼ ▼
|
|
118
|
+
hierarchical graph harmonizer (leaf-nodes ↔ parent super-nodes)
|
|
119
|
+
│
|
|
120
|
+
▼
|
|
121
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
122
|
+
│ ST-GNN core │
|
|
123
|
+
│ TCN / LSTM / frozen-MLP encoders → GATv2 spatial coupling │
|
|
124
|
+
│ → explicit temporal attention → Mixture Density Network head │
|
|
125
|
+
└──────────────────────────────────────────────────────────────┘
|
|
126
|
+
│
|
|
127
|
+
trained under TerzaghiPhysicsLoss, spatial-block +
|
|
128
|
+
temporal-roll-forward CV, two-phase curriculum
|
|
129
|
+
│
|
|
130
|
+
▼
|
|
131
|
+
scenario "what-if" forecasting → explainability + calibration
|
|
132
|
+
(ONNX or native PyTorch, MC Dropout CIs) (temporal SHAP, coupling maps, Moran's I)
|
|
133
|
+
│
|
|
134
|
+
▼
|
|
135
|
+
automated HTML report
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Each stage is its own module, independently usable:
|
|
139
|
+
|
|
140
|
+
| Module | Responsibility |
|
|
141
|
+
|---|---|
|
|
142
|
+
| `st_insar.data.harmonizer` | Multi-modal alignment onto a dynamic hierarchical graph; missing-data interpolation |
|
|
143
|
+
| `st_insar.models` | TCN / LSTM / frozen-MLP encoders, GATv2 + temporal attention core, MDN forecast head, `TerzaghiPhysicsLoss` |
|
|
144
|
+
| `st_insar.training` | Spatial-block + temporal-roll-forward CV, two-phase curriculum, AdamW/cosine training loop, CLI |
|
|
145
|
+
| `st_insar.inference` | Scenario "what-if" forecasting, Monte Carlo Dropout uncertainty, ONNX export/serving |
|
|
146
|
+
| `st_insar.analytics` | Temporal SHAP, spatial-attention coupling maps, spatio-temporal Moran's I, seasonal bias, calibration, HTML reporting |
|
|
147
|
+
| `st_insar.visualization` | Animated subsidence maps, 3D space-time cubes, forecast/SHAP/loss charts |
|
|
148
|
+
|
|
149
|
+
## Validation sites
|
|
150
|
+
|
|
151
|
+
Three sites ship pre-configured, each with an independent ground-truth source the forecasts are checked against — not just internal cross-validation:
|
|
152
|
+
|
|
153
|
+
| Site | Ground truth | What makes it hard |
|
|
154
|
+
|---|---|---|
|
|
155
|
+
| Mexico City | UNAM continuous GNSS network | Some of the fastest subsidence rates on Earth (>300 mm/yr in places), highly non-linear urban extraction |
|
|
156
|
+
| Central Valley, California | USGS groundwater monitoring wells | Decades of intermittent, drought-driven pumping cycles; strong seasonal signal to separate from trend |
|
|
157
|
+
| Jakarta | Coastal tide gauge network | Subsidence compounding with sea-level rise; land and sea both moving |
|
|
158
|
+
|
|
159
|
+
## Installation
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
git clone https://github.com/st-insar/st-insar.git
|
|
163
|
+
cd st-insar
|
|
164
|
+
pip install -e ".[dev]"
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
GPU-accelerated scatter/sparse ops (recommended for graphs beyond a few thousand nodes):
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
pip install -e ".[gpu]"
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Quickstart
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
from st_insar.data.harmonizer import MultiModalHarmonizer, VALIDATION_SITES
|
|
177
|
+
|
|
178
|
+
site = VALIDATION_SITES["central_valley"]
|
|
179
|
+
harmonizer = MultiModalHarmonizer(site=site)
|
|
180
|
+
|
|
181
|
+
harmonizer.register_insar(ps_points_path="data/central_valley_ps.geojson")
|
|
182
|
+
harmonizer.register_hydrology(era5_path="data/era5_tws.nc", grace_path="data/grace_tws.nc")
|
|
183
|
+
harmonizer.register_optical_embeddings(embeddings_path="data/clay_embeddings.nc")
|
|
184
|
+
harmonizer.register_static(topo_path="data/dem.tif", geology_path="data/soil_type.tif")
|
|
185
|
+
|
|
186
|
+
graphs = harmonizer.build_dynamic_graph_sequence(start="2016-01-01", end="2020-12-31", freq="MS")
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from st_insar.inference.forecaster import ScenarioForecaster
|
|
191
|
+
|
|
192
|
+
forecaster = ScenarioForecaster(schema=harmonizer.schema, checkpoint_path="checkpoints/best.pt")
|
|
193
|
+
result = forecaster.forecast(x_seq, edge_index, mc_samples=50) # mean, 95% CI, aleatoric + epistemic variance
|
|
194
|
+
|
|
195
|
+
# "What if extraction increases 20% over the next 6 months?"
|
|
196
|
+
scenario = ScenarioForecaster.scale_hydro_channel(x_seq, harmonizer.schema, factor=1.2, steps=6)
|
|
197
|
+
what_if = forecaster.forecast(scenario, edge_index)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Training from raw files, with the full spatial-block CV × curriculum pipeline, runs from the CLI:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
st-insar-train \
|
|
204
|
+
--site central_valley \
|
|
205
|
+
--ps-points data/central_valley_ps.geojson \
|
|
206
|
+
--grace data/grace_tws.nc --era5 data/era5.nc \
|
|
207
|
+
--history-len 24 --horizon 12 \
|
|
208
|
+
--phase1-epochs 20 --phase2-epochs 40
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Project layout
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
st_insar/
|
|
215
|
+
├── data/ harmonization: multi-modal alignment onto a dynamic hierarchical graph
|
|
216
|
+
├── models/ encoders, ST-GNN core (GAT + temporal attention + MDN), TerzaghiPhysicsLoss
|
|
217
|
+
├── training/ spatial-block + temporal-roll-forward CV, curriculum, trainer, CLI
|
|
218
|
+
├── inference/ scenario forecasting, MC Dropout uncertainty, ONNX export/serving
|
|
219
|
+
├── analytics/ temporal SHAP, coupling maps, Moran's I, calibration, HTML report generator
|
|
220
|
+
├── visualization/ animated maps, 3D space-time cubes, forecast/SHAP/loss charts
|
|
221
|
+
└── utils/ shared helpers
|
|
222
|
+
tests/ unit, model, leakage, ONNX, and end-to-end integration tests
|
|
223
|
+
assets/ logo source files
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Testing
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
pytest tests/ -m "not slow" # unit, model, and leakage tests — a few seconds
|
|
230
|
+
pytest tests/ # add the full harmonize → train → forecast → report integration test
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
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).
|
|
234
|
+
|
|
235
|
+
## Roadmap
|
|
236
|
+
|
|
237
|
+
- [x] Package scaffold, `pyproject.toml`, multi-modal harmonizer
|
|
238
|
+
- [x] Modality encoders, GATv2 spatial coupling, temporal attention, MDN forecast head
|
|
239
|
+
- [x] `TerzaghiPhysicsLoss`, scenario "what-if" forecasting API, ONNX export
|
|
240
|
+
- [x] Spatial-block + temporal-roll-forward CV, two-phase curriculum, training CLI
|
|
241
|
+
- [x] Temporal SHAP, spatial-attention coupling maps, spatio-temporal Moran's I, calibration
|
|
242
|
+
- [x] Animated maps, 3D space-time cubes, automated HTML reporting
|
|
243
|
+
- [x] Unit, model, leakage, ONNX, and integration test suite; CI
|
|
244
|
+
- [ ] Pretrained checkpoints for all three validation sites
|
|
245
|
+
- [ ] Direct `pygeofetch` ingestion (currently: bring your own harmonized files)
|
|
246
|
+
- [ ] Multi-GPU / distributed training for continental-scale graphs
|
|
247
|
+
|
|
248
|
+
## Related projects
|
|
249
|
+
|
|
250
|
+
st-insar is designed to sit downstream of two companion packages and, eventually, feed into a third:
|
|
251
|
+
|
|
252
|
+
- **`ps-gnn`** — Persistent Scatterer identification
|
|
253
|
+
- **`pyunwrap`** — AI-based InSAR phase unwrapping
|
|
254
|
+
- **`pygeofetch`** — multi-source Earth observation ingestion (planned integration)
|
|
255
|
+
|
|
256
|
+
## Citation
|
|
257
|
+
|
|
258
|
+
If st-insar is useful in your research, please cite it:
|
|
259
|
+
|
|
260
|
+
```bibtex
|
|
261
|
+
@software{stinsar2026,
|
|
262
|
+
title = {st-insar: Physics-Informed Spatio-Temporal Deformation Forecasting},
|
|
263
|
+
author = {{st-insar contributors}},
|
|
264
|
+
year = {2026},
|
|
265
|
+
url = {https://github.com/st-insar/st-insar}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Contributing
|
|
270
|
+
|
|
271
|
+
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.
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
MIT — see [LICENSE](LICENSE).
|
st_insar-0.1.0/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="assets/logo-dark.svg">
|
|
5
|
+
<img src="assets/logo.svg" alt="st-insar" width="520">
|
|
6
|
+
</picture>
|
|
7
|
+
|
|
8
|
+
<br>
|
|
9
|
+
|
|
10
|
+
## ⚠️ Important Note
|
|
11
|
+
|
|
12
|
+
This project is **actively under development**. While the core functionality
|
|
13
|
+
is production-ready and thoroughly tested, some advanced features are still
|
|
14
|
+
being refined.
|
|
15
|
+
|
|
16
|
+
[](LICENSE)
|
|
17
|
+
[](pyproject.toml)
|
|
18
|
+
[](.github/workflows/ci.yml)
|
|
19
|
+
[](tests/)
|
|
20
|
+
[](https://github.com/psf/black)
|
|
21
|
+
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
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.
|
|
27
|
+
|
|
28
|
+
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.
|
|
29
|
+
|
|
30
|
+
## Contents
|
|
31
|
+
|
|
32
|
+
- [Why this exists](#why-this-exists)
|
|
33
|
+
- [How it works](#how-it-works)
|
|
34
|
+
- [Validation sites](#validation-sites)
|
|
35
|
+
- [Installation](#installation)
|
|
36
|
+
- [Quickstart](#quickstart)
|
|
37
|
+
- [Project layout](#project-layout)
|
|
38
|
+
- [Testing](#testing)
|
|
39
|
+
- [Roadmap](#roadmap)
|
|
40
|
+
- [Related projects](#related-projects)
|
|
41
|
+
- [Citation](#citation)
|
|
42
|
+
- [Contributing](#contributing)
|
|
43
|
+
- [License](#license)
|
|
44
|
+
|
|
45
|
+
## Why this exists
|
|
46
|
+
|
|
47
|
+
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.
|
|
48
|
+
|
|
49
|
+
st-insar closes that gap with three design decisions that most subsidence-modeling pipelines skip:
|
|
50
|
+
|
|
51
|
+
- **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.
|
|
52
|
+
- **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.
|
|
53
|
+
- **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.
|
|
54
|
+
|
|
55
|
+
## How it works
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
InSAR (ps-gnn / pyunwrap) Optical (Clay / Prithvi) Hydrology (GRACE / ERA5 / SMAP / CHIRPS)
|
|
59
|
+
6–12 days · ~5 m ~5 days · ~10 m monthly · 30 km+
|
|
60
|
+
│ │ │
|
|
61
|
+
└──────────────┬──────────────┴──────────────┬──────────────┘
|
|
62
|
+
▼ ▼
|
|
63
|
+
hierarchical graph harmonizer (leaf-nodes ↔ parent super-nodes)
|
|
64
|
+
│
|
|
65
|
+
▼
|
|
66
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
67
|
+
│ ST-GNN core │
|
|
68
|
+
│ TCN / LSTM / frozen-MLP encoders → GATv2 spatial coupling │
|
|
69
|
+
│ → explicit temporal attention → Mixture Density Network head │
|
|
70
|
+
└──────────────────────────────────────────────────────────────┘
|
|
71
|
+
│
|
|
72
|
+
trained under TerzaghiPhysicsLoss, spatial-block +
|
|
73
|
+
temporal-roll-forward CV, two-phase curriculum
|
|
74
|
+
│
|
|
75
|
+
▼
|
|
76
|
+
scenario "what-if" forecasting → explainability + calibration
|
|
77
|
+
(ONNX or native PyTorch, MC Dropout CIs) (temporal SHAP, coupling maps, Moran's I)
|
|
78
|
+
│
|
|
79
|
+
▼
|
|
80
|
+
automated HTML report
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Each stage is its own module, independently usable:
|
|
84
|
+
|
|
85
|
+
| Module | Responsibility |
|
|
86
|
+
|---|---|
|
|
87
|
+
| `st_insar.data.harmonizer` | Multi-modal alignment onto a dynamic hierarchical graph; missing-data interpolation |
|
|
88
|
+
| `st_insar.models` | TCN / LSTM / frozen-MLP encoders, GATv2 + temporal attention core, MDN forecast head, `TerzaghiPhysicsLoss` |
|
|
89
|
+
| `st_insar.training` | Spatial-block + temporal-roll-forward CV, two-phase curriculum, AdamW/cosine training loop, CLI |
|
|
90
|
+
| `st_insar.inference` | Scenario "what-if" forecasting, Monte Carlo Dropout uncertainty, ONNX export/serving |
|
|
91
|
+
| `st_insar.analytics` | Temporal SHAP, spatial-attention coupling maps, spatio-temporal Moran's I, seasonal bias, calibration, HTML reporting |
|
|
92
|
+
| `st_insar.visualization` | Animated subsidence maps, 3D space-time cubes, forecast/SHAP/loss charts |
|
|
93
|
+
|
|
94
|
+
## Validation sites
|
|
95
|
+
|
|
96
|
+
Three sites ship pre-configured, each with an independent ground-truth source the forecasts are checked against — not just internal cross-validation:
|
|
97
|
+
|
|
98
|
+
| Site | Ground truth | What makes it hard |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| Mexico City | UNAM continuous GNSS network | Some of the fastest subsidence rates on Earth (>300 mm/yr in places), highly non-linear urban extraction |
|
|
101
|
+
| Central Valley, California | USGS groundwater monitoring wells | Decades of intermittent, drought-driven pumping cycles; strong seasonal signal to separate from trend |
|
|
102
|
+
| Jakarta | Coastal tide gauge network | Subsidence compounding with sea-level rise; land and sea both moving |
|
|
103
|
+
|
|
104
|
+
## Installation
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
git clone https://github.com/st-insar/st-insar.git
|
|
108
|
+
cd st-insar
|
|
109
|
+
pip install -e ".[dev]"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
GPU-accelerated scatter/sparse ops (recommended for graphs beyond a few thousand nodes):
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pip install -e ".[gpu]"
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Quickstart
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from st_insar.data.harmonizer import MultiModalHarmonizer, VALIDATION_SITES
|
|
122
|
+
|
|
123
|
+
site = VALIDATION_SITES["central_valley"]
|
|
124
|
+
harmonizer = MultiModalHarmonizer(site=site)
|
|
125
|
+
|
|
126
|
+
harmonizer.register_insar(ps_points_path="data/central_valley_ps.geojson")
|
|
127
|
+
harmonizer.register_hydrology(era5_path="data/era5_tws.nc", grace_path="data/grace_tws.nc")
|
|
128
|
+
harmonizer.register_optical_embeddings(embeddings_path="data/clay_embeddings.nc")
|
|
129
|
+
harmonizer.register_static(topo_path="data/dem.tif", geology_path="data/soil_type.tif")
|
|
130
|
+
|
|
131
|
+
graphs = harmonizer.build_dynamic_graph_sequence(start="2016-01-01", end="2020-12-31", freq="MS")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from st_insar.inference.forecaster import ScenarioForecaster
|
|
136
|
+
|
|
137
|
+
forecaster = ScenarioForecaster(schema=harmonizer.schema, checkpoint_path="checkpoints/best.pt")
|
|
138
|
+
result = forecaster.forecast(x_seq, edge_index, mc_samples=50) # mean, 95% CI, aleatoric + epistemic variance
|
|
139
|
+
|
|
140
|
+
# "What if extraction increases 20% over the next 6 months?"
|
|
141
|
+
scenario = ScenarioForecaster.scale_hydro_channel(x_seq, harmonizer.schema, factor=1.2, steps=6)
|
|
142
|
+
what_if = forecaster.forecast(scenario, edge_index)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Training from raw files, with the full spatial-block CV × curriculum pipeline, runs from the CLI:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
st-insar-train \
|
|
149
|
+
--site central_valley \
|
|
150
|
+
--ps-points data/central_valley_ps.geojson \
|
|
151
|
+
--grace data/grace_tws.nc --era5 data/era5.nc \
|
|
152
|
+
--history-len 24 --horizon 12 \
|
|
153
|
+
--phase1-epochs 20 --phase2-epochs 40
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Project layout
|
|
157
|
+
|
|
158
|
+
```
|
|
159
|
+
st_insar/
|
|
160
|
+
├── data/ harmonization: multi-modal alignment onto a dynamic hierarchical graph
|
|
161
|
+
├── models/ encoders, ST-GNN core (GAT + temporal attention + MDN), TerzaghiPhysicsLoss
|
|
162
|
+
├── training/ spatial-block + temporal-roll-forward CV, curriculum, trainer, CLI
|
|
163
|
+
├── inference/ scenario forecasting, MC Dropout uncertainty, ONNX export/serving
|
|
164
|
+
├── analytics/ temporal SHAP, coupling maps, Moran's I, calibration, HTML report generator
|
|
165
|
+
├── visualization/ animated maps, 3D space-time cubes, forecast/SHAP/loss charts
|
|
166
|
+
└── utils/ shared helpers
|
|
167
|
+
tests/ unit, model, leakage, ONNX, and end-to-end integration tests
|
|
168
|
+
assets/ logo source files
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Testing
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
pytest tests/ -m "not slow" # unit, model, and leakage tests — a few seconds
|
|
175
|
+
pytest tests/ # add the full harmonize → train → forecast → report integration test
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
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).
|
|
179
|
+
|
|
180
|
+
## Roadmap
|
|
181
|
+
|
|
182
|
+
- [x] Package scaffold, `pyproject.toml`, multi-modal harmonizer
|
|
183
|
+
- [x] Modality encoders, GATv2 spatial coupling, temporal attention, MDN forecast head
|
|
184
|
+
- [x] `TerzaghiPhysicsLoss`, scenario "what-if" forecasting API, ONNX export
|
|
185
|
+
- [x] Spatial-block + temporal-roll-forward CV, two-phase curriculum, training CLI
|
|
186
|
+
- [x] Temporal SHAP, spatial-attention coupling maps, spatio-temporal Moran's I, calibration
|
|
187
|
+
- [x] Animated maps, 3D space-time cubes, automated HTML reporting
|
|
188
|
+
- [x] Unit, model, leakage, ONNX, and integration test suite; CI
|
|
189
|
+
- [ ] Pretrained checkpoints for all three validation sites
|
|
190
|
+
- [ ] Direct `pygeofetch` ingestion (currently: bring your own harmonized files)
|
|
191
|
+
- [ ] Multi-GPU / distributed training for continental-scale graphs
|
|
192
|
+
|
|
193
|
+
## Related projects
|
|
194
|
+
|
|
195
|
+
st-insar is designed to sit downstream of two companion packages and, eventually, feed into a third:
|
|
196
|
+
|
|
197
|
+
- **`ps-gnn`** — Persistent Scatterer identification
|
|
198
|
+
- **`pyunwrap`** — AI-based InSAR phase unwrapping
|
|
199
|
+
- **`pygeofetch`** — multi-source Earth observation ingestion (planned integration)
|
|
200
|
+
|
|
201
|
+
## Citation
|
|
202
|
+
|
|
203
|
+
If st-insar is useful in your research, please cite it:
|
|
204
|
+
|
|
205
|
+
```bibtex
|
|
206
|
+
@software{stinsar2026,
|
|
207
|
+
title = {st-insar: Physics-Informed Spatio-Temporal Deformation Forecasting},
|
|
208
|
+
author = {{st-insar contributors}},
|
|
209
|
+
year = {2026},
|
|
210
|
+
url = {https://github.com/st-insar/st-insar}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Contributing
|
|
215
|
+
|
|
216
|
+
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.
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "st-insar"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "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."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "st-insar contributors" }
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"InSAR",
|
|
17
|
+
"land-subsidence",
|
|
18
|
+
"graph-neural-network",
|
|
19
|
+
"spatio-temporal-forecasting",
|
|
20
|
+
"physics-informed-machine-learning",
|
|
21
|
+
"hydrogeology",
|
|
22
|
+
"earth-observation",
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 3 - Alpha",
|
|
26
|
+
"Intended Audience :: Science/Research",
|
|
27
|
+
"License :: OSI Approved :: MIT License",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3.10",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Topic :: Scientific/Engineering :: GIS",
|
|
33
|
+
"Topic :: Scientific/Engineering :: Atmospheric Science",
|
|
34
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
dependencies = [
|
|
38
|
+
"torch>=2.2,<3.0",
|
|
39
|
+
"torch-geometric>=2.5",
|
|
40
|
+
"xarray>=2024.3",
|
|
41
|
+
"rasterio>=1.3",
|
|
42
|
+
"geopandas>=0.14",
|
|
43
|
+
"shapely>=2.0",
|
|
44
|
+
"numpy>=1.26",
|
|
45
|
+
"pandas>=2.2",
|
|
46
|
+
"scikit-learn>=1.4",
|
|
47
|
+
"scipy>=1.12",
|
|
48
|
+
"shap>=0.45",
|
|
49
|
+
"plotly>=5.20",
|
|
50
|
+
"jinja2>=3.1",
|
|
51
|
+
"onnx>=1.15",
|
|
52
|
+
"onnxruntime>=1.17",
|
|
53
|
+
"folium>=0.16",
|
|
54
|
+
"branca>=0.7",
|
|
55
|
+
"netCDF4>=1.6", # xarray's NetCDF read/write backend; not imported by name but required at runtime
|
|
56
|
+
"tensorboard>=2.16",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
[project.optional-dependencies]
|
|
60
|
+
dev = [
|
|
61
|
+
"pytest>=8.0",
|
|
62
|
+
"pytest-cov>=5.0",
|
|
63
|
+
"pytest-mock>=3.14",
|
|
64
|
+
"black>=24.3",
|
|
65
|
+
"ruff>=0.4",
|
|
66
|
+
"mypy>=1.9",
|
|
67
|
+
]
|
|
68
|
+
gpu = [
|
|
69
|
+
"torch-scatter",
|
|
70
|
+
"torch-sparse",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
[project.urls]
|
|
74
|
+
Homepage = "https://github.com/st-insar/st-insar"
|
|
75
|
+
Documentation = "https://st-insar.readthedocs.io"
|
|
76
|
+
Repository = "https://github.com/st-insar/st-insar"
|
|
77
|
+
Issues = "https://github.com/st-insar/st-insar/issues"
|
|
78
|
+
|
|
79
|
+
[project.scripts]
|
|
80
|
+
st-insar-train = "st_insar.training.trainer:main"
|
|
81
|
+
|
|
82
|
+
[tool.setuptools.packages.find]
|
|
83
|
+
include = ["st_insar*"]
|
|
84
|
+
exclude = ["tests*", "docs*"]
|
|
85
|
+
|
|
86
|
+
[tool.pytest.ini_options]
|
|
87
|
+
testpaths = ["tests"]
|
|
88
|
+
markers = [
|
|
89
|
+
"slow: marks tests as slow / integration-level (deselect with '-m \"not slow\"')",
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
[tool.black]
|
|
93
|
+
line-length = 100
|
|
94
|
+
target-version = ["py310", "py311", "py312"]
|
|
95
|
+
|
|
96
|
+
[tool.ruff]
|
|
97
|
+
line-length = 100
|
|
98
|
+
target-version = "py310"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
[tool.mypy]
|
|
103
|
+
python_version = "3.10"
|
|
104
|
+
ignore_missing_imports = true
|
|
105
|
+
disallow_untyped_defs = false
|
|
106
|
+
exclude = ["venv/", "tests/"]
|
|
107
|
+
follow_imports = "skip"
|
st_insar-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
st-insar
|
|
3
|
+
========
|
|
4
|
+
|
|
5
|
+
Multi-Modal Spatio-Temporal Deformation Forecasting.
|
|
6
|
+
|
|
7
|
+
`st-insar` fuses high-resolution InSAR displacement time-series with
|
|
8
|
+
coarse-resolution hydrological reanalysis (ERA5, GRACE, SMAP, CHIRPS) and
|
|
9
|
+
optical foundation-model embeddings (Clay / Prithvi) on a hierarchical
|
|
10
|
+
dynamic graph, and forecasts future land subsidence with a physics-informed
|
|
11
|
+
Spatio-Temporal Graph Neural Network (ST-GNN) that enforces Terzaghi's
|
|
12
|
+
principle of effective stress.
|
|
13
|
+
|
|
14
|
+
This package is designed to operate standalone, but its outputs (harmonized
|
|
15
|
+
graphs, forecasts) are shaped to plug directly into the `pygeofetch`
|
|
16
|
+
ingestion pipeline and to consume outputs from `ps-gnn` (PS identification)
|
|
17
|
+
and `pyunwrap` (AI phase unwrapping) upstream.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
__version__ = "0.1.0"
|
|
23
|
+
__author__ = "st-insar contributors"
|
|
24
|
+
|
|
25
|
+
__all__ = ["__version__"]
|