202 lines
5.6 KiBLFS
Bash
202 lines
5.6 KiBLFS
Bash
#!/bin/bash
|
|
set -e
|
|
|
|
pip install --no-cache-dir seisbench==0.10.2
|
|
|
|
python3 << 'EOF'
|
|
import os
|
|
from pathlib import Path
|
|
from glob import glob
|
|
|
|
import numpy as np
|
|
import obspy
|
|
import pandas as pd
|
|
import seisbench.models as sbm
|
|
from obspy import Stream, Trace, UTCDateTime
|
|
|
|
|
|
def load_npz_inputs(npz_dir: str | Path) -> dict[str, obspy.Stream]:
|
|
npz_dir = Path(npz_dir)
|
|
npz_files = sorted(glob(str(npz_dir / "*.npz")))
|
|
|
|
required_fields = ["data", "dt", "start_time", "network", "station", "channels"]
|
|
|
|
streams = {}
|
|
|
|
for npz_file in npz_files:
|
|
filename = os.path.basename(npz_file)
|
|
data = np.load(npz_file, allow_pickle=False)
|
|
|
|
missing_fields = [field for field in required_fields if field not in data]
|
|
if missing_fields:
|
|
print(f"Skipping {filename}: missing required fields {missing_fields}")
|
|
continue
|
|
|
|
waveform = data["data"] * 1e10 # (12000, 3)
|
|
dt = float(data["dt"])
|
|
sampling_rate = 1.0 / dt
|
|
|
|
network = str(data["network"])
|
|
station = str(data["station"])
|
|
channels = str(data["channels"])
|
|
start_time_str = str(data["start_time"])
|
|
|
|
if "," in channels:
|
|
channel_list = channels.split(",")
|
|
else:
|
|
channel_list = [f"HH{c}" for c in "ENZ"]
|
|
|
|
try:
|
|
start_time = UTCDateTime(start_time_str)
|
|
except Exception:
|
|
start_time = UTCDateTime(0)
|
|
|
|
stream = Stream()
|
|
for i, ch in enumerate(channel_list):
|
|
if i < waveform.shape[1]:
|
|
tr = Trace(data=waveform[:, i].astype(np.float64))
|
|
tr.stats.network = network
|
|
tr.stats.station = station
|
|
tr.stats.channel = ch if len(ch) >= 2 else f"HH{ch}"
|
|
tr.stats.sampling_rate = sampling_rate
|
|
tr.stats.starttime = start_time
|
|
stream.append(tr)
|
|
|
|
streams[filename] = stream
|
|
print(f"Loaded {filename}: {len(stream)} traces")
|
|
|
|
return streams
|
|
|
|
|
|
def load_denoiser(device: str = None) -> sbm.DeepDenoiser:
|
|
denoiser = sbm.DeepDenoiser.from_pretrained("original")
|
|
|
|
if device:
|
|
denoiser.to(device)
|
|
else:
|
|
denoiser.to_preferred_device()
|
|
|
|
return denoiser
|
|
|
|
|
|
def load_models(model_names: list[str] = None, device: str = None) -> dict:
|
|
if model_names is None:
|
|
model_names = ["phasenet", "eqtransformer", "gpd"]
|
|
|
|
models = {}
|
|
|
|
for name in model_names:
|
|
name_lower = name.lower()
|
|
if name_lower == "phasenet":
|
|
model = sbm.PhaseNet.from_pretrained("original")
|
|
elif name_lower == "eqtransformer":
|
|
model = sbm.EQTransformer.from_pretrained("original")
|
|
elif name_lower == "gpd":
|
|
model = sbm.GPD.from_pretrained("stead")
|
|
else:
|
|
print(f"Unknown model: {name}, skipping")
|
|
continue
|
|
|
|
if device:
|
|
model.to(device)
|
|
else:
|
|
model.to_preferred_device()
|
|
|
|
models[name_lower] = model
|
|
|
|
return models
|
|
|
|
|
|
def run_inference(
|
|
streams: dict[str, obspy.Stream],
|
|
models: dict,
|
|
output_csv: str | Path = None,
|
|
denoiser: sbm.DeepDenoiser = None,
|
|
) -> dict:
|
|
all_results = {}
|
|
|
|
for model_name, model in models.items():
|
|
print(f"\n{'='*60}")
|
|
print(f"Running inference with {model_name}")
|
|
if denoiser:
|
|
print("(with DeepDenoiser preprocessing)")
|
|
print(f"{'='*60}")
|
|
|
|
all_picks = []
|
|
|
|
for filename, stream in streams.items():
|
|
sampling_rate = stream[0].stats.sampling_rate
|
|
|
|
try:
|
|
if denoiser:
|
|
input_stream = denoiser.annotate(stream)
|
|
else:
|
|
input_stream = stream
|
|
|
|
outputs = model.classify(input_stream)
|
|
picks = outputs.picks
|
|
|
|
start_time = stream[0].stats.starttime
|
|
|
|
for pick in picks:
|
|
pick_idx = int((pick.peak_time - start_time) * sampling_rate)
|
|
|
|
all_picks.append({
|
|
"file_name": filename,
|
|
"phase": pick.phase,
|
|
"pick_idx": pick_idx,
|
|
"probability": pick.peak_value,
|
|
})
|
|
|
|
file_p_picks = [p for p in all_picks if p["file_name"] == filename and p["phase"] == "P"]
|
|
file_s_picks = [p for p in all_picks if p["file_name"] == filename and p["phase"] == "S"]
|
|
|
|
print(f" {filename}: P picks: {len(file_p_picks)}, S picks: {len(file_s_picks)}")
|
|
|
|
except Exception as e:
|
|
print(f" Error processing {filename}: {e}")
|
|
continue
|
|
|
|
all_results[model_name] = all_picks
|
|
|
|
# one row per pick
|
|
if output_csv and all_picks:
|
|
picks_df = pd.DataFrame(all_picks)
|
|
picks_df.to_csv(output_csv, index=False)
|
|
print(f"\nSaved {len(picks_df)} picks to {output_csv}")
|
|
|
|
return all_results
|
|
|
|
|
|
def main():
|
|
npz_inputs_dir = Path("/root/data")
|
|
output_csv = Path("/root/results.csv")
|
|
|
|
print("="*60)
|
|
print("SeisBench Phase Picking Inference")
|
|
print("="*60)
|
|
|
|
print("\n1. Loading NPZ input data...")
|
|
streams = load_npz_inputs(npz_inputs_dir)
|
|
print(f"Loaded {len(streams)} samples")
|
|
|
|
if not streams:
|
|
print("No data loaded. Exiting.")
|
|
return None
|
|
|
|
print("\n2. Loading SeisBench models...")
|
|
models = load_models(["phasenet"])
|
|
denoiser = load_denoiser()
|
|
|
|
print("\n3. Running inference...")
|
|
results = run_inference(streams, models, output_csv, denoiser=None)
|
|
|
|
print("\n" + "="*60)
|
|
print(f"Results saved to: {output_csv}")
|
|
|
|
return results
|
|
|
|
main()
|
|
|
|
EOF
|