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.
Files changed (36) hide show
  1. st_insar-0.1.0/LICENSE +21 -0
  2. st_insar-0.1.0/PKG-INFO +275 -0
  3. st_insar-0.1.0/README.md +220 -0
  4. st_insar-0.1.0/pyproject.toml +107 -0
  5. st_insar-0.1.0/setup.cfg +4 -0
  6. st_insar-0.1.0/st_insar/__init__.py +25 -0
  7. st_insar-0.1.0/st_insar/analytics/__init__.py +28 -0
  8. st_insar-0.1.0/st_insar/analytics/explainability.py +254 -0
  9. st_insar-0.1.0/st_insar/analytics/report_generator.py +306 -0
  10. st_insar-0.1.0/st_insar/analytics/spatio_temporal_stats.py +304 -0
  11. st_insar-0.1.0/st_insar/data/__init__.py +0 -0
  12. st_insar-0.1.0/st_insar/data/harmonizer.py +751 -0
  13. st_insar-0.1.0/st_insar/inference/__init__.py +7 -0
  14. st_insar-0.1.0/st_insar/inference/forecaster.py +599 -0
  15. st_insar-0.1.0/st_insar/models/__init__.py +32 -0
  16. st_insar-0.1.0/st_insar/models/encoders.py +230 -0
  17. st_insar-0.1.0/st_insar/models/losses.py +213 -0
  18. st_insar-0.1.0/st_insar/models/st_gnn.py +462 -0
  19. st_insar-0.1.0/st_insar/training/__init__.py +37 -0
  20. st_insar-0.1.0/st_insar/training/trainer.py +861 -0
  21. st_insar-0.1.0/st_insar/utils/__init__.py +0 -0
  22. st_insar-0.1.0/st_insar/visualization/__init__.py +21 -0
  23. st_insar-0.1.0/st_insar/visualization/charts.py +130 -0
  24. st_insar-0.1.0/st_insar/visualization/maps.py +135 -0
  25. st_insar-0.1.0/st_insar/visualization/space_time_plots.py +107 -0
  26. st_insar-0.1.0/st_insar.egg-info/PKG-INFO +275 -0
  27. st_insar-0.1.0/st_insar.egg-info/SOURCES.txt +34 -0
  28. st_insar-0.1.0/st_insar.egg-info/dependency_links.txt +1 -0
  29. st_insar-0.1.0/st_insar.egg-info/entry_points.txt +2 -0
  30. st_insar-0.1.0/st_insar.egg-info/requires.txt +31 -0
  31. st_insar-0.1.0/st_insar.egg-info/top_level.txt +1 -0
  32. st_insar-0.1.0/tests/test_harmonizer.py +193 -0
  33. st_insar-0.1.0/tests/test_leakage.py +206 -0
  34. st_insar-0.1.0/tests/test_model.py +214 -0
  35. st_insar-0.1.0/tests/test_onnx.py +247 -0
  36. 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.
@@ -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: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
72
+ [![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)](pyproject.toml)
73
+ [![Tests](https://github.com/st-insar/st-insar/actions/workflows/ci.yml/badge.svg)](.github/workflows/ci.yml)
74
+ [![Coverage](https://img.shields.io/badge/coverage-76%25-yellowgreen)](tests/)
75
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](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).
@@ -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: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
17
+ [![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)](pyproject.toml)
18
+ [![Tests](https://github.com/st-insar/st-insar/actions/workflows/ci.yml/badge.svg)](.github/workflows/ci.yml)
19
+ [![Coverage](https://img.shields.io/badge/coverage-76%25-yellowgreen)](tests/)
20
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](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"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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__"]