# HOMEROS demo — ML-based earthquake detection over large seismic datasets

[![Notebook](https://img.shields.io/badge/Jupyter-notebook-F37626?logo=jupyter&logoColor=white)](notebooks/HOMEROS_demo_large_dataset.ipynb)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ORG/REPO/blob/main/notebooks/HOMEROS_demo_large_dataset.ipynb)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)


An open, end-to-end Jupyter notebook that builds an earthquake catalogue from continuous
seismic waveforms, using machine-learning phase picking. It starts from nothing but a station
list and a date range, downloads open FDSN data, picks P and S arrivals with Phasenet,
associates them into events, estimates local magnitudes, and exports a catalogue, a map and a
phase file ready for double-difference relocation.

The demo region is the **central Ionian Islands area**, one of the study
areas of the [HOMEROS](https://oscars-project.eu/projects/homeros-harmonising-observations-multi-hazard-environments-research-open-science)
project (*Harmonising Observations from Multi-hazard Environments in Research for Open Science*).
Nothing in the workflow is specific to that region — the search area, station list and date range
are all parameters in a single configuration cell.

<p align="center">
  <img src="docs/assets/detected_events_map.png" width="620" alt="Map of detected events and stations in the central Ionian Islands"><br>
  <em>Events detected by the demo run (colour = depth, size = ML) and the stations used (red triangles).</em>
</p>

---

## What the notebook does

<p align="center">
  <img src="docs/assets/workflow.svg" width="820" alt="Diagram of the six processing stages of the workflow">
</p>

The workflow is organised as a **daily batch pipeline** so it scales to long time spans without
running out of memory. Each stage below corresponds to a section of the notebook.

**1 · Station metadata.** A response-level `StationXML` inventory is requested from the configured
FDSN providers (NOA, then EIDA as fallback). Wildcard channel requests are expanded against the
inventory into exact three-component (E/N/Z) triplets, so only channels that actually exist are
requested. The inventory is cached on disk and reused on later runs.

**2 · Daily waveform download.** For each day, one folder `YYYYMMDD/` is created and filled with
one full-day (86 400 s) MiniSEED file per component. Each trace is merged, padded, resampled to a
common sampling rate, detrended, and **instrument-response-corrected** at download
time. Downloads run in a thread pool, fall back across providers, skip files that already exist,
and record the outcome of every request in a per-day JSON manifest. Because the response is
removed up front, every later stage reads ready-to-use physical units from disk.

**3 · Phase picking.** [PhaseNet](https://doi.org/10.1093/gji/ggy423) is applied through
[SeisBench](https://doi.org/10.1785/0220210324) using the pre-trained **INSTANCE** weights. The
network outputs continuous P and S probability traces, which are converted to discrete picks at a
configurable probability threshold. A CUDA device is used automatically when one is available.

**4 · Association.** Picks are grouped into events with
[PyOcto](https://doi.org/10.26443/seismica.v3i1.1130), using 4D space–time partitioning over the
configured latitude/longitude/depth volume and a homogeneous velocity model. Association is run
day by day, on a station table filtered to the stations that actually produced picks that day, with
conservative time-slicing and threading settings that keep memory use predictable inside a
notebook.

**5 · Local magnitude.** For each event, the horizontal components of each contributing station are
simulated to a Wood–Anderson instrument, the peak amplitude is measured in a 3 s window anchored on
the S pick (or offset from the P pick when S is missing), and a station ML is computed with the
[Hutton & Boore (1987)](https://doi.org/10.1785/BSSA0770062074) distance correction. The event
magnitude is the median of the station values.

**6 · Figures and export.** The notebook maps epicentres and stations with Cartopy, plots associated
picks per station, and writes a **hypoDD-format phase file** ready for double-difference relocation.

---

## Demo run

The notebook ships with the outputs of a two-day run over 23 requested stations from networks
HT, HL, HP and HA, on 1–2 March 2026:

| Day | Traces | PhaseNet picks | Associated events | Events with ML |
|---|---|---|---|---|
| 2026-03-01 | 50 | 1 671 | 10 | 10 |
| 2026-03-02 | 52 | 1 823 | 18 | 18 |
| **Total** | — | **3 494** | **28** | **28** |

<p align="center">
  <img src="docs/assets/picks_per_station.png" width="720" alt="Bar chart of associated P and S picks per station">
</p>

Two days is a demonstration size. The same notebook runs unchanged over months of data by
changing `NDAYS`; the per-day loop releases waveform memory and clears the GPU cache between
days.

---

## Quick start

### Google Colab

Open the badge at the top of this page. The first cell installs the dependencies. A GPU runtime
is recommended but not required — the notebook falls back to CPU.



# conda (recommended — ObsPy and Cartopy pull in compiled geospatial libraries)
conda env create -f environment.yml
conda activate homeros-demo

# or pip, into a clean virtual environment
pip install -r requirements.txt

jupyter lab notebooks/HOMEROS_demo_large_dataset.ipynb
```

Then run the cells top to bottom. The only cell that normally needs editing is the
**"Parameters (edit me)"** cell.

---

## Configuration

All settings live in one cell near the top of the notebook.

| Parameter | Demo value | Meaning |
|---|---|---|
| `START_DATE`, `NDAYS` | 2026-03-01, 2 | First day and number of days to process |
| `PROVIDERS` | `["NOA", "EIDA"]` | FDSN providers, tried in order |
| `networks`, `stations`, `channel` | `HT,HL,HP,HA` · 23 codes · `HH?` | Station selection; wildcards are expanded via the inventory |
| `SAMPLING_RATE` | 100 Hz | Common sampling rate for all traces |
| `pre_filt`, `water_level`, `resp_output` | `[0.001, 0.002, 25, 30]`, 60, `VEL` | Instrument-response removal |
| `MAX_DOWNLOAD_THREADS`, `USE_CACHE` | 8, `True` | Parallel downloads; skip files already on disk |
| `BATCH_SIZE` | 128 | PhaseNet inference batch size |
| `P_THRESHOLD`, `S_THRESHOLD` | 0.30, 0.30 | Probability thresholds for accepting a pick |
| `LAT_BOUNDS`, `LON_BOUNDS`, `ZLIM_KM` | 37.5–39.0 °N, 20.0–22.0 °E, 0–100 km | Association search volume |
| `N_PICKS`, `N_P_AND_S_PICKS` | 8, 4 | Minimum picks / minimum P-and-S stations per event |
| `P_VELOCITY`, `S_VELOCITY`, `TOLERANCE_S` | 6.2, 3.3 km/s, 2.0 s | Homogeneous velocity model and travel-time tolerance |
| `ASSOCIATION_TIME_SLICING_S`, `ASSOCIATION_THREADS` | 600 s, 1 | PyOcto runtime controls; keep low in notebooks |
| `PROCESS_ALL_DAYS`, `STOP_ON_DAY_ERROR` | `True`, `False` | Process every day folder; continue past a failing day |

### Adapting it to another region

1. Set `LAT_BOUNDS`, `LON_BOUNDS` and `ZLIM_KM` to your search volume.
2. Replace `networks` / `stations`, and put the FDSN node that serves them first in `PROVIDERS`.
3. Set `START_DATE` and `NDAYS`.
4. Replace `P_VELOCITY` / `S_VELOCITY` with values appropriate for your crust, and consider a
   1D model (`pyocto.VelocityModel1D`) instead of the homogeneous one used here.
5. If your region has a locally calibrated ML scale, replace the distance-correction coefficients
   in `station_ml_calc_mag_phase`.

---

## Outputs

Everything is written under `outputs/`

```
outputs/
├── waveforms_daily/
│   ├── metadata/stations.xml           # cached response-level inventory
│   ├── manifests/YYYYMMDD.json         # per-request download outcome
│   └── YYYYMMDD/NET.STA.CHA            # full-day, response-corrected MiniSEED
└── results_daily/
    ├── YYYYMMDD/
    │   ├── picks.csv                   # per-day picks
    │   ├── assignments.csv             # per-day pick-to-event assignments
    │   └── events_with_magnitude.csv   # per-day catalogue
    ├── all_picks.csv                   # merged across all days
    ├── all_assignments.csv
    ├── all_events_with_magnitude.csv   # the final catalogue
    ├── processing_summary.csv          # traces / picks / events per day
    ├── magnitude_summary.csv
    ├── detected_events_map.png
    └── phases_hypodd.dat               # hypoDD phase file
```

The final catalogue has one row per event: `day`, `event_uid`, `datetime` (UTC),
`latitude`, `longitude`, `depth` (km), `ML`, and `Stations_used`.

---

## Scope and caveats

This is a **teaching and demonstration workflow**, based on a catalogue pipeline. Before
using any of its output in a scientific result, be aware that:

- **Hypocentres are association-grade.** PyOcto locations come from a homogeneous velocity model
  and are meant to be good enough to group picks, not to be final. They are a starting point for
  proper location or relocation — hence the hypoDD export.
- **Magnitudes are approximate.** The ML implementation uses a southern-California distance
  correction and no station corrections, and does not apply a distance or SNR cut-off. Treat the
  values as indicative.
- **Detection completeness depends on thresholds.** `P_THRESHOLD`, `S_THRESHOLD`, `N_PICKS` and
  `N_P_AND_S_PICKS` control the trade-off between missed events and false associations, and were
  not tuned for this region.
- **Results depend on data availability.** Traces are fetched live from FDSN services, so a rerun
  can differ if a station's data or metadata have changed. The download manifests record exactly
  what was retrieved.
- **hypoDD phase weights are placeholders** — a flat 1.0 for P and 0.5 for S, not derived from
  pick quality. Location uncertainties in the event headers are written as zero.

---

## Requirements

Python ≥ 3.9 with `seisbench`, `pyocto`, `obspy`, `torch`, `cartopy`, `numpy`, `pandas` and
`matplotlib`. See [`requirements.txt`](requirements.txt) or [`environment.yml`](environment.yml).

A CUDA GPU speeds up picking considerably but is optional. The demo run used a GTX 1650 and
processed a day of 50 traces in a few minutes. Disk usage is roughly 1 GB per day for ~50
three-component stations at 100 Hz.

---

## How to cite

If you use this workflow, please cite the archived release (see [`CITATION.cff`](CITATION.cff))
together with the underlying software and data sources listed below.

```
Anagnostou, V., Papadimitriou, E. & Karakostas, V. (2026). HOMEROS demo: ML-based earthquake detection over large seismic datasets
(Version 1.0) [Computer software]. Zenodo. https://doi.org/10.5281/zenodo.22745993
```

### References

- Woollam, J., Münchmeyer, J., Tilmann, F., *et al.* (2022). SeisBench — A toolbox for machine
  learning in seismology. *Seismological Research Letters*, 93(3), 1695–1709.
  https://doi.org/10.1785/0220210324
- Zhu, W., & Beroza, G. C. (2019). PhaseNet: a deep-neural-network-based seismic arrival-time
  picking method. *Geophysical Journal International*, 216(1), 261–273.
  https://doi.org/10.1093/gji/ggy423
- Michelini, A., Cianetti, S., Gaviano, S., *et al.* (2021). INSTANCE – the Italian seismic dataset
  for machine learning. *Earth System Science Data*, 13, 5509–5544.
  https://doi.org/10.5194/essd-13-5509-2021
- Münchmeyer, J. (2024). PyOcto: a high-throughput seismic phase associator. *Seismica*, 3(1).
  https://doi.org/10.26443/seismica.v3i1.1130
- Beyreuther, M., Barsch, R., Krischer, L., *et al.* (2010). ObsPy: a Python toolbox for seismology.
  *Seismological Research Letters*, 81(3), 530–533. https://doi.org/10.1785/gssrl.81.3.530
- Waldhauser, F., & Ellsworth, W. L. (2000). A double-difference earthquake location algorithm.
  *Bulletin of the Seismological Society of America*, 90(6), 1353–1368.
  https://doi.org/10.1785/0120000006
- Hutton, L. K., & Boore, D. M. (1987). The ML scale in southern California. *Bulletin of the
  Seismological Society of America*, 77(6), 2074–2094. https://doi.org/10.1785/BSSA0770062074
- Met Office (2010–2015). *Cartopy: a cartographic python library with a Matplotlib interface*.
  https://scitools.org.uk/cartopy

### Data

Waveforms and station metadata are obtained from open FDSN services — the National Observatory of
Athens (NOA) and EIDA nodes — for networks HT, HL, HP and HA. Please cite the network operators
and data providers according to their own terms when publishing results derived from their data.

---

## Acknowledgements

Parts of this notebook were adapted from the example notebooks published by the
[SeisBench](https://github.com/seisbench/seisbench) project, for which we are grateful.

Developed within the HOMEROS project (*Harmonising Observations from Multi-hazard Environments in
Research for Open Science*), supported by the OSCARS project, funded by the European Commission's
Horizon Europe Research and Innovation programme under grant agreement No. 101129751.

## License

Released under the GNU General Public License v3.0. Figures and documentation may be reused
under CC BY 4.0 with attribution.
