utdquake 0.2.2__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {utdquake-0.2.2/utdquake.egg-info → utdquake-0.3.0}/PKG-INFO +1 -1
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/__init__.py +12 -2
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/bank/bank.py +16 -7
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/bank/utils.py +118 -24
- utdquake-0.3.0/utdquake/figures.py +442 -0
- utdquake-0.3.0/utdquake/hub.py +418 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/qc/catalog.py +67 -34
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/qc/travel_time.py +84 -2
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/utils/cache.py +1 -1
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/utils/plot.py +34 -25
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/utils/utils.py +26 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/writers/parquet.py +106 -1
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/writers/schema.py +24 -7
- {utdquake-0.2.2 → utdquake-0.3.0/utdquake.egg-info}/PKG-INFO +1 -1
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake.egg-info/SOURCES.txt +2 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/LICENSE +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/README.md +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/setup.cfg +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/setup.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/bank/__init__.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/core/__init__.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/core/config.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/core/data.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/core/load.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/core/patch.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/core/utdquake.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/core/utils.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/qc/__init__.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/qc/config.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/qc/events.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/qc/log.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/qc/picks.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/utils/__init__.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/writers/__init__.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake/writers/manifest.py +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake.egg-info/dependency_links.txt +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake.egg-info/requires.txt +0 -0
- {utdquake-0.2.2 → utdquake-0.3.0}/utdquake.egg-info/top_level.txt +0 -0
|
@@ -9,6 +9,10 @@ includes:
|
|
|
9
9
|
- `Network`: Class for network-specific access, including EventBank, picks, and stations.
|
|
10
10
|
- `download_snapshot`: Function to download UTDQuake data from Hugging Face.
|
|
11
11
|
- `load`: Function to load datasets from Hugging Face by key.
|
|
12
|
+
- `publish_network`: Function to publish a network's manifests to the shared Hub repo.
|
|
13
|
+
- `remove_network`: Function to remove a network's files from the shared Hub repo.
|
|
14
|
+
- `generate_network_figures`: Function to generate a network's documentation figures.
|
|
15
|
+
- `publish_network_figures`: Function to publish those figures to GitHub.
|
|
12
16
|
|
|
13
17
|
Usage
|
|
14
18
|
-----
|
|
@@ -20,11 +24,17 @@ Usage
|
|
|
20
24
|
>>> net.events.head()
|
|
21
25
|
|
|
22
26
|
"""
|
|
23
|
-
__version__ = "0.
|
|
27
|
+
__version__ = "0.3.0"
|
|
24
28
|
|
|
25
29
|
from .core.utdquake import Dataset, Network
|
|
26
30
|
from .core.data import download_snapshot, load
|
|
27
31
|
from .core.patch import utdquake_obspy_patch
|
|
32
|
+
from .hub import publish_network, remove_network
|
|
33
|
+
from .figures import generate_network_figures, publish_network_figures
|
|
28
34
|
|
|
29
35
|
utdquake_obspy_patch()
|
|
30
|
-
__all__ = [
|
|
36
|
+
__all__ = [
|
|
37
|
+
"Dataset", "Network", "download_snapshot", "load",
|
|
38
|
+
"publish_network", "remove_network",
|
|
39
|
+
"generate_network_figures", "publish_network_figures",
|
|
40
|
+
]
|
|
@@ -73,6 +73,7 @@ class UTDQBank(obsplus.EventBank):
|
|
|
73
73
|
self.das = das
|
|
74
74
|
self.contributor = os.path.basename(self.bank_path)
|
|
75
75
|
self.db_paths = self.__prepare_paths()
|
|
76
|
+
self.utdq_paths = self.db_paths
|
|
76
77
|
|
|
77
78
|
def __prepare_paths(self) -> None:
|
|
78
79
|
"""
|
|
@@ -297,7 +298,7 @@ class UTDQBank(obsplus.EventBank):
|
|
|
297
298
|
raise FileNotFoundError(f"Folder not found: {folder_path}")
|
|
298
299
|
|
|
299
300
|
# Build full glob pattern (e.g., /path/to/folder/*.xml)
|
|
300
|
-
pattern = os.path.join(folder_path, file_extension)
|
|
301
|
+
pattern = os.path.join(folder_path, "**", file_extension)
|
|
301
302
|
|
|
302
303
|
# Retrieve all matching files
|
|
303
304
|
files: List[str] = glob.glob(pattern, recursive=True)
|
|
@@ -343,7 +344,8 @@ class UTDQBank(obsplus.EventBank):
|
|
|
343
344
|
def put_utdq_stations(self, stations, starttime=None, endtime=None,
|
|
344
345
|
chunk_seconds=86400,
|
|
345
346
|
das=False,
|
|
346
|
-
calculate_d_az=True
|
|
347
|
+
calculate_d_az=True,
|
|
348
|
+
station_sample_size=None) -> None:
|
|
347
349
|
"""
|
|
348
350
|
Save station metadata to events in the event bank over a given time range.
|
|
349
351
|
|
|
@@ -367,15 +369,21 @@ class UTDQBank(obsplus.EventBank):
|
|
|
367
369
|
calculate_d_az : bool, optional
|
|
368
370
|
Whether to calculate distance and azimuth between
|
|
369
371
|
events and stations (default is True).
|
|
372
|
+
station_sample_size : int, optional
|
|
373
|
+
At most this many station records (one per arrival) per station
|
|
374
|
+
database, spread over the events: enough for the station-location
|
|
375
|
+
summary (calculated position and its spread). None (default)
|
|
376
|
+
keeps one per arrival. Distances and azimuths of all arrivals
|
|
377
|
+
are computed either way.
|
|
370
378
|
|
|
371
379
|
"""
|
|
372
380
|
|
|
373
381
|
ebank_index_path = os.path.join(self.bank_path, ".index.db")
|
|
374
382
|
|
|
375
383
|
if starttime is None:
|
|
376
|
-
starttime = self.read_index()["time"].min()
|
|
384
|
+
starttime = self.read_index()["time"].min() - pd.Timedelta(seconds=1)
|
|
377
385
|
if endtime is None:
|
|
378
|
-
endtime = self.read_index()["time"].max()
|
|
386
|
+
endtime = self.read_index()["time"].max() + pd.Timedelta(seconds=1)
|
|
379
387
|
|
|
380
388
|
iteration = 0
|
|
381
389
|
for catalog_dict in ut.catalog_generator(self,starttime=starttime,
|
|
@@ -397,7 +405,8 @@ class UTDQBank(obsplus.EventBank):
|
|
|
397
405
|
df_stations=stations,
|
|
398
406
|
calculate_d_az=calculate_d_az,
|
|
399
407
|
ebank_index_path=ebank_index_path,
|
|
400
|
-
das=das
|
|
408
|
+
das=das,
|
|
409
|
+
station_sample_size=station_sample_size,
|
|
401
410
|
)
|
|
402
411
|
|
|
403
412
|
new_len_catalog = len(catalog)
|
|
@@ -536,7 +545,7 @@ class UTDQBank(obsplus.EventBank):
|
|
|
536
545
|
i,
|
|
537
546
|
total_chunks,
|
|
538
547
|
)
|
|
539
|
-
catalog.apply_utdq_qc()
|
|
548
|
+
catalog = catalog.apply_utdq_qc()
|
|
540
549
|
|
|
541
550
|
events_df = catalog.utdq_events_to_df()
|
|
542
551
|
picks_df = catalog.utdq_picks_to_df()
|
|
@@ -628,7 +637,7 @@ class UTDQBank(obsplus.EventBank):
|
|
|
628
637
|
)
|
|
629
638
|
"""
|
|
630
639
|
|
|
631
|
-
df = ut._read_table(self.
|
|
640
|
+
df = ut._read_table(self.utdq_paths[".utdquake/export/db/stations"], query)
|
|
632
641
|
|
|
633
642
|
df = sanitize_dataframe(df,
|
|
634
643
|
order_cols=PREF_STATIONS_ORDER,
|
|
@@ -1228,11 +1228,14 @@ def get_stations_summary(stations_folder,
|
|
|
1228
1228
|
# --- Collapse into one row ---
|
|
1229
1229
|
summary = {}
|
|
1230
1230
|
exclude_cols = {"dist_deg", "esaz", "used", "origin_id", "creation_time"}
|
|
1231
|
+
std_cols = {"calculated_latitude", "calculated_longitude"}
|
|
1231
1232
|
for col in df.columns:
|
|
1232
1233
|
if col in exclude_cols:
|
|
1233
1234
|
continue
|
|
1234
1235
|
if pd.api.types.is_float_dtype(df[col]):
|
|
1235
1236
|
summary[col] = df[col].mean()
|
|
1237
|
+
if col in std_cols:
|
|
1238
|
+
summary[f"{col}_std"] = df[col].std()
|
|
1236
1239
|
elif pd.api.types.is_bool_dtype(df[col]):
|
|
1237
1240
|
summary[col] = df[col].any() # True if any True
|
|
1238
1241
|
elif pd.api.types.is_integer_dtype(df[col]):
|
|
@@ -1242,6 +1245,12 @@ def get_stations_summary(stations_folder,
|
|
|
1242
1245
|
else:
|
|
1243
1246
|
summary[col] = df[col].iloc[0] # fallback
|
|
1244
1247
|
|
|
1248
|
+
# Number of arrivals that contributed to the calculated position
|
|
1249
|
+
# (one row per origin at this point, since drop_duplicates above
|
|
1250
|
+
# collapses to one entry per origin_id).
|
|
1251
|
+
if "calculated_latitude" in df.columns:
|
|
1252
|
+
summary["calculated_num_entries"] = int(df["calculated_latitude"].notna().sum())
|
|
1253
|
+
|
|
1245
1254
|
summary["db_path"] = db_path
|
|
1246
1255
|
summary["creation_time"] = UTCDateTime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
|
|
1247
1256
|
return summary
|
|
@@ -1265,13 +1274,23 @@ def process_arrival(arrival, source_loc,
|
|
|
1265
1274
|
ebank_index_path,
|
|
1266
1275
|
# table="/stations/index",
|
|
1267
1276
|
calculate_d_az = False,
|
|
1268
|
-
das=False
|
|
1277
|
+
das=False,
|
|
1278
|
+
row_sink=None,
|
|
1279
|
+
station_lookup=None,
|
|
1280
|
+
stations_folder=None,
|
|
1269
1281
|
):
|
|
1270
1282
|
"""
|
|
1271
1283
|
Process an individual arrival: verify pick and station information,
|
|
1272
1284
|
optionally update distance and azimuth, and add missing or confirmed
|
|
1273
1285
|
station records to the database.
|
|
1274
1286
|
|
|
1287
|
+
With ``row_sink`` (a dict), the station record is appended to
|
|
1288
|
+
``row_sink[<station db path>]`` instead of being written at once; the
|
|
1289
|
+
caller writes each station database once (see
|
|
1290
|
+
:func:`write_station_rows`). ``station_lookup`` ({(network, station):
|
|
1291
|
+
DataFrame}) and ``stations_folder`` avoid repeating per arrival what is
|
|
1292
|
+
the same for the whole catalog.
|
|
1293
|
+
|
|
1275
1294
|
Parameters:
|
|
1276
1295
|
arrival (Arrival): The arrival object to process.
|
|
1277
1296
|
source_loc (tuple): Tuple of (latitude, longitude) for the event origin.
|
|
@@ -1294,15 +1313,15 @@ def process_arrival(arrival, source_loc,
|
|
|
1294
1313
|
"network_type": None,
|
|
1295
1314
|
"station": None,
|
|
1296
1315
|
"channel": None,
|
|
1297
|
-
"dist_deg": np.
|
|
1298
|
-
"esaz": np.
|
|
1316
|
+
"dist_deg": np.nan,
|
|
1317
|
+
"esaz": np.nan,
|
|
1299
1318
|
"confirmed": False,
|
|
1300
|
-
"confirmed_latitude": np.
|
|
1301
|
-
"confirmed_longitude": np.
|
|
1302
|
-
"confirmed_elevation": np.
|
|
1319
|
+
"confirmed_latitude": np.nan,
|
|
1320
|
+
"confirmed_longitude": np.nan,
|
|
1321
|
+
"confirmed_elevation": np.nan,
|
|
1303
1322
|
"calculated": False,
|
|
1304
|
-
"calculated_latitude": np.
|
|
1305
|
-
"calculated_longitude": np.
|
|
1323
|
+
"calculated_latitude": np.nan,
|
|
1324
|
+
"calculated_longitude": np.nan,
|
|
1306
1325
|
"used": False,
|
|
1307
1326
|
"origin_id": origin_id,
|
|
1308
1327
|
"creation_time": UTCDateTime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ")
|
|
@@ -1323,7 +1342,10 @@ def process_arrival(arrival, source_loc,
|
|
|
1323
1342
|
info["channel"] = cha
|
|
1324
1343
|
phase = arrival.phase
|
|
1325
1344
|
|
|
1326
|
-
|
|
1345
|
+
if station_lookup is not None:
|
|
1346
|
+
df_sta = station_lookup.get((net, sta), df_stations.iloc[0:0])
|
|
1347
|
+
else:
|
|
1348
|
+
df_sta = df_stations[(df_stations.network == net) & (df_stations.station == sta)]
|
|
1327
1349
|
|
|
1328
1350
|
if df_sta is None:
|
|
1329
1351
|
pass
|
|
@@ -1393,18 +1415,18 @@ def process_arrival(arrival, source_loc,
|
|
|
1393
1415
|
# stations_folder = os.path.join( os.path.dirname(ebank_index_path),".stations")
|
|
1394
1416
|
|
|
1395
1417
|
#finding the corresponding stations folder for the network
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
# stations_folder = os.path.join( stations_folder,".stations")
|
|
1401
|
-
|
|
1402
|
-
os.makedirs(stations_folder, exist_ok=True)
|
|
1418
|
+
if stations_folder is None:
|
|
1419
|
+
stations_folder = get_stations_folder(ebank_index_path)
|
|
1420
|
+
os.makedirs(stations_folder, exist_ok=True)
|
|
1403
1421
|
if das:
|
|
1404
1422
|
stations_db_ev_path = os.path.join(stations_folder,f".{net}_{sta}_{cha}.db")
|
|
1405
1423
|
else:
|
|
1406
1424
|
stations_db_ev_path = os.path.join(stations_folder,f".{net}_{sta}.db")
|
|
1407
1425
|
|
|
1426
|
+
if row_sink is not None:
|
|
1427
|
+
row_sink.setdefault(stations_db_ev_path, []).append(info)
|
|
1428
|
+
return info
|
|
1429
|
+
|
|
1408
1430
|
logger.info(stations_db_ev_path)
|
|
1409
1431
|
|
|
1410
1432
|
with sqlite3.connect(stations_db_ev_path) as con:
|
|
@@ -1420,6 +1442,50 @@ def process_arrival(arrival, source_loc,
|
|
|
1420
1442
|
|
|
1421
1443
|
return info
|
|
1422
1444
|
|
|
1445
|
+
def get_stations_folder(ebank_index_path):
|
|
1446
|
+
"""The folder of the per-station databases of the network of an event bank."""
|
|
1447
|
+
network_name = os.path.basename(os.path.dirname(ebank_index_path))
|
|
1448
|
+
utdq_paths = get_utdq_paths(network_name)
|
|
1449
|
+
return utdq_paths[".utdquake/export/db"] / "stations" / f".{network_name}"
|
|
1450
|
+
|
|
1451
|
+
|
|
1452
|
+
def build_station_lookup(df_stations):
|
|
1453
|
+
"""{(network, station): rows of df_stations}, to look stations up once per arrival without filtering."""
|
|
1454
|
+
if df_stations is None or df_stations.empty:
|
|
1455
|
+
return {}
|
|
1456
|
+
return {key: group for key, group in df_stations.groupby(["network", "station"], sort=False)}
|
|
1457
|
+
|
|
1458
|
+
|
|
1459
|
+
def write_station_rows(row_sink, station_sample_size=None):
|
|
1460
|
+
"""
|
|
1461
|
+
Write the station records collected in ``row_sink`` ({db path: [records]}),
|
|
1462
|
+
one transaction per station database.
|
|
1463
|
+
|
|
1464
|
+
With ``station_sample_size``, each station database keeps at most that
|
|
1465
|
+
many records in total (spread evenly over the events of this call): enough
|
|
1466
|
+
for the station-location summary (the mean and spread of the positions
|
|
1467
|
+
back-projected from the arrivals), without one record per pick. Distances
|
|
1468
|
+
and azimuths of the arrivals themselves are not affected.
|
|
1469
|
+
|
|
1470
|
+
Returns the number of records written.
|
|
1471
|
+
"""
|
|
1472
|
+
written = 0
|
|
1473
|
+
for path, rows in row_sink.items():
|
|
1474
|
+
with sqlite3.connect(path) as con:
|
|
1475
|
+
if station_sample_size is not None:
|
|
1476
|
+
try:
|
|
1477
|
+
have = con.execute('SELECT COUNT(*) FROM "/stations/index"').fetchone()[0]
|
|
1478
|
+
except sqlite3.OperationalError:
|
|
1479
|
+
have = 0
|
|
1480
|
+
room = max(station_sample_size - have, 0)
|
|
1481
|
+
if room < len(rows):
|
|
1482
|
+
keep = np.unique(np.linspace(0, len(rows) - 1, room).round().astype(int)) if room else []
|
|
1483
|
+
rows = [rows[i] for i in keep]
|
|
1484
|
+
if rows:
|
|
1485
|
+
pd.DataFrame(rows).to_sql("/stations/index", con, if_exists="append", index=False)
|
|
1486
|
+
written += len(rows)
|
|
1487
|
+
return written
|
|
1488
|
+
|
|
1423
1489
|
# ------------------------------------------------------
|
|
1424
1490
|
def update_origin_quality(origin, distances, esazs, stations_used, used_phase_count):
|
|
1425
1491
|
"""
|
|
@@ -1458,7 +1524,10 @@ def parse_origin(origin,
|
|
|
1458
1524
|
df_stations=None,
|
|
1459
1525
|
calculate_d_az=True,
|
|
1460
1526
|
require_arrivals=True,
|
|
1461
|
-
das=False
|
|
1527
|
+
das=False,
|
|
1528
|
+
row_sink=None,
|
|
1529
|
+
station_lookup=None,
|
|
1530
|
+
stations_folder=None,
|
|
1462
1531
|
):
|
|
1463
1532
|
"""
|
|
1464
1533
|
Process all arrivals in a seismic Origin: verify picks, update
|
|
@@ -1498,7 +1567,10 @@ def parse_origin(origin,
|
|
|
1498
1567
|
df_stations=df_stations,
|
|
1499
1568
|
calculate_d_az=calculate_d_az,
|
|
1500
1569
|
ebank_index_path=ebank_index_path,
|
|
1501
|
-
das=das
|
|
1570
|
+
das=das,
|
|
1571
|
+
row_sink=row_sink,
|
|
1572
|
+
station_lookup=station_lookup,
|
|
1573
|
+
stations_folder=stations_folder,
|
|
1502
1574
|
)
|
|
1503
1575
|
# print("############### INFO",info)
|
|
1504
1576
|
if not info["available"]:
|
|
@@ -1586,8 +1658,8 @@ def xxx(origin, df_stations,
|
|
|
1586
1658
|
missing_info ={
|
|
1587
1659
|
"network": net,
|
|
1588
1660
|
"station": sta,
|
|
1589
|
-
"latitude": np.
|
|
1590
|
-
"longitude": np.
|
|
1661
|
+
"latitude": np.nan,
|
|
1662
|
+
"longitude": np.nan,
|
|
1591
1663
|
"status": "missing",
|
|
1592
1664
|
"creation_time": UTCDateTime.now().strftime("%Y-%m-%d %H:%M:%S.%f")}
|
|
1593
1665
|
missing_info = pd.DataFrame([missing_info])
|
|
@@ -1673,8 +1745,8 @@ def xxx(origin, df_stations,
|
|
|
1673
1745
|
missing_info = {
|
|
1674
1746
|
"network": net,
|
|
1675
1747
|
"station": sta,
|
|
1676
|
-
"latitude": np.
|
|
1677
|
-
"longitude": np.
|
|
1748
|
+
"latitude": np.nan,
|
|
1749
|
+
"longitude": np.nan,
|
|
1678
1750
|
"status": "missing",
|
|
1679
1751
|
"creation_time": UTCDateTime.now().strftime("%Y-%m-%d %H:%M:%S.%f")}
|
|
1680
1752
|
missing_info = pd.DataFrame([missing_info])
|
|
@@ -1776,7 +1848,8 @@ def random_string(length=8):
|
|
|
1776
1848
|
def append_stations_to_catalog(catalog: Catalog, df_stations,
|
|
1777
1849
|
ebank_index_path: str,
|
|
1778
1850
|
calculate_d_az=True,
|
|
1779
|
-
das=False
|
|
1851
|
+
das=False,
|
|
1852
|
+
station_sample_size=None,
|
|
1780
1853
|
) -> tuple[Catalog, pd.DataFrame]:
|
|
1781
1854
|
"""
|
|
1782
1855
|
Append distance and azimuth to each arrival in a catalog,
|
|
@@ -1789,13 +1862,27 @@ def append_stations_to_catalog(catalog: Catalog, df_stations,
|
|
|
1789
1862
|
df_stations : pandas.DataFrame
|
|
1790
1863
|
DataFrame containing station metadata with columns:
|
|
1791
1864
|
['network', 'station', 'latitude', 'longitude'].
|
|
1865
|
+
station_sample_size : int, optional
|
|
1866
|
+
At most this many records per station database (see
|
|
1867
|
+
:func:`write_station_rows`); None (default) keeps one per arrival.
|
|
1792
1868
|
|
|
1793
1869
|
Returns
|
|
1794
1870
|
-------
|
|
1795
1871
|
catalog : obspy.core.event.Catalog
|
|
1796
1872
|
Modified catalog with distance, azimuth, and quality metrics added.
|
|
1873
|
+
|
|
1874
|
+
Notes
|
|
1875
|
+
-----
|
|
1876
|
+
The station records are collected in memory and each station database is
|
|
1877
|
+
written once per call (one transaction per station instead of one per
|
|
1878
|
+
arrival: the per-arrival writes made this step take hours on a network
|
|
1879
|
+
filesystem). The records themselves are the same.
|
|
1797
1880
|
"""
|
|
1798
1881
|
random_id = random_string(8)
|
|
1882
|
+
stations_folder = get_stations_folder(ebank_index_path)
|
|
1883
|
+
os.makedirs(stations_folder, exist_ok=True)
|
|
1884
|
+
station_lookup = build_station_lookup(df_stations)
|
|
1885
|
+
row_sink = {}
|
|
1799
1886
|
logger.info(f"Starting to process {len(catalog.events)} events in catalog {random_id}. (This id is created just for logging purposes)")
|
|
1800
1887
|
|
|
1801
1888
|
for event in catalog:
|
|
@@ -1810,7 +1897,10 @@ def append_stations_to_catalog(catalog: Catalog, df_stations,
|
|
|
1810
1897
|
ebank_index_path=ebank_index_path,
|
|
1811
1898
|
df_stations=df_stations,
|
|
1812
1899
|
calculate_d_az=calculate_d_az,
|
|
1813
|
-
das=das
|
|
1900
|
+
das=das,
|
|
1901
|
+
row_sink=row_sink,
|
|
1902
|
+
station_lookup=station_lookup,
|
|
1903
|
+
stations_folder=stations_folder,
|
|
1814
1904
|
)
|
|
1815
1905
|
if not gd_proc:
|
|
1816
1906
|
logger.warning(f"Removing event {event.resource_id.id} due to too many missing picks.")
|
|
@@ -1818,6 +1908,10 @@ def append_stations_to_catalog(catalog: Catalog, df_stations,
|
|
|
1818
1908
|
catalog.events.remove(event)
|
|
1819
1909
|
|
|
1820
1910
|
|
|
1911
|
+
n_rows = sum(len(r) for r in row_sink.values())
|
|
1912
|
+
written = write_station_rows(row_sink, station_sample_size=station_sample_size)
|
|
1913
|
+
logger.info(f"Wrote {written} of {n_rows} station records to {len(row_sink)} station databases "
|
|
1914
|
+
f"(station_sample_size={station_sample_size}).")
|
|
1821
1915
|
logger.info(f"Finishing to process {len(catalog.events)} events in catalog {random_id}.")
|
|
1822
1916
|
|
|
1823
1917
|
return catalog
|