Build Earthquake Catalog
In this final notebook, we read the matched-filter database, remove the multiple detections and write a clean earthquake catalog in a csv file.
[18]:
import os
n_CPUs = 12
os.environ["OMP_NUM_THREADS"] = str(n_CPUs)
import BPMF
import glob
import h5py as h5
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sys
from BPMF.data_reader_examples import data_reader_mseed
from tqdm import tqdm
from time import time as give_time
pd.set_option('display.width', 1000)
[19]:
# PROGRAM PARAMETERS
NETWORK_FILENAME = "network.csv"
TEMPLATE_DB = "template_db"
MATCHED_FILTER_DB = "matched_filter_db"
OUTPUT_CSV_FILENAME = "final_catalog.csv"
OUTPUT_DB_FILENAME = "final_catalog.h5"
BACKPROJ_DB_FILENAME = "reloc_bp.h5"
CHECK_SUMMARY_FILE = False
PATH_MF = os.path.join(BPMF.cfg.OUTPUT_PATH, MATCHED_FILTER_DB)
DATA_FOLDER = "preprocessed_2_12"
[20]:
# read network metadata
net = BPMF.dataset.Network(NETWORK_FILENAME)
net.read()
Read the detected events’ metadata for each template
[21]:
# template filenames
template_filenames = glob.glob(os.path.join(BPMF.cfg.OUTPUT_PATH, TEMPLATE_DB, "template*"))
template_filenames.sort()
# initialize the template group
template_group = BPMF.dataset.TemplateGroup.read_from_files(template_filenames, net)
template_group.read_catalog(
extra_attributes=["cc"],
progress=True,
db_path=PATH_MF,
check_summary_file=CHECK_SUMMARY_FILE,
)
# sometimes, tid is not cast to the correct dtype
template_group.catalog.catalog["tid"] = template_group.catalog.catalog["tid"].astype("int32")
Reading catalog: 100%|██████████| 13/13 [00:00<00:00, 14.36it/s]
The BPMF.dataset.TemplateGroup
now has a catalog
attribute, which is a BPMF.dataset.Catalog
instance.
Remove the multiple detections
Remove multiple detections with the TemplateGroup.remove_multiples
method.
[22]:
# DISTANCE_CRITERION_KM: Distance, in km, between two detected events (within uncertainties) below which
# detected events are investigated for equality.
DISTANCE_CRITERION_KM = 15.0
# DT_CRITERION_SEC: Inter-event time, in seconds, between two detected events below which
# detected events are investigated for redundancy.
DT_CRITERION_SEC = 4.0
# SIMILARITY_CRITERION: Inter-template correlation coefficient below which detected events are investigated for equality.
SIMILARITY_CRITERION = 0.10
# N_CLOSEST_STATIONS: When computing the inter-template correlation coefficient, use the N_CLOSEST_STATIONS closest stations
# of a given pair of templates. This parameter is relevant for studies with large seismic networks.
N_CLOSEST_STATIONS = 10
[23]:
# we need to read the waveforms first
template_group.read_waveforms()
template_group.normalize(method="rms")
[24]:
template_group.remove_multiples(
n_closest_stations=N_CLOSEST_STATIONS,
dt_criterion=DT_CRITERION_SEC,
distance_criterion=DISTANCE_CRITERION_KM,
similarity_criterion=SIMILARITY_CRITERION,
progress=True,
)
/home/ebeauce/software/Seismic_BPMF/BPMF/dataset.py:4493: RuntimeWarning: invalid value encountered in true_divide
unit_direction /= np.sqrt(np.sum(unit_direction**2, axis=1))[
Computing the similarity matrix...
Computing the inter-template directional errors...
Searching for events detected by multiple templates
All events occurring within 4.0 sec, with uncertainty ellipsoids closer than 15.0 km will and inter-template CC larger than 0.10 be considered the same
Removing multiples: 0%| | 0/196 [00:00<?, ?it/s]
Removing multiples: 100%|██████████| 196/196 [00:00<00:00, 3229.92it/s]
0.06s to flag the multiples
The catalog now has three new columns: origin_time_sec
(a timestamp of origin_time
in seconds), interevent_time_sec
(template-wise computation), unique_event
.
[25]:
template_group.catalog.catalog.head(10)
[25]:
longitude | latitude | depth | origin_time | cc | tid | return_time | origin_time_sec | interevent_time_sec | unique_event | |
---|---|---|---|---|---|---|---|---|---|---|
event_id | ||||||||||
2.0 | 30.210000 | 40.600000 | -1.500000 | 2012-07-26 00:20:46.080 | 0.355306 | 2 | NaN | 1.343262e+09 | 0.00 | True |
0.0 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 00:58:10.640 | 0.271688 | 0 | NaN | 1.343264e+09 | 2244.56 | False |
1.0 | 30.343555 | 40.716875 | 9.273438 | 2012-07-26 00:58:11.000 | 0.321159 | 1 | NaN | 1.343264e+09 | 0.36 | False |
3.0 | 30.330000 | 40.700000 | -1.500000 | 2012-07-26 00:58:11.080 | 0.309295 | 3 | NaN | 1.343264e+09 | 0.08 | False |
6.0 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 00:58:11.080 | 0.226411 | 6 | NaN | 1.343264e+09 | 0.00 | False |
10.0 | 30.337695 | 40.716875 | 8.867188 | 2012-07-26 00:58:11.120 | 0.342251 | 10 | NaN | 1.343264e+09 | 0.04 | True |
0.1 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 00:58:16.160 | 0.307403 | 0 | 5.52 | 1.343264e+09 | 5.04 | True |
1.1 | 30.343555 | 40.716875 | 9.273438 | 2012-07-26 00:58:16.520 | 0.262917 | 1 | 5.52 | 1.343264e+09 | 0.36 | False |
6.1 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 00:58:16.640 | 0.195023 | 6 | 5.56 | 1.343264e+09 | 0.12 | False |
10.1 | 30.337695 | 40.716875 | 8.867188 | 2012-07-26 00:58:16.680 | 0.268669 | 10 | 5.56 | 1.343264e+09 | 0.04 | False |
The final catalog is made of the unique events only.
[26]:
template_group.catalog.catalog = template_group.catalog.catalog[template_group.catalog.catalog["unique_event"]]
initial_catalog = template_group.catalog.catalog.copy()
Let’s add the location uncertainties from the template events.
[27]:
for tp in template_group.templates:
tid = tp.tid
selection = template_group.catalog.catalog["tid"] == tid
template_group.catalog.catalog.loc[selection, "hmax_unc"] = tp.hmax_unc
template_group.catalog.catalog.loc[selection, "hmin_unc"] = tp.hmin_unc
template_group.catalog.catalog.loc[selection, "az_hmax_unc"] = tp.az_hmax_unc
template_group.catalog.catalog.loc[selection, "vmax_unc"] = tp.vmax_unc
Class instance does not have a `cov_mat` attribute.
[28]:
print(f"There are {len(template_group.catalog.catalog)} events in our template matching catalog!")
template_group.catalog.catalog
There are 63 events in our template matching catalog!
[28]:
longitude | latitude | depth | origin_time | cc | tid | return_time | origin_time_sec | interevent_time_sec | unique_event | hmax_unc | hmin_unc | az_hmax_unc | vmax_unc | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
event_id | ||||||||||||||
2.0 | 30.210000 | 40.600000 | -1.500000 | 2012-07-26 00:20:46.080 | 0.355306 | 2 | NaN | 1.343262e+09 | 0.00 | True | NaN | NaN | NaN | NaN |
10.0 | 30.337695 | 40.716875 | 8.867188 | 2012-07-26 00:58:11.120 | 0.342251 | 10 | NaN | 1.343264e+09 | 0.04 | True | NaN | NaN | NaN | NaN |
0.1 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 00:58:16.160 | 0.307403 | 0 | 5.52 | 1.343264e+09 | 5.04 | True | NaN | NaN | NaN | NaN |
0.2 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 01:02:52.920 | 0.283410 | 0 | 276.76 | 1.343265e+09 | 276.24 | True | NaN | NaN | NaN | NaN |
1.3 | 30.343555 | 40.716875 | 9.273438 | 2012-07-26 01:03:46.920 | 0.403296 | 1 | 53.64 | 1.343265e+09 | 0.36 | True | NaN | NaN | NaN | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
5.13 | 30.339648 | 40.716875 | 9.273438 | 2012-07-26 14:49:28.560 | 0.231381 | 5 | 638.00 | 1.343314e+09 | 636.20 | True | NaN | NaN | NaN | NaN |
11.0 | 30.326099 | 40.600078 | -1.987305 | 2012-07-26 15:06:20.160 | 1.000000 | 11 | NaN | 1.343315e+09 | 1011.60 | True | NaN | NaN | NaN | NaN |
7.3 | 30.338672 | 40.716250 | 8.765625 | 2012-07-26 16:26:52.560 | 0.250604 | 7 | 22768.84 | 1.343320e+09 | 4832.40 | True | NaN | NaN | NaN | NaN |
7.4 | 30.338672 | 40.716250 | 8.765625 | 2012-07-26 16:33:57.680 | 0.171082 | 7 | 425.12 | 1.343320e+09 | 425.12 | True | NaN | NaN | NaN | NaN |
12.0 | 30.430000 | 40.720000 | 10.000000 | 2012-07-26 17:28:20.400 | 1.000000 | 12 | NaN | 1.343324e+09 | 3262.72 | True | NaN | NaN | NaN | NaN |
63 rows × 14 columns
Let’s plot these events on a map. You will see that there are far fewer dots on the map than the total number of earthquakes in our catalog… This is because all newly detected events are attributed their template locations. Therefore, most events are plotted at the exact same location.
[29]:
fig = template_group.catalog.plot_map(
figsize=(10, 10), network=net, s=50, markersize_station=50, lat_margin=0.02, plot_uncertainties=False
)
ax = fig.get_axes()[0]
ax.set_facecolor("dimgrey")
ax.patch.set_alpha(0.15)
Bonus: Relocate each events
In your workflow, you might be ok with the approximate locations of the template matching catalog. However, if you decide to keep refining the catalog, here are some suggestions to get started.
One possibility to start refining the locations of all events is to re-run the same process as in notebook 6, namely PhaseNet
and NonLinLoc
.
[30]:
import seisbench.models as sbm
ml_detector = sbm.PhaseNet.from_pretrained("original")
ml_detector.eval()
[30]:
PhaseNet(
(inc): Conv1d(3, 8, kernel_size=(7,), stride=(1,), padding=same)
(in_bn): BatchNorm1d(8, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(down_branch): ModuleList(
(0): ModuleList(
(0): Conv1d(8, 8, kernel_size=(7,), stride=(1,), padding=same, bias=False)
(1): BatchNorm1d(8, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(2): Conv1d(8, 8, kernel_size=(7,), stride=(4,), padding=(3,), bias=False)
(3): BatchNorm1d(8, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
)
(1): ModuleList(
(0): Conv1d(8, 16, kernel_size=(7,), stride=(1,), padding=same, bias=False)
(1): BatchNorm1d(16, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(2): Conv1d(16, 16, kernel_size=(7,), stride=(4,), bias=False)
(3): BatchNorm1d(16, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
)
(2): ModuleList(
(0): Conv1d(16, 32, kernel_size=(7,), stride=(1,), padding=same, bias=False)
(1): BatchNorm1d(32, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(2): Conv1d(32, 32, kernel_size=(7,), stride=(4,), bias=False)
(3): BatchNorm1d(32, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
)
(3): ModuleList(
(0): Conv1d(32, 64, kernel_size=(7,), stride=(1,), padding=same, bias=False)
(1): BatchNorm1d(64, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(2): Conv1d(64, 64, kernel_size=(7,), stride=(4,), bias=False)
(3): BatchNorm1d(64, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
)
(4): ModuleList(
(0): Conv1d(64, 128, kernel_size=(7,), stride=(1,), padding=same, bias=False)
(1): BatchNorm1d(128, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(2): None
(3): None
)
)
(up_branch): ModuleList(
(0): ModuleList(
(0): ConvTranspose1d(128, 64, kernel_size=(7,), stride=(4,), bias=False)
(1): BatchNorm1d(64, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(2): Conv1d(128, 64, kernel_size=(7,), stride=(1,), padding=same, bias=False)
(3): BatchNorm1d(64, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
)
(1): ModuleList(
(0): ConvTranspose1d(64, 32, kernel_size=(7,), stride=(4,), bias=False)
(1): BatchNorm1d(32, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(2): Conv1d(64, 32, kernel_size=(7,), stride=(1,), padding=same, bias=False)
(3): BatchNorm1d(32, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
)
(2): ModuleList(
(0): ConvTranspose1d(32, 16, kernel_size=(7,), stride=(4,), bias=False)
(1): BatchNorm1d(16, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(2): Conv1d(32, 16, kernel_size=(7,), stride=(1,), padding=same, bias=False)
(3): BatchNorm1d(16, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
)
(3): ModuleList(
(0): ConvTranspose1d(16, 8, kernel_size=(7,), stride=(4,), bias=False)
(1): BatchNorm1d(8, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(2): Conv1d(16, 8, kernel_size=(7,), stride=(1,), padding=same, bias=False)
(3): BatchNorm1d(8, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
)
)
(out): Conv1d(8, 3, kernel_size=(1,), stride=(1,), padding=same)
(softmax): Softmax(dim=1)
)
[31]:
# PhaseNet picking parameters
# PhaseNet was trained for 100Hz data. Even if we saw that running PhaseNet on 25Hz data
# was good for backprojection, here, picking benefits from running PhaseNet on 100Hz data.
# Thus, we will upsample the waveforms before running PhaseNet.
PHASENET_SAMPLING_RATE_HZ = 100.
UPSAMPLING_BEFORE_PN_RELOCATION = int(PHASENET_SAMPLING_RATE_HZ/BPMF.cfg.SAMPLING_RATE_HZ)
DOWNSAMPLING_BEFORE_PN_RELOCATION = 1
# DURATION_SEC: the duration, in seconds, of the data stream starting at the detection time
# defined by Event.origin_time. This data stream is used for picking the P/S waves.
DURATION_SEC = 60.0
# THRESHOLD_P: probability of P-wave arrival above which we declare a pick. If several picks are
# declared during the DURATION_SEC data stream, we only keep the best one. We can
# afford using a low probability threshold since we already know with some confidence
# that an earthquake is in the data stream.
THRESHOLD_P = 0.10
# THRESHOLD_S: probability of S-wave arrival above which we declare a pick.
THRESHOLD_S = 0.10
# DATA_FOLDER: name of the folder where the waveforms we want to use for picking are stored
DATA_FOLDER = "preprocessed_2_12"
# COMPONENT_ALIASES: A dictionary that defines the possible channel names to search for
# for example, the seismometer might not be oriented and the horizontal channels
# might be named 1 and 2, in which case we arbitrarily decide to take 1 as the "N" channel
# and 2 as the "E" channel. This doesn't matter for picking P- and S-wave arrival times.
COMPONENT_ALIASES = {"N": ["N", "1"], "E": ["E", "2"], "Z": ["Z"]}
# PHASE_ON_COMP: dictionary defining which moveout we use to extract the waveform
PHASE_ON_COMP = {"N": "S", "1": "S", "E": "S", "2": "S", "Z": "P"}
# USE_APRIORI_PICKS: boolean. This option is IMPORTANT when running BPMF in HIGH SEISMICITY CONTEXTS, like
# during the aftershock sequence of a large earthquake. If there are many events happening
# close to each other in time, we need to guide PhaseNet to pick the right set of picks.
# For that, we use the predicted P- and S-wave times from backprojection to add extra weight to
# the picks closer to those times and make it more likely to identify them as the "best" picks.
# WARNING: If there are truly many events, even this trick might fail. It's because "phase association"
# is an intrinsically hard problem in this case, and the picking might be hard to do automatically.
USE_APRIORI_PICKS = True
#SEARCH_WIN_SEC: When `use_apriori_picks=True`, the P/S picks in the `DURATION_SEC`-long window are weighted
# using a gaussian centered on the predicted P/S wave arrivals and with standard deviation
# equal to `SEARCH_WIN_SEC`. Thus, the picks outside of +/- `3*SEARCH_WIN_SEC` are essentially
# given a weight of 0.
SEARCH_WIN_SEC = 1.0
# MAX_HORIZONTAL_UNC_KM: Horizontal location uncertainty, in km, above which we keep the template location
MAX_HORIZONTAL_UNC_KM = 10.
# location parameters
LOCATION_ROUTINE = "NLLoc"
# NLLOC_METHOD: string that defines what loss function is used by NLLoc, see http://alomax.free.fr/nlloc/ for more info.
# Using some flavor of 'EDT' is important to obtain robust locations that are not sensitive to pick outliers.
NLLOC_METHOD = "EDT_OT_WT_ML"
# MINIMUM_NUM_STATIONS_W_PICKS: minimum number of stations with picks to even try relocation.
MINIMUM_NUM_STATIONS_W_PICKS = 3
# we set a maximum tolerable difference, in percentage, between the picked time and the predicted travel time
MAX_TIME_DIFFERENT_PICKS_PREDICTED_PERCENT = 25
[32]:
events = {}
for i in tqdm(range(len(template_group.catalog.catalog)), desc="Relocating each individual event"):
row = template_group.catalog.catalog.iloc[i]
tid, evidx = row.name.split(".")
# get the template instance from template_group
template = template_group.templates[template_group.tindexes.loc[int(tid)]]
# this is the filename of the database where template tid's detected events were stored
detection_db_filename = f"detections_template{tid}.h5"
db_path = os.path.join(BPMF.cfg.OUTPUT_PATH, MATCHED_FILTER_DB)
with h5.File(os.path.join(db_path, detection_db_filename), mode="r") as fdet:
keys = list(fdet.keys())
event = BPMF.dataset.Event.read_from_file(
hdf5_file=fdet[keys[int(evidx)]], data_reader=data_reader_mseed
)
# set arrival_times attribute to re-pick P-/S-wave arrivals with prior information
event.set_arrival_times_from_moveouts(verbose=0)
# # attach data reader this way (note: conflict with data_reader argument in phasenet's wrapper module)
# event.data_reader = data_reader_mseed
# pick P-/S-wave arrivals
event.pick_PS_phases(
DURATION_SEC,
phase_on_comp=PHASE_ON_COMP,
threshold_P=THRESHOLD_P,
threshold_S=THRESHOLD_S,
component_aliases=COMPONENT_ALIASES,
data_folder=DATA_FOLDER,
upsampling=UPSAMPLING_BEFORE_PN_RELOCATION,
downsampling=DOWNSAMPLING_BEFORE_PN_RELOCATION,
use_apriori_picks=USE_APRIORI_PICKS,
ml_model=ml_detector,
)
if len(event.picks.dropna(how="any")) >= MINIMUM_NUM_STATIONS_W_PICKS:
# first relocation, insensitive to outliers
event.relocate(
stations=net.stations, routine=LOCATION_ROUTINE, method=NLLOC_METHOD,
)
if "NLLoc_reloc" in event.aux_data:
# this variable was inserted into ev.aux_data if NLLoc successfully located the event
# use predicted times to remove outlier picks
event.remove_outlier_picks(max_diff_percent=MAX_TIME_DIFFERENT_PICKS_PREDICTED_PERCENT)
if len(event.picks.dropna(how="any")) >= MINIMUM_NUM_STATIONS_W_PICKS:
# first relocation, insensitive to outliers
event.relocate(
stations=net.stations, routine=LOCATION_ROUTINE, method=NLLOC_METHOD,
)
else:
del event.aux_data["NLLoc_reloc"]
events[row.name] = event
if ("NLLoc_reloc" in event.aux_data) and (event.hmax_unc) < MAX_HORIZONTAL_UNC_KM:
template_group.catalog.catalog.loc[row.name, "longitude"] = event.longitude
template_group.catalog.catalog.loc[row.name, "latitude"] = event.latitude
template_group.catalog.catalog.loc[row.name, "depth"] = event.depth
template_group.catalog.catalog.loc[row.name, "origin_time"] = event.origin_time
template_group.catalog.catalog.loc[row.name, "hmax_unc"] = event.hmax_unc
template_group.catalog.catalog.loc[row.name, "hmin_unc"] = event.hmin_unc
template_group.catalog.catalog.loc[row.name, "vmax_unc"] = event.vmax_unc
template_group.catalog.catalog.loc[row.name, "az_hmax_unc"] = event.az_hmax_unc
Relocating each individual event: 100%|██████████| 63/63 [00:33<00:00, 1.88it/s]
[33]:
fig = template_group.catalog.plot_map(
figsize=(10, 10), network=net, s=50, markersize_station=50, lat_margin=0.02, plot_uncertainties=False
)
ax = fig.get_axes()[0]
ax.set_facecolor("dimgrey")
ax.patch.set_alpha(0.15)
Assemble the backprojection and template matching catalog
When selecting the template events from the backprojection catalog, we imposed some quality criteria that might have thrown out some events that we still want in our final catalog. Here, we make a simple comparison of the backprojection and template matching catalogs to find these missing events and add them to the final catalog.
[34]:
BACKPROJECTION_CATALOG_FILENAME = "backprojection_catalog.csv"
[35]:
tm_catalog = template_group.catalog.catalog.copy()
tm_catalog.reset_index(inplace=True)
for i in range(len(tm_catalog)):
tm_catalog.loc[i, "event_id"] = f"tm_{tm_catalog.loc[i, 'event_id']}"
tm_catalog
[35]:
event_id | longitude | latitude | depth | origin_time | cc | tid | return_time | origin_time_sec | interevent_time_sec | unique_event | hmax_unc | hmin_unc | az_hmax_unc | vmax_unc | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | tm_2.0 | 30.210000 | 40.600000 | -1.500000 | 2012-07-26 00:20:46.080000 | 0.355306 | 2 | NaN | 1.343262e+09 | 0.00 | True | NaN | NaN | NaN | NaN |
1 | tm_10.0 | 30.337695 | 40.716875 | 8.867188 | 2012-07-26 00:58:11.120000 | 0.342251 | 10 | NaN | 1.343264e+09 | 0.04 | True | NaN | NaN | NaN | NaN |
2 | tm_0.1 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 00:58:16.160000 | 0.307403 | 0 | 5.52 | 1.343264e+09 | 5.04 | True | NaN | NaN | NaN | NaN |
3 | tm_0.2 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 01:02:52.920000 | 0.283410 | 0 | 276.76 | 1.343265e+09 | 276.24 | True | NaN | NaN | NaN | NaN |
4 | tm_1.3 | 30.343555 | 40.716875 | 9.273438 | 2012-07-26 01:03:46.920000 | 0.403296 | 1 | 53.64 | 1.343265e+09 | 0.36 | True | NaN | NaN | NaN | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
58 | tm_5.13 | 30.339648 | 40.716875 | 9.273438 | 2012-07-26 14:49:28.560000 | 0.231381 | 5 | 638.00 | 1.343314e+09 | 636.20 | True | NaN | NaN | NaN | NaN |
59 | tm_11.0 | 30.327686 | 40.600156 | -1.974609 | 2012-07-26T15:06:20.160000Z | 1.000000 | 11 | NaN | 1.343315e+09 | 1011.60 | True | 1.188194 | 0.549611 | 91.233016 | 1.402678 |
60 | tm_7.3 | 30.338672 | 40.716250 | 8.765625 | 2012-07-26 16:26:52.560000 | 0.250604 | 7 | 22768.84 | 1.343320e+09 | 4832.40 | True | NaN | NaN | NaN | NaN |
61 | tm_7.4 | 30.338672 | 40.716250 | 8.765625 | 2012-07-26 16:33:57.680000 | 0.171082 | 7 | 425.12 | 1.343320e+09 | 425.12 | True | NaN | NaN | NaN | NaN |
62 | tm_12.0 | 30.430000 | 40.720000 | 10.000000 | 2012-07-26 17:28:20.400000 | 1.000000 | 12 | NaN | 1.343324e+09 | 3262.72 | True | NaN | NaN | NaN | NaN |
63 rows × 15 columns
[36]:
bp_catalog = pd.read_csv(
os.path.join(BPMF.cfg.OUTPUT_PATH, BACKPROJECTION_CATALOG_FILENAME),
index_col=0
)
# convert origin times from string to pandas.Timestamp
bp_catalog["origin_time"] = pd.to_datetime(bp_catalog["origin_time"])
bp_catalog
[36]:
longitude | latitude | depth | origin_time | hmax_unc | hmin_unc | az_hmax_unc | vmax_unc | event_id.1 | |
---|---|---|---|---|---|---|---|---|---|
event_id | |||||||||
2012-07-26T01:10:21.800000Z | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 01:10:21.800 | 1.736381 | 1.185190 | 156.991406 | 2.658909 | bp_0 |
2012-07-26T01:15:54.160000Z | 30.343555 | 40.716875 | 9.273438 | 2012-07-26 01:15:54.160 | 2.069702 | 1.101945 | 89.976650 | 2.302121 | bp_1 |
2012-07-26T01:58:19.720000Z | 30.210000 | 40.600000 | -1.500000 | 2012-07-26 01:58:19.720 | 0.969304 | 0.828467 | 1.958087 | 2.184634 | bp_2 |
2012-07-26T02:35:01.160000Z | 30.330000 | 40.700000 | -1.500000 | 2012-07-26 02:35:01.160 | 4.120233 | 2.617063 | 74.828267 | 4.698645 | bp_3 |
2012-07-26T03:00:39.160000Z | 30.324023 | 40.715625 | 9.070312 | 2012-07-26 03:00:39.160 | 1.791514 | 1.152377 | 98.957242 | 3.450490 | bp_4 |
2012-07-26T04:43:40.160000Z | 30.350000 | 40.720000 | 8.500000 | 2012-07-26 04:43:40.160 | 15.000000 | 15.000000 | 0.000000 | 15.000000 | bp_5 |
2012-07-26T04:48:38.640000Z | 30.339648 | 40.716875 | 9.273438 | 2012-07-26 04:48:38.640 | 2.723289 | 1.353023 | 77.879040 | 3.955719 | bp_6 |
2012-07-26T08:08:25.680000Z | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 08:08:25.680 | 2.498276 | 1.368690 | 78.911129 | 3.864549 | bp_7 |
2012-07-26T10:07:23.720000Z | 30.338672 | 40.716250 | 8.765625 | 2012-07-26 10:07:23.720 | 1.648093 | 0.947081 | 92.574287 | 2.542094 | bp_8 |
2012-07-26T11:55:35.520000Z | 30.270000 | 40.600000 | 3.000000 | 2012-07-26 11:55:35.520 | 2.728958 | 1.461620 | 152.650097 | 3.774164 | bp_9 |
2012-07-26T13:35:26.480000Z | 30.295215 | 40.797812 | -1.949219 | 2012-07-26 13:35:26.480 | 3.689917 | 0.996875 | 88.219249 | 5.571478 | bp_10 |
2012-07-26T13:48:32.480000Z | 30.315234 | 40.723750 | 11.609375 | 2012-07-26 13:48:32.480 | 1.456039 | 1.326382 | 38.952550 | 2.881104 | bp_11 |
2012-07-26T13:51:58.200000Z | 30.314258 | 40.713125 | 13.335938 | 2012-07-26 13:51:58.200 | 1.774422 | 1.626758 | 134.647549 | 3.745274 | bp_12 |
2012-07-26T13:53:31.400000Z | 30.311328 | 40.718750 | 12.015625 | 2012-07-26 13:53:31.400 | 1.563932 | 1.491863 | 32.859366 | 3.106936 | bp_13 |
2012-07-26T13:56:54.320000Z | 30.337695 | 40.716875 | 8.867188 | 2012-07-26 13:56:54.320 | 2.864502 | 1.240042 | 83.687111 | 2.879827 | bp_14 |
2012-07-26T15:06:20.160000Z | 30.326099 | 40.600078 | -1.987305 | 2012-07-26 15:06:20.160 | 1.464562 | 0.501280 | 92.486000 | 1.075268 | bp_15 |
2012-07-26T17:28:20.400000Z | 30.430000 | 40.720000 | 10.000000 | 2012-07-26 17:28:20.400 | 3.513222 | 1.443351 | 71.766505 | 6.046675 | bp_16 |
[37]:
# catalog merging parameters
dt_criterion_pd = pd.Timedelta(DT_CRITERION_SEC, "s")
HMAX_UNC_CRITERION_KM = 10.
[38]:
combined_catalog = tm_catalog.copy()
missing_event = np.zeros(len(bp_catalog), dtype=bool)
for i in tqdm(range(len(bp_catalog)), desc="Loop through BP cat"):
if bp_catalog.iloc[i]["hmax_unc"] > HMAX_UNC_CRITERION_KM:
continue
t_min = bp_catalog.iloc[i]["origin_time"] - dt_criterion_pd
t_max = bp_catalog.iloc[i]["origin_time"] + dt_criterion_pd
subset_tm = (tm_catalog["origin_time"] > t_min) & (tm_catalog["origin_time"] < t_max)
if np.sum(subset_tm) == 0:
missing_event[i] = True
combined_catalog = pd.concat(
(tm_catalog, bp_catalog[missing_event]),
ignore_index=True
)
combined_catalog.sort_values(
"origin_time", ascending=True, inplace=True
)
Loop through BP cat: 100%|██████████| 17/17 [00:00<00:00, 984.69it/s]
And here is the combined catalog! In this simple example, it appears that all events in the backprojection catalog were re-detected in the template matching catalog so that the final catalog has no extra events with respect to the template matching catalog. However, it may not be always like that. Some events in the backprojection catalog may not have been well enough located for you to use them as templates, and they may not have been subsequently re-detected in the template matching catalog.
[39]:
combined_catalog
[39]:
event_id | longitude | latitude | depth | origin_time | cc | tid | return_time | origin_time_sec | interevent_time_sec | unique_event | hmax_unc | hmin_unc | az_hmax_unc | vmax_unc | event_id.1 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | tm_2.0 | 30.210000 | 40.600000 | -1.500000 | 2012-07-26 00:20:46.080000 | 0.355306 | 2.0 | NaN | 1.343262e+09 | 0.00 | True | NaN | NaN | NaN | NaN | NaN |
1 | tm_10.0 | 30.337695 | 40.716875 | 8.867188 | 2012-07-26 00:58:11.120000 | 0.342251 | 10.0 | NaN | 1.343264e+09 | 0.04 | True | NaN | NaN | NaN | NaN | NaN |
2 | tm_0.1 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 00:58:16.160000 | 0.307403 | 0.0 | 5.52 | 1.343264e+09 | 5.04 | True | NaN | NaN | NaN | NaN | NaN |
3 | tm_0.2 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 01:02:52.920000 | 0.283410 | 0.0 | 276.76 | 1.343265e+09 | 276.24 | True | NaN | NaN | NaN | NaN | NaN |
4 | tm_1.3 | 30.343555 | 40.716875 | 9.273438 | 2012-07-26 01:03:46.920000 | 0.403296 | 1.0 | 53.64 | 1.343265e+09 | 0.36 | True | NaN | NaN | NaN | NaN | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
58 | tm_5.13 | 30.339648 | 40.716875 | 9.273438 | 2012-07-26 14:49:28.560000 | 0.231381 | 5.0 | 638.00 | 1.343314e+09 | 636.20 | True | NaN | NaN | NaN | NaN | NaN |
59 | tm_11.0 | 30.327686 | 40.600156 | -1.974609 | 2012-07-26T15:06:20.160000Z | 1.000000 | 11.0 | NaN | 1.343315e+09 | 1011.60 | True | 1.188194 | 0.549611 | 91.233016 | 1.402678 | NaN |
60 | tm_7.3 | 30.338672 | 40.716250 | 8.765625 | 2012-07-26 16:26:52.560000 | 0.250604 | 7.0 | 22768.84 | 1.343320e+09 | 4832.40 | True | NaN | NaN | NaN | NaN | NaN |
61 | tm_7.4 | 30.338672 | 40.716250 | 8.765625 | 2012-07-26 16:33:57.680000 | 0.171082 | 7.0 | 425.12 | 1.343320e+09 | 425.12 | True | NaN | NaN | NaN | NaN | NaN |
62 | tm_12.0 | 30.430000 | 40.720000 | 10.000000 | 2012-07-26 17:28:20.400000 | 1.000000 | 12.0 | NaN | 1.343324e+09 | 3262.72 | True | NaN | NaN | NaN | NaN | NaN |
63 rows × 16 columns
In this example, we see that two entries of the catalog end up pointing to the same event (see below):
[40]:
combined_catalog[
(combined_catalog["origin_time"] >= pd.Timestamp("2012-07-26T13:40:00"))
&
(combined_catalog["origin_time"] <= pd.Timestamp("2012-07-26T13:50:00"))
]
[40]:
event_id | longitude | latitude | depth | origin_time | cc | tid | return_time | origin_time_sec | interevent_time_sec | unique_event | hmax_unc | hmin_unc | az_hmax_unc | vmax_unc | event_id.1 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
51 | tm_0.32 | 30.312305 | 40.719375 | 12.523438 | 2012-07-26T13:48:32.400000Z | 0.193654 | 0.0 | 18.88 | 1.343311e+09 | 16.68 | True | 1.698073 | 1.416915 | -157.812149 | 3.070552 | NaN |
50 | tm_0.31 | 30.314258 | 40.721875 | 11.914062 | 2012-07-26T13:48:32.440000Z | 0.733365 | 0.0 | 20407.12 | 1.343311e+09 | 785.84 | True | 1.516158 | 1.360425 | -140.439618 | 2.908200 | NaN |
versus (before relocating each event individually)
[41]:
initial_catalog[
(initial_catalog["origin_time"] >= pd.Timestamp("2012-07-26T13:40:00"))
&
(initial_catalog["origin_time"] <= pd.Timestamp("2012-07-26T13:50:00"))
]
[41]:
longitude | latitude | depth | origin_time | cc | tid | return_time | origin_time_sec | interevent_time_sec | unique_event | |
---|---|---|---|---|---|---|---|---|---|---|
event_id | ||||||||||
0.31 | 30.323047 | 40.73125 | 12.015625 | 2012-07-26 13:48:32.320 | 0.733365 | 0 | 20407.12 | 1.343311e+09 | 785.84 | True |
0.32 | 30.323047 | 40.73125 | 12.015625 | 2012-07-26 13:48:51.200 | 0.193654 | 0 | 18.88 | 1.343311e+09 | 16.68 | True |
This is because, despite using the use_apriori_picks=True
, the automatic picking ended up picking the event before tm_0.33
. So, before I release a fix to this issue, we need to clean up these redundant catalog entries! And keep in mind that the initial template matching catalog might have better temporal resolution.
Last clean up
[42]:
final_cat = combined_catalog.copy()
final_cat["origin_time_sec"] = (
final_cat["origin_time"].values.astype("datetime64[ms]").astype("float64") / 1000.
)
num_removed = -1
iteration = 0
while num_removed != 0:
print(f"------ Iteration {iteration} --------")
interevent_time_sec = (
final_cat["origin_time_sec"].values[1:] - final_cat["origin_time_sec"].values[:-1]
)
interevent_time_sec = np.hstack( (1000., interevent_time_sec) )
remove = np.zeros(len(final_cat), dtype=bool)
for i in np.where(interevent_time_sec < DT_CRITERION_SEC)[0]:
dist = BPMF.utils.two_point_epicentral_distance(
final_cat.iloc[i-1].longitude,
final_cat.iloc[i-1].latitude,
final_cat.iloc[i].longitude,
final_cat.iloc[i].latitude,
)
if dist < DISTANCE_CRITERION_KM:
if final_cat.iloc[i-1].hmax_unc < final_cat.iloc[i].hmax_unc:
remove[i] = True
else:
remove[i-1] = True
num_removed = np.sum(remove)
print(f"Removed {num_removed} events. Examples:")
if num_removed > 1:
removed_events = np.where(remove)[0]
print(final_cat[["origin_time", "event_id", "longitude", "latitude"]].iloc[removed_events[0]])
if interevent_time_sec[removed_events[0]] < DT_CRITERION_SEC:
print(final_cat[["origin_time", "event_id", "longitude",
"latitude"]].iloc[removed_events[0] - 1])
else:
print(final_cat[["origin_time", "event_id", "longitude",
"latitude"]].iloc[removed_events[0] + 1])
final_cat = final_cat[~remove]
iteration += 1
------ Iteration 0 --------
Removed 3 events. Examples:
origin_time 2012-07-26T01:15:14.320000Z
event_id tm_0.9
longitude 30.381641
latitude 40.72125
Name: 10, dtype: object
origin_time 2012-07-26T01:15:15.000000Z
event_id tm_1.9
longitude 30.329883
latitude 40.714375
Name: 11, dtype: object
------ Iteration 1 --------
Removed 0 events. Examples:
[43]:
final_cat.set_index("event_id", drop=False, inplace=True)
final_cat.drop(columns=["interevent_time_sec", "unique_event", "origin_time_sec"], inplace=True)
final_cat.sort_values("origin_time", ascending=True, inplace=True)
final_cat
[43]:
event_id | longitude | latitude | depth | origin_time | cc | tid | return_time | hmax_unc | hmin_unc | az_hmax_unc | vmax_unc | event_id.1 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
event_id | |||||||||||||
tm_2.0 | tm_2.0 | 30.210000 | 40.600000 | -1.500000 | 2012-07-26 00:20:46.080000 | 0.355306 | 2.0 | NaN | NaN | NaN | NaN | NaN | NaN |
tm_10.0 | tm_10.0 | 30.337695 | 40.716875 | 8.867188 | 2012-07-26 00:58:11.120000 | 0.342251 | 10.0 | NaN | NaN | NaN | NaN | NaN | NaN |
tm_0.1 | tm_0.1 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 00:58:16.160000 | 0.307403 | 0.0 | 5.52 | NaN | NaN | NaN | NaN | NaN |
tm_0.2 | tm_0.2 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 01:02:52.920000 | 0.283410 | 0.0 | 276.76 | NaN | NaN | NaN | NaN | NaN |
tm_1.3 | tm_1.3 | 30.343555 | 40.716875 | 9.273438 | 2012-07-26 01:03:46.920000 | 0.403296 | 1.0 | 53.64 | NaN | NaN | NaN | NaN | NaN |
tm_0.4 | tm_0.4 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 01:09:15.880000 | 0.210914 | 0.0 | 329.32 | NaN | NaN | NaN | NaN | NaN |
tm_10.3 | tm_10.3 | 30.337695 | 40.716875 | 8.867188 | 2012-07-26 01:09:26 | 0.290443 | 10.0 | 338.96 | NaN | NaN | NaN | NaN | NaN |
tm_0.6 | tm_0.6 | 30.320117 | 40.729375 | 12.117188 | 2012-07-26T01:10:21.840000Z | 1.000000 | 0.0 | 56.28 | 1.776457 | 1.293067 | 153.641704 | 2.649126 | NaN |
tm_10.5 | tm_10.5 | 30.337695 | 40.716875 | 8.867188 | 2012-07-26 01:10:44.080000 | 0.590581 | 10.0 | 21.76 | NaN | NaN | NaN | NaN | NaN |
tm_1.7 | tm_1.7 | 30.343555 | 40.716875 | 9.273438 | 2012-07-26 01:12:32.880000 | 0.315028 | 1.0 | 108.92 | NaN | NaN | NaN | NaN | NaN |
tm_1.9 | tm_1.9 | 30.329883 | 40.714375 | 8.664062 | 2012-07-26T01:15:15.000000Z | 0.332720 | 1.0 | 7.56 | 5.721093 | 1.856309 | 109.504639 | 4.681934 | NaN |
tm_1.11 | tm_1.11 | 30.339648 | 40.718125 | 9.070312 | 2012-07-26T01:15:54.240000Z | 1.000000 | 1.0 | 19.08 | 1.949209 | 1.118634 | 93.451420 | 2.413492 | NaN |
tm_0.13 | tm_0.13 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 01:16:29.640000 | 0.378459 | 0.0 | 35.84 | NaN | NaN | NaN | NaN | NaN |
tm_0.14 | tm_0.14 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 01:18:32.520000 | 0.279227 | 0.0 | 122.88 | NaN | NaN | NaN | NaN | NaN |
tm_3.8 | tm_3.8 | 30.330000 | 40.700000 | -1.500000 | 2012-07-26 01:35:14.720000 | 0.257078 | 3.0 | 1124.60 | NaN | NaN | NaN | NaN | NaN |
tm_1.14 | tm_1.14 | 30.343555 | 40.716875 | 9.273438 | 2012-07-26 01:39:53.680000 | 0.230187 | 1.0 | 1280.80 | NaN | NaN | NaN | NaN | NaN |
tm_0.16 | tm_0.16 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 01:47:30.080000 | 0.201523 | 0.0 | 456.76 | NaN | NaN | NaN | NaN | NaN |
tm_6.12 | tm_6.12 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 01:52:27.960000 | 0.204428 | 6.0 | 2034.96 | NaN | NaN | NaN | NaN | NaN |
tm_1.15 | tm_1.15 | 30.358203 | 40.693750 | 11.609375 | 2012-07-26T01:52:36.480000Z | 0.395754 | 1.0 | 762.76 | 6.427452 | 2.654027 | 110.981749 | 5.236795 | NaN |
tm_2.1 | tm_2.1 | 30.210000 | 40.600000 | -1.500000 | 2012-07-26 01:58:19.720000 | 1.000000 | 2.0 | 5853.64 | NaN | NaN | NaN | NaN | NaN |
tm_0.18 | tm_0.18 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 02:22:49.440000 | 0.301763 | 0.0 | 1813.36 | NaN | NaN | NaN | NaN | NaN |
tm_1.17 | tm_1.17 | 30.343555 | 40.716875 | 9.273438 | 2012-07-26 02:24:34.920000 | 0.467743 | 1.0 | 105.12 | NaN | NaN | NaN | NaN | NaN |
tm_3.11 | tm_3.11 | 30.330000 | 40.700000 | -1.500000 | 2012-07-26 02:35:01.160000 | 1.000000 | 3.0 | 626.12 | NaN | NaN | NaN | NaN | NaN |
tm_4.2 | tm_4.2 | 30.350000 | 40.720000 | 8.500000 | 2012-07-26 02:43:23.080000 | 0.231139 | 4.0 | 5557.32 | NaN | NaN | NaN | NaN | NaN |
tm_0.21 | tm_0.21 | 30.315234 | 40.713750 | 12.015625 | 2012-07-26T03:00:38.720000Z | 0.697115 | 0.0 | 1537.84 | 1.676146 | 1.188218 | 93.072440 | 3.278191 | NaN |
tm_6.18 | tm_6.18 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 03:08:33.560000 | 0.411435 | 6.0 | 474.56 | NaN | NaN | NaN | NaN | NaN |
tm_6.19 | tm_6.19 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 03:10:55.080000 | 0.321954 | 6.0 | 141.52 | NaN | NaN | NaN | NaN | NaN |
tm_0.24 | tm_0.24 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 03:32:50.800000 | 0.172344 | 0.0 | 1316.20 | NaN | NaN | NaN | NaN | NaN |
tm_0.25 | tm_0.25 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 03:38:13.200000 | 0.196873 | 0.0 | 322.40 | NaN | NaN | NaN | NaN | NaN |
tm_6.20 | tm_6.20 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 04:30:33.560000 | 0.191620 | 6.0 | 4778.48 | NaN | NaN | NaN | NaN | NaN |
tm_4.5 | tm_4.5 | 30.350000 | 40.720000 | 8.500000 | 2012-07-26 04:43:40.160000 | 1.000000 | 4.0 | 5704.84 | NaN | NaN | NaN | NaN | NaN |
tm_10.19 | tm_10.19 | 30.337695 | 40.716875 | 8.867188 | 2012-07-26 04:45:04 | 0.277363 | 10.0 | 5648.88 | NaN | NaN | NaN | NaN | NaN |
tm_4.6 | tm_4.6 | 30.350000 | 40.720000 | 8.500000 | 2012-07-26 04:46:51.080000 | 0.335807 | 4.0 | 190.92 | NaN | NaN | NaN | NaN | NaN |
tm_5.5 | tm_5.5 | 30.339648 | 40.718125 | 9.273438 | 2012-07-26T04:48:38.640000Z | 1.000000 | 5.0 | 109.32 | 2.760022 | 1.433242 | 80.900073 | 4.036697 | NaN |
tm_4.8 | tm_4.8 | 30.350000 | 40.720000 | 8.500000 | 2012-07-26 04:51:08.440000 | 0.354223 | 4.0 | 148.04 | NaN | NaN | NaN | NaN | NaN |
tm_6.24 | tm_6.24 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 05:22:04.480000 | 0.286994 | 6.0 | 1857.76 | NaN | NaN | NaN | NaN | NaN |
tm_6.25 | tm_6.25 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 05:45:46.040000 | 0.182386 | 6.0 | 1421.56 | NaN | NaN | NaN | NaN | NaN |
tm_6.26 | tm_6.26 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 05:46:59.280000 | 0.267265 | 6.0 | 73.24 | NaN | NaN | NaN | NaN | NaN |
tm_6.27 | tm_6.27 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 05:57:16.480000 | 0.211332 | 6.0 | 617.20 | NaN | NaN | NaN | NaN | NaN |
tm_6.28 | tm_6.28 | 30.322070 | 40.720625 | 8.867188 | 2012-07-26T08:08:25.760000Z | 1.000000 | 6.0 | 7869.20 | 1.687249 | 1.420870 | 119.057751 | 4.167423 | NaN |
tm_7.0 | tm_7.0 | 30.338672 | 40.716250 | 8.765625 | 2012-07-26 09:21:39.400000 | 0.205630 | 7.0 | NaN | NaN | NaN | NaN | NaN | NaN |
tm_7.1 | tm_7.1 | 30.338672 | 40.716250 | 8.765625 | 2012-07-26 09:28:48.360000 | 0.174923 | 7.0 | 428.96 | NaN | NaN | NaN | NaN | NaN |
tm_7.2 | tm_7.2 | 30.339160 | 40.716563 | 8.816406 | 2012-07-26T10:07:23.720000Z | 1.000000 | 7.0 | 2315.36 | 1.549251 | 0.960434 | 88.241652 | 2.583515 | NaN |
tm_6.29 | tm_6.29 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 10:16:45.800000 | 0.359092 | 6.0 | 7700.12 | NaN | NaN | NaN | NaN | NaN |
tm_6.30 | tm_6.30 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 10:53:46.600000 | 0.235509 | 6.0 | 2220.80 | NaN | NaN | NaN | NaN | NaN |
tm_6.31 | tm_6.31 | 30.335742 | 40.720625 | 8.867188 | 2012-07-26 11:02:23.600000 | 0.350748 | 6.0 | 517.00 | NaN | NaN | NaN | NaN | NaN |
tm_8.0 | tm_8.0 | 30.270000 | 40.600000 | 3.000000 | 2012-07-26 11:55:35.520000 | 1.000000 | 8.0 | NaN | NaN | NaN | NaN | NaN | NaN |
tm_9.0 | tm_9.0 | 30.294238 | 40.798437 | -1.949219 | 2012-07-26T13:35:26.440000Z | 1.000000 | 9.0 | NaN | 3.715397 | 0.989535 | 89.098493 | 5.636553 | NaN |
tm_0.31 | tm_0.31 | 30.314258 | 40.721875 | 11.914062 | 2012-07-26T13:48:32.440000Z | 0.733365 | 0.0 | 20407.12 | 1.516158 | 1.360425 | -140.439618 | 2.908200 | NaN |
tm_0.33 | tm_0.33 | 30.323047 | 40.731250 | 12.015625 | 2012-07-26 13:50:18.120000 | 0.342391 | 0.0 | 86.92 | NaN | NaN | NaN | NaN | NaN |
tm_10.24 | tm_10.24 | 30.312305 | 40.710625 | 13.539062 | 2012-07-26T13:51:58.160000Z | 0.536160 | 10.0 | 100.08 | 1.930258 | 1.772629 | 154.039822 | 3.537436 | NaN |
tm_0.35 | tm_0.35 | 30.308398 | 40.718125 | 12.320312 | 2012-07-26T13:53:31.360000Z | 0.757365 | 0.0 | 93.04 | 1.609972 | 1.401390 | -153.469187 | 2.941569 | NaN |
tm_10.26 | tm_10.26 | 30.351367 | 40.721875 | 7.648438 | 2012-07-26T13:54:54.240000Z | 0.408196 | 10.0 | 82.60 | 4.956472 | 2.006614 | 89.078378 | 6.387298 | NaN |
tm_10.27 | tm_10.27 | 30.337695 | 40.716875 | 8.867188 | 2012-07-26T13:56:54.320000Z | 1.000000 | 10.0 | 120.04 | 2.815204 | 1.244460 | 83.432871 | 2.466413 | NaN |
tm_4.14 | tm_4.14 | 30.350000 | 40.720000 | 8.500000 | 2012-07-26 14:38:52.360000 | 0.257913 | 4.0 | 2718.96 | NaN | NaN | NaN | NaN | NaN |
tm_5.13 | tm_5.13 | 30.339648 | 40.716875 | 9.273438 | 2012-07-26 14:49:28.560000 | 0.231381 | 5.0 | 638.00 | NaN | NaN | NaN | NaN | NaN |
tm_11.0 | tm_11.0 | 30.327686 | 40.600156 | -1.974609 | 2012-07-26T15:06:20.160000Z | 1.000000 | 11.0 | NaN | 1.188194 | 0.549611 | 91.233016 | 1.402678 | NaN |
tm_7.3 | tm_7.3 | 30.338672 | 40.716250 | 8.765625 | 2012-07-26 16:26:52.560000 | 0.250604 | 7.0 | 22768.84 | NaN | NaN | NaN | NaN | NaN |
tm_7.4 | tm_7.4 | 30.338672 | 40.716250 | 8.765625 | 2012-07-26 16:33:57.680000 | 0.171082 | 7.0 | 425.12 | NaN | NaN | NaN | NaN | NaN |
tm_12.0 | tm_12.0 | 30.430000 | 40.720000 | 10.000000 | 2012-07-26 17:28:20.400000 | 1.000000 | 12.0 | NaN | NaN | NaN | NaN | NaN | NaN |
Save final catalog
[44]:
# csv format
final_cat.to_csv(
os.path.join(BPMF.cfg.OUTPUT_PATH, OUTPUT_CSV_FILENAME)
)
[45]:
# hdf5 / BPMF format
for i in range(len(final_cat)):
evid = final_cat.iloc[i]["event_id"]
if evid[:2] == "tm":
events[evid.split("_")[1]].write(
OUTPUT_DB_FILENAME,
db_path=BPMF.cfg.OUTPUT_PATH,
gid=evid
)
elif evid[:2] == "bp":
with h5.File(os.path.join(BPMF.cfg.OUTPUT_PATH, OUTPUT_DB_FILENAME), mode="a") as fout:
with h5.File(os.path.join(BPMF.cfg.OUTPUT_PATH, BACKPROJ_DB_FILENAME), mode="r") as fin:
# copy the corresponding hdf5 group into fout
fin.copy(fin[evid], fout)
[46]:
# The final cat!
BPMF.utils.donefun()
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⢀⡤⣤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢀⡏⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⣀⠴⠋⠉⠉⡆⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠈⠉⠉⠙⠓⠚⠁⠀⠀⠀⠀⣿⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢀⠞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣄⠀⠀⠀⠀
⠀⠀⠀⠀⡞⠀⠀⠀⠀⠀⠶⠀⠀⠀⠀⠀⠀⠦⠀⠀⠀⠀⠀⠸⡆⠀⠀⠀
⢠⣤⣶⣾⣧⣤⣤⣀⡀⠀⠀⠀⠀⠈⠀⠀⠀⢀⡤⠴⠶⠤⢤⡀⣧⣀⣀⠀
⠻⠶⣾⠁⠀⠀⠀⠀⠙⣆⠀⠀⠀⠀⠀⠀⣰⠋⠀⠀⠀⠀⠀⢹⣿⣭⣽⠇
⠀⠀⠙⠤⠴⢤⡤⠤⠤⠋⠉⠉⠉⠉⠉⠉⠉⠳⠖⠦⠤⠶⠦⠞⠁⠀⠀⠀
⠀ALL DONE!⠀⠀⠀