Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 28 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,25 @@ jobs:
- name: Install package and validation extras
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[validation,docs,dashboard,waveforms]"
python -m pip install -e ".[validation,docs,dashboard,waveforms,notebooks]"

- name: Run tests
run: |
MPLCONFIGDIR=/tmp/mplconfig_svtk python -m pytest -q

- name: Execute public waveform tutorial
if: matrix.python-version == '3.12'
env:
SVTK_NO_BASEMAP: "1"
MPLBACKEND: Agg
MPLCONFIGDIR: /tmp/mplconfig_svtk
run: python tools/execute_tutorial_notebooks.py --steps 1 2 3 4 5 6 7 --allow-missing-basemaps

- name: Exercise tutorial dashboards
if: matrix.python-version == '3.12'
run: |
MPLCONFIGDIR=/tmp/mplconfig_svtk python tools/smoke_tutorial_dashboards.py --metrics-root outputs/tutorials/dashboards/metrics_dashboard --summary-root outputs/tutorials/dashboards/dashboard_summaries --trace-summary outputs/tutorials/tables/qc_trace_summary.csv

- name: Compile source and tests
run: |
python -m compileall -q src tests
Expand Down Expand Up @@ -90,7 +103,7 @@ jobs:
- name: Install package and docs extras
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[validation,docs,dashboard,waveforms]"
python -m pip install -e ".[validation,docs,dashboard,waveforms,notebooks]"

- name: Install Pandoc
run: |
Expand All @@ -101,13 +114,17 @@ jobs:
run: |
MPLCONFIGDIR=/tmp/mplconfig_svtk python -m sphinx -W -b html docs docs/_build/html

- name: Check generated documentation search
run: node tools/check_documentation_search.mjs docs/_build/html

- name: Build source distribution and wheel
run: |
python -m build --sdist --wheel

- name: Check package metadata
run: |
python -m twine check dist/*
python tools/check_release_artifacts.py

- name: Inspect wheel contents
run: |
Expand All @@ -130,3 +147,12 @@ jobs:
raise SystemExit("Wheel does not contain spatial_vtk.")
print(f"checked {wheels[0].name}: {len(names)} files")
PY

- name: Verify base wheel outside checkout
run: |
SVTK_CHECKOUT="$PWD"
python -m venv "$RUNNER_TEMP/svtk-base"
"$RUNNER_TEMP/svtk-base/bin/python" -m pip install dist/*.whl
cd "$RUNNER_TEMP"
"$RUNNER_TEMP/svtk-base/bin/python" "$SVTK_CHECKOUT/tools/verify_base_installation.py" --snapshot "$SVTK_CHECKOUT/data/examples/data_formats/example_metrics_large_qc_passed.parquet" --output "$RUNNER_TEMP/svtk-base-proof.json"
"$RUNNER_TEMP/svtk-base/bin/svtk" --help
22 changes: 20 additions & 2 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
branches: ["main"]
paths:
- "docs/**"
- "data/**"
- "tools/**"
- "src/**"
- "README.md"
- "pyproject.toml"
Expand Down Expand Up @@ -36,16 +38,32 @@ jobs:
- name: Install package and documentation extras
run: |
python -m pip install --upgrade pip
python -m pip install -e ".[validation,docs,dashboard,waveforms]"
python -m pip install -e ".[validation,docs,dashboard,waveforms,notebooks]"

- name: Install Pandoc
run: |
sudo apt-get update
sudo apt-get install -y pandoc

- name: Restore tutorial imagery cache
uses: actions/cache@v4
with:
path: .cache/contextily
key: tutorial-esri-world-imagery-z8-v1

- name: Execute tutorials with required imagery
env:
SVTK_BASEMAP_CACHE: ${{ github.workspace }}/.cache/contextily
MPLBACKEND: Agg
MPLCONFIGDIR: /tmp/mplconfig_svtk
run: |
python tools/prepare_tutorial_basemap.py --cache-dir "$SVTK_BASEMAP_CACHE"
python tools/execute_tutorial_notebooks.py
python tools/prepare_documentation_preview.py --execution-root .

- name: Build docs with warnings as errors
run: |
MPLCONFIGDIR=/tmp/mplconfig_svtk python -m sphinx -W -b html docs docs/_build/html
MPLCONFIGDIR=/tmp/mplconfig_svtk python -m sphinx -W -b html outputs/documentation_preview/docs docs/_build/html

- name: Configure GitHub Pages
uses: actions/configure-pages@v5
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ jobs:
python-version: "3.11"
cache: "pip"

- name: Verify release tag matches package version
if: github.event_name == 'release'
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
python - <<'PYCODE'
import os, re
from pathlib import Path
version = re.search(r'^version = "([^"]+)"', Path("pyproject.toml").read_text(), re.M).group(1)
assert os.environ["RELEASE_TAG"] == f"v{version}", "Release tag/version mismatch"
PYCODE

- name: Install build tooling
run: |
python -m pip install --upgrade pip
Expand Down
24 changes: 20 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,26 @@ AGENTS.md
inputs/basemaps/
.DS_Store

# Compact example metadata can be versioned, but waveform binaries and
# processed copies stay local.
/data/examples/example_five_event_subset/observed/*.mseed
# Original tutorial MiniSEED inputs are versioned with checksums.
# Pickles and generated processed copies stay local.
/data/examples/example_five_event_subset/observed/*.pkl
/data/examples/example_five_event_subset/synthetics/
/data/examples/example_five_event_subset/processed/
/data/examples/example_five_event_subset/metadata/selected_event_stations_lowpass_1hz.csv

.venv/

# Whitelist only the ten public tutorial waveform inputs.
/data/examples/example_five_event_subset/observed/*.mseed
!/data/examples/example_five_event_subset/observed/ci38038071.mseed
!/data/examples/example_five_event_subset/observed/ci38695658.mseed
!/data/examples/example_five_event_subset/observed/ci39756418.mseed
!/data/examples/example_five_event_subset/observed/ci39812319.mseed
!/data/examples/example_five_event_subset/observed/ci40699207.mseed
/data/examples/example_five_event_subset/synthetics/*
!/data/examples/example_five_event_subset/synthetics/cvmsi_20260506_material_0p6x1p2_asdf/
/data/examples/example_five_event_subset/synthetics/cvmsi_20260506_material_0p6x1p2_asdf/*
!/data/examples/example_five_event_subset/synthetics/cvmsi_20260506_material_0p6x1p2_asdf/ci38038071.mseed
!/data/examples/example_five_event_subset/synthetics/cvmsi_20260506_material_0p6x1p2_asdf/ci38695658.mseed
!/data/examples/example_five_event_subset/synthetics/cvmsi_20260506_material_0p6x1p2_asdf/ci39756418.mseed
!/data/examples/example_five_event_subset/synthetics/cvmsi_20260506_material_0p6x1p2_asdf/ci39812319.mseed
!/data/examples/example_five_event_subset/synthetics/cvmsi_20260506_material_0p6x1p2_asdf/ci40699207.mseed
2 changes: 1 addition & 1 deletion CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors:
- family-names: Birkel
given-names: Brianna
email: birkel@usc.edu
version: 0.1.0
version: 0.1.4
license: BSD-3-Clause
repository-code: "https://github.com/bcbirkel/spatial-vtk"
abstract: "spatial-vtk provides spatial validation tools for ground-motion simulations, with data QC, residual and metric calculations, geologic metadata integration, spatial statistics, extensive mapping capabilities, and interactive dashboards for quantifying and visualizing model performance patterns."
Expand Down
16 changes: 14 additions & 2 deletions DATA_PROVENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ The tutorial dataset is a five-event LA Basin subset under
`data/examples/example_five_event_subset/`. It includes event metadata, station
metadata, event-station tables, GeoJSON regions, event JSON files, and local
paths for observed and synthetic MiniSEED files used by the tutorial notebooks.
The public repository keeps the lightweight metadata in git; large waveform
binaries and processed waveform copies stay local.
The repaired source checkout includes ten original MiniSEED inputs with SHA-256
checksums in `metadata/waveform_manifest.json`. Step 1 generates processed copies
under `outputs/`. Neither the wheel nor the PyPI source distribution includes
example data. No additional research data or preprocessed copies are distributed.

The example events are southern California earthquakes with public USGS event
pages:
Expand All @@ -22,6 +24,16 @@ pages:
- `ci39756418`: M 4.2, 3 km SW of San Bernardino, CA, 2024-01-25.
- `ci40699207`: M 4.4, 4 km SSE of Highland Park, CA, 2024-08-12.

## Physical units

On 8 September 2026, the dataset owner confirmed that both observed and
synthetic example waveforms are acceleration in cm/s² (cm/s/s). The examples
use these stored units directly; no factor-of-100 conversion is applied.
This declaration supplements the MiniSEED headers, which do not independently
encode the full unit/conversion history. Derived velocity and displacement
use cm/s and cm; acceleration PSA uses cm/s². Ln amplitude residuals are
dimensionless.

## Observed Records

Observed records in the public examples are derived from public southern
Expand Down
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,36 @@ The package imports as `spatial_vtk` and installs the `svtk` command:
See the [public documentation](https://bcbirkel.github.io/spatial-vtk/) for
installation, package overview, examples, API reference, support, and changelog
pages.

## Reproducible source tutorials (unreleased 0.1.4rc1)

This research/alpha package is under active validation. The existing PyPI 0.1.3
release does not contain these repairs. From this repaired source checkout:

```bash
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install ".[notebooks,waveforms]"
export SVTK_NO_BASEMAP=1
python tools/execute_tutorial_notebooks.py
```

The source example bundle includes ten original MiniSEED inputs, five events,
and 30 selected stations, with checksums in `data/examples/`. Examples are not
included in the wheel. Step 1 generates processed waveforms; Step 2 performs QC;
Step 3 calculates metrics and native ln(observed / synthetic) residuals.
Steps 4–7 use those generated results; run Steps 1–3 first.
See the tutorial index and CLI workflow for launch commands.

Notebooks find the checkout from its root or `docs/examples`. For downloaded
notebooks set `SVTK_PROJECT_ROOT` to the complete example checkout. Unset
`SVTK_NO_BASEMAP` to request Esri World Imagery backgrounds.

Base metric/statistics workflows do not require an arrival picker. PhaseNet is
an optional external TensorFlow installation with an explicit command and model;
see [the integration contract](docs/phasenet.rst). The PyPI package named
`phasenet` has a different interface and is not installed by Spatial-VTK.

CI runs unit tests on Python 3.10–3.12 and executes the complete waveform-to-QC-
to-metrics tutorial on Python 3.12. Sphinx builds documentation without executing
notebooks; the notebook runner retains execution evidence separately.
53 changes: 53 additions & 0 deletions RELEASE_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Release checklist

This checklist applies to the public package, Python 3.10–3.12. Keep the
statistics-module proposal out of this release.

1. Check the staged public diff and version agreement in pyproject.toml,
src/spatial_vtk/__init__.py and CITATION.cff. Keep generated outputs, private
plans, caches and local paths out of the commit. Record the final commit.
2. Install the validation, docs, dashboard, notebooks and waveforms extras.
Run from the checkout:
```sh
PYTHONPATH=src MPLCONFIGDIR=/tmp/mplconfig_svtk python -m pytest -q
PYTHONPYCACHEPREFIX=/tmp/svtk-pycache python -m compileall -q src tests tools
PYTHONPATH=src MPLCONFIGDIR=/tmp/mplconfig_svtk python -m sphinx -W -b html docs docs/_build/html
node tools/check_documentation_search.mjs docs/_build/html
python -m build --sdist --wheel
python -m twine check dist/*
python tools/check_release_artifacts.py
```
3. Install the built wheel with dependencies in a new virtual environment.
Confirm imports resolve to that environment's site-packages, not src.
Test CLI help/version and all seven tutorials in a clean copy of the
example data/docs/tools, with no PYTHONPATH override:
```sh
unset SVTK_NO_BASEMAP
export MPLBACKEND=Agg
python tools/execute_tutorial_notebooks.py
```
Final reviewed figures require successful Esri imagery. Only computation CI
may use SVTK_NO_BASEMAP=1 and --allow-missing-basemaps.
4. Use tools/smoke_tutorial_dashboards.py on the resulting dashboard tables
and QC trace summary. Check empty and restored model/passband selections,
filters, application exceptions and localhost health. Inspect browser
behavior separately; application tests do not certify a clean browser console.
5. Run tools/prepare_documentation_preview.py --execution-root <validated-project>
and build Sphinx from outputs/documentation_preview/docs for output-bearing
tutorial pages. The Pages workflow executes all seven tutorials with required
cached Esri imagery before preparing these pages. Verify the docs reference sidebar, search results and result links.
Sphinx rendering does not execute notebook cells. Keep hosted-browser
evidence separate from local-file JavaScript tests.
6. Review unit/data statements in DATA_PROVENANCE.md. The owner confirmed both
example waveform populations are acceleration in cm/s² on 2026-09-08.
External PhaseNet inference remains separately unverified and optional.
7. Push a review branch and obtain passing CI on its exact commit, including
Python 3.10, 3.11 and 3.12, tutorials, docs and distribution checks. Confirm
GitHub Pages and the testpypi/pypi Trusted Publisher environments.
8. Keep 0.1.4 for the candidate unless deliberately changing the version.
Rebuild and revalidate if the code or version changes. Do not overwrite a
previously published version. Publishing a GitHub Release triggers PyPI,
including prereleases; manual Release dispatch defaults to TestPyPI.
9. After final publication authorization, merge/deploy docs, publish the exact
matching tag, verify a fresh public clone and PyPI install, and check Pages
search, links and README badges. Preserve final artifact hashes and logs.
19 changes: 13 additions & 6 deletions data/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ outputs. The example files are not included in the PyPI wheel.
## Included Example Bundle

`example_five_event_subset/` contains the five-event LA Basin tutorial bundle:
event metadata, station metadata, event-station tables, GeoJSON regions, event
JSON files, and local paths for the observed and synthetic MiniSEED products
used by the workflow. The public repository keeps the lightweight metadata in
git; large waveform binaries and processed waveform copies stay local. The
tutorial configuration applies a 1 Hz lowpass preprocessing step before QC,
metric calculations, waveform figures, spatial analysis, and dashboards.
event metadata, 30 selected stations, GeoJSON regions, and ten original MiniSEED
files (five observed and five CVM-SI synthetic files, about 245 MB total).
These inputs retain all original samples and Z/R/T components; no fabricated
waveforms or previously processed copies are substituted. The existing five-event
subset is retained to preserve regional/corridor coverage and documented missing
station records. Step 1 generates the 1 Hz lowpass products under `outputs/`.

`metadata/waveform_manifest.json` lists every distributed waveform's byte count
and SHA-256. Run `python -c 'from spatial_vtk.tutorials import verify_waveforms;
verify_waveforms()'` from the source checkout before starting. Obtain the files
from the matching source checkout; no separately hosted bundle URL is required.
This repair is not available from public main until the maintainer publishes it.


`data_formats/` contains tiny preview inputs and outputs used by the Data
Formats page. These examples show file structure and column conventions; they
Expand Down
23 changes: 11 additions & 12 deletions data/examples/configuration/example_spatial_vtk_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ metrics:

# Transform options: residual, log2_residual, ln_residual,
# anderson_2004_gof, olsen_mayhew_gof.
transforms: [log2_residual, anderson_2004_gof]
transforms: [ln_residual, anderson_2004_gof]

# Output mode options: observed, synthetic, residual, gof, full.
output_mode: full
Expand All @@ -110,7 +110,7 @@ metrics:

spectral:
# Periods, in seconds, where PSA/FAS values should be stored.
periods_s: [1.0, 2.0, 3.0, 5.0]
periods_s: [1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
# A period passes spectral QC only if its amplitude is at least this
# fraction of the maximum supported spectral amplitude.
relative_amplitude_threshold: 0.25
Expand Down Expand Up @@ -162,8 +162,8 @@ qc:
run_scenarios:
tutorial:
# This scenario expects the companion five-event LA Basin waveform bundle
# under data/examples/example_five_event_subset/. The repo keeps the
# metadata tables in git and ignores the large MiniSEED products.
# under data/examples/example_five_event_subset/. Original inputs and their
# checksums are versioned; Step 1 generates processed copies under outputs/.
paths:
observed_root: "{root_dir}/data/examples/example_five_event_subset/observed"
synthetic_root: "{root_dir}/data/examples/example_five_event_subset/synthetics/cvmsi_20260506_material_0p6x1p2_asdf"
Expand All @@ -174,7 +174,6 @@ run_scenarios:
site_metadata: "{root_dir}/data/examples/data_formats/example_site_metadata.csv"
region_geojson: "{root_dir}/data/examples/example_five_event_subset/metadata/example_path_regions.geojson"
metric_snapshot: "{root_dir}/data/examples/data_formats/example_metrics_snapshot.csv"
metric_figure_snapshot: "{root_dir}/data/examples/data_formats/example_metrics_large_qc_passed.parquet"
outputs:
tutorials_root: "{root_dir}/outputs/tutorials"
root: "{root_dir}/outputs/tutorials"
Expand All @@ -184,15 +183,15 @@ run_scenarios:
dashboards: "{root_dir}/outputs/tutorials/dashboards"
metrics:
groups: [amplitude, spectral]
transforms: [log2_residual, anderson_2004_gof]
transforms: [ln_residual, anderson_2004_gof]
output_mode: full
components: [Z, R, T]
models: [cvmsi_20260506_material_0p6x1p2_asdf]
passbands:
- [1, 2]
- [2, 3]
spectral:
periods_s: [1.0, 2.0, 3.0, 5.0]
periods_s: [1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
synthetics:
max_frequency_hz: 1.0
waveforms:
Expand All @@ -210,8 +209,8 @@ run_scenarios:
snr_threshold: 3.0
spatial:
metric: all
field_mode: log2_residual
value_column: log2_residual
field_mode: ln_residual
value_column: ln_residual
min_stations_per_event: 2
min_events_per_station: 1
moran_neighbors: 2
Expand All @@ -234,13 +233,13 @@ run_scenarios:
quick_amplitude_check:
metrics:
groups: [amplitude]
transforms: [log2_residual]
transforms: [ln_residual]
outputs:
metrics: outputs/metrics/quick_amplitude_metrics.parquet

spectral_period_review:
metrics:
metrics: [PSA, FAS]
transforms: [log2_residual, anderson_2004_gof]
transforms: [ln_residual, anderson_2004_gof]
spectral:
periods_s: [1.0, 2.0, 3.0, 5.0, 7.5]
periods_s: [1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]
Loading
Loading