This packages contains scripts and interfaces to run either PyOcto, HARPA or GaMMA for seismic phase association on seismic picks. Seismic data can be picked, for example, by using PhaseNet or available Deep-Learning pickers from SeisBench.
To learn how to pick seismic data, either follow the examples given in SeisBench or use my package to pick seismic phase onsets with SeisBench.
- standard packages:
numpy,obspy,pandas,seisbench,matplotlib - Packages for phase association:
PyOcto:pip install pyocto, PyOcto requires Pyrocko:https://github.com/pyrocko/pyrockoHARPA:pip install -q git+https://github.com/DaDaCheng/phase_association.git,
HARPA requires POT:pip install POT
HARPA requires scikit-learn:pip install scikit-learnGaMMA:pip install git+https://github.com/wayneweiqiang/GaMMA.git
A .csv file that contains station information (note the file header!):
| id | latitude | longitude | elevation |
|---|---|---|---|
| FO.BETS.00 | 48.89357 | 7.92429 | 146 |
| RG.RITT.00 | 48.89436 | 7.96103 | 138 |
| RG.KUHL.00 | 48.91473 | 7.92996 | 176 |
| FO.OPS.00 | 48.92126 | 7.88278 | 198 |
id: ID of each station (network.station.location)
Iflocationis not known usenetwork.stationlatitude: Latitude of station in degreelongitude: Longitude of station in degreeelevation: Elevation of station in m above sea level
Since the column for the table above are only valid for HARPA, the column
names will be changed for PyOcto. This is done by defining a dictionary
as an argument of the function interface_pytocto (see example below).
A .csv file that contains all picks from all available stations:
| id | timestamp | prob | type |
|---|---|---|---|
| FO.BETS.00 | 2025-04-01 15:31:41.065029 | 0.48751 | P |
| RG.RITT.00 | 2025-04-01 15:31:41.12786 | 0.76287 | P |
| RG.RITT.00 | 2025-04-01 15:31:41.496281 | 0.62723 | S |
| RG.KUHL.00 | 2025-04-01 15:31:42.015891 | 0.89125 | P |
| RG.KUHL.00 | 2025-04-01 15:31:42.158434 | 0.68523 | S |
id: ID of station (network.station.location). Must fit with IDs of station file.
Iflocationis not known usenetwork.stationtimestamp: Time of pick from PhaseNetprob: Pick probability of PhaseNettype: Phase type (Either P or S)
For PyOcto, the column names will be changed. This is done by defining
a dictionary as an argument of the function interface_pytocto (see
example below).
A .csv file that contains a velocity model of the area. If the velocity model is unknown,
define an own velocity model. A detailed velocity model is not required for this purpose.
Otherwise, the velocity model only has constant P- and S-velocities, which is also the case
for HARPA.
| depth | vp | vs |
|---|---|---|
| -0.55 | 1.318 | 0.621 |
| 0.074 | 2.279 | 1.073 |
| 0.418 | 2.551 | 1.202 |
| 0.65 | 2.954 | 1.52 |
depth: Depth in km (positive downwards)vp: P-velocity in km/svs: S-velocity in km/s
Once all files are created, read the different .csv files by using pandas' function read_csv.
Then, phase association is started by running the following codes for the different associators
(config dictionaries can be varied by user; use available keywords that can be found in
the descriptions of Pyocto, Harpa or GaMMA):
In comparison to HARPA and GaMMA, PyOcto needs other keywords. Therefore, the columns of the given
.csv files for the stations and picks are modified by using the arguments station_colum_renaming
and pick_column_renaming. The key of the dictionary represents the original column name and the
value is the required column name for PyOcto.
from association.core.interfaces import interface_pyocto
# Association with Pyocto
config = {
"p_velocity": vp,
"s_velocity": vs,
"zlim": (0, 30),
"time_before": 10,
"n_picks": 6,
"n_p_picks": 3,
"n_s_picks": 3,
"n_p_and_s_picks": 3,
}
catalog = interface_pyocto(
picks=picks,
stations=stations,
config=config,
velocity_model=None, # i.e. no velocity model is given
station_column_renaming={"trace_id": "id", "elevation_m": "elevation"},
pick_column_renaming={"id": "station", "timestamp": "time", "type": "phase"}
)from association.core.interfaces import interface_harpa
# Association with HARPA
config = {
"vel": {"P": 6.0, "S": 6.0 / 1.75},
"ncpu": 6,
"P_phase": True,
"S_phase": True,
"z(km)": (0, 10),
"min_peak_pre_event": 6,
"min_peak_pre_event_p": 0,
"min_peak_pre_event_s": 0,
"time_before": 5,
"DBSCAN": True,
"dbscan_min_samples": 1,
"epoch_before_decay": 2000, # Training more epochs can help find more events
"epoch_after_decay": 2000
}
catalog = interface_harpa(
picks=picks,
stations=stations,
config=config
)from association.core.interfaces import interface_gamma
config = {
"vel": {
"p": 4.5, # If velocity model is used then the velocity model will be used
"s": 2.6,
},
"z(km)": (0, 30),
"ncpu": 4,
"use_dbscan": True,
"use_amplitude": False,
"min_picks_per_eq": 6,
"min_p_picks_per_eq": 3,
"min_s_picks_per_eq": 3,
"max_sigma11": 2.0,
}
catalog = interface_gamma(
stations=station_df,
picks=pick_df,
velocity_model=velocity_model,
config=config,
verbose=True,
)All interface functions return an obspy.Catalog that contains all events, including
the picks.
After the first association some picks remain. In order to associate events from these
remaining picks, a second_pass_iteration is possible for each associator. This is done
in PyOcto by modifying the config dictionary, for example:
config_pyocto = {
"p_velocity": 4500, # Not necessary when velocity model is used
"s_velocity": 2600, # Not necessary when velocity model is used
"zlim": (0, 30),
"time_before": 10,
"n_picks": 6,
"n_p_picks": 3,
"n_s_picks": 3,
"n_p_and_s_picks": 3,
"second_pass_overwrites": { # Do a second or third association with remaining picks
"time_before": 10,
"n_picks": 6,
"n_p_picks": 3,
"n_s_picks": 3,
"n_p_and_s_picks": 3,
"iterations": 1, # Number of iterations for association of remaining picks
},
}In HARPA and GaMMA both interfaces have the argument second_pass_iterations which is an
int and controls the number of additional iterations to associate the remaining picks.
After seismic phase association, the events can be relocalised using NonLinLoc. Therefore, a velocity model (see above) is required. In the following, two different approaches are described to use NLL
- semi-automatic approach: does relocalisation using a single function
- manual approach: creating
.obsfile(s) and localise with own methods
from association.core.nll_functions import nll_wrapper
catalog = nll_wrapper(catalog=catalog,
station_df=stations,
nll_basepath="../NonLinLoc",
nll_executable="/work/software/nlloc/7.00.16/src/bin",
vel_model="../metadata/rittershoffen.nll")catalog: Seismicity catalog from associationstation_df: pandas Dataframe with station information (same as above)nll_basepath: Pathname were all NLL files are savednll_executable: Pathname of executable NLL filesvel_model: Pathname (to .csv-file) or pandas Dataframe of velocity model
If nll_basepath already exists, the time and model files will not be
created by NonLinLoc. This is only done during the first run, when nll_basepath
is created by nll_wrapper.
from association.core.nll_functions import Event2NLL
# Convert catalogue to NLL format
nll = Event2NLL(catalog=catalog, # Previously derived seismicity catalog
nll_basepath="../NonLinLoc",
vel_model=vel_model, # Velocity model for NLL
stations_df=stations, # pandas Dataframe for stations
)The .obs files are saved in nll_basepath/obs (e.g. ../NonLinLoc/obs/).
From here, the files can be copied to a different NonLinLoc project for relocalisation.
Note the NLL implementation can be modified in association.core.nll_functions since
some settings for NLL are hard coded.
tests/example_harpa.py: Association using HARPA.tests/example_pyocto.py: Association using PyOcto (including velocity model).tests/example_gamma.py: Association using GaMMAtests/example_harpa_simulations.py: Simulates events in area, adds random picks and associates simulated picks with HARPA.tests/compare_associators.py: Comparison of all associators classes against each other on example dataset.