Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

py-hypoellipse

Python reimplementation proof-of-concept of the HYPOELLIPSE local-earthquake locator, validated against the original Fortran 77 program as an oracle.

The goal of the PoC is to reproduce a Fortran HYPOELLIPSE location run within geophysical tolerance and structural equivalence — starting from the example/ run shipped with the oracle (3 Norcia-2016 events) and generalised to further reference datasets.

Status: the PoC is complete and generalises beyond the original example. The Python pipeline (read inputs -> weighted least-squares location -> write .arc/.sum) reproduces the example's 3 events within tolerance, with structural equivalence and 97/106 .arc lines byte-identical to the Fortran output. It has additionally been validated on two private reference datasets (107 further events, see Reference datasets below).

Magnitude is out of scope: coda/amplitude magnitude is computed by other software. The magnitude code path stays faithful to the Fortran (it emits a blank when the input carries no amplitude/coda data), but it is not a goal and not gated. The gate is a location gate.

Requirements

  • Docker (all builds, runs and tests happen inside containers — nothing on the host).
  • Git with submodule support.

Setup

The Fortran oracle (INGV/hypoellipse) is pinned as a git submodule under ext/hypoellipse. After cloning, initialise it:

git submodule update --init

Oracle and golden

The reference .arc/.sum outputs are not committed upstream in INGV/hypoellipse (only the inputs are). They are regenerated here by running the pristine Fortran oracle on the committed example inputs, and snapshotted into tests/golden/ as the versioned reference.

# 1. Build the oracle image from the pinned submodule source
bash scripts/oracle-build.sh

# 2. Regenerate and verify the golden (runs the oracle twice for a determinism
#    check, then diffs against tests/golden/; exits non-zero on any mismatch)
bash scripts/oracle-golden.sh

The Fortran location run is deterministic (ran3 uses a fixed seed), so the two runs are byte-identical and the snapshot is stable.

To intentionally re-baseline the golden (e.g. after bumping the oracle version):

GOLDEN_UPDATE=1 bash scripts/oracle-golden.sh

Python package: build, test, run

The Python code lives in src/pyhypoellipse/ and runs inside a dedicated poc container image — never on the host.

# Build the Python poc image (Python 3.12 + numpy + fortranformat + pytest)
bash scripts/poc-build.sh

# Run the test suite in the container (repo is live-mounted, so editing code
# needs no rebuild). Includes the primary gate: generated .arc/.sum vs golden.
bash scripts/test.sh
bash scripts/test.sh -v -k matches_golden   # extra pytest args pass through
                                            # (runs just the .arc/.sum gate)

# Run the pipeline end-to-end on the example inputs; writes the generated
# .arc/.sum into scratch/ (gitignored)
bash scripts/poc-run.sh

The primary gate (tests/test_writers.py) runs the whole pipeline and compares the generated .arc/.sum against tests/golden/ with numeric tolerance (src/pyhypoellipse/tolerances.py) plus structural equivalence (same stations, same weight codes). tests/test_multidataset_gate.py applies the same gate, event by event, to every reference dataset present.

Reference datasets

Beyond the example, the gate runs over any dataset placed under tests/datasets/<name>/{input,golden}/, discovered at collection time. Datasets are not published in this repository: the ones used so far consist of colleague-provided phase picks and station lists that are not ours to redistribute, so tests/datasets/*/ is gitignored and each dataset stays local to the machine that holds it. See tests/datasets/README.md for the expected layout. A fresh clone therefore runs the example gate only, and that is the intended behaviour, not a broken checkout.

Layout

Path Purpose
ext/hypoellipse Pinned Fortran oracle source (git submodule)
src/pyhypoellipse/ The Python reimplementation (readers, travel time, locate, writers, magnitude, comparator)
tests/ pytest suite; tests/golden/ holds the reference .arc/.sum
tests/datasets/ Extra reference datasets, local-only (gitignored)
docker/poc/ Dockerfile for the Python poc image
scripts/ Bash orchestration wrappers around docker build/run
pyproject.toml Package metadata and dependencies
scratch/ Throwaway container run dirs (gitignored)

Scope

This is a proof-of-concept, not a production replacement. It ports the location critical path exercised by the reference datasets; branches those datasets never reach (linear-gradient and travel-time-table velocity models, variable Vp/Vs models, scatter/RNG, station-calibration magnitude, azimuthal and boxcar weighting, ...) raise NotImplementedError rather than silently mis-handling input. First-motion plotting is out of scope.

Success is defined as a hybrid match rather than byte-exactness: gated numeric fields within geophysical tolerances (position ~0.1 km, depth ~0.5 km, origin time ~0.05 s, RMS ~0.02 s) plus structural equivalence. Byte-exactness is infeasible — the Fortran mixes single and double precision — and is tracked only as an observation.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages