|
|
|
import json |
|
import multiprocessing as mp |
|
import os |
|
from dataclasses import dataclass, asdict |
|
|
|
import matplotlib.pyplot as plt |
|
import numpy as np |
|
import pandas as pd |
|
from fastapi import FastAPI |
|
from pyproj import Proj |
|
|
|
import adloc |
|
|
|
from adloc.eikonal2d import init_eikonal2d |
|
from adloc.sacloc2d import ADLoc |
|
from adloc.utils import invert_location, invert_location_iter |
|
|
|
app = FastAPI() |
|
|
|
|
|
@app.get("/") |
|
def greet_json(): |
|
return {"Hello": "World!"} |
|
|
|
|
|
@app.post("/predict/") |
|
def predict(picks: dict, stations: dict, config: dict): |
|
picks = picks["data"] |
|
stations = stations["data"] |
|
picks = pd.DataFrame(picks) |
|
picks["phase_time"] = pd.to_datetime(picks["phase_time"]) |
|
stations = pd.DataFrame(stations) |
|
picks_, events_ = run_adloc(picks, stations, config) |
|
picks_ = picks_.to_dict(orient="records") |
|
events_ = events_.to_dict(orient="records") |
|
|
|
return {"picks": picks_, "events": events_} |
|
|
|
|
|
def set_config(region="ridgecrest"): |
|
|
|
|
|
config = { |
|
"min_picks": 8, |
|
"min_picks_ratio": 0.2, |
|
"max_residual_time": 1.0, |
|
"max_residual_amplitude": 1.0, |
|
"min_score": 0.6, |
|
"min_s_picks": 2, |
|
"min_p_picks": 2, |
|
"use_amplitude": False, |
|
} |
|
|
|
|
|
|
|
if region.lower() == "ridgecrest": |
|
config.update( |
|
{ |
|
"region": "ridgecrest", |
|
"minlongitude": -118.004, |
|
"maxlongitude": -117.004, |
|
"minlatitude": 35.205, |
|
"maxlatitude": 36.205, |
|
"mindepth_km": 0.0, |
|
"maxdepth_km": 30.0, |
|
} |
|
) |
|
|
|
|
|
lon0 = (config["minlongitude"] + config["maxlongitude"]) / 2 |
|
lat0 = (config["minlatitude"] + config["maxlatitude"]) / 2 |
|
proj = Proj(f"+proj=sterea +lon_0={lon0} +lat_0={lat0} +units=km") |
|
xmin, ymin = proj(config["minlongitude"], config["minlatitude"]) |
|
xmax, ymax = proj(config["maxlongitude"], config["maxlatitude"]) |
|
zmin, zmax = config["mindepth_km"], config["maxdepth_km"] |
|
xlim_km = (xmin, xmax) |
|
ylim_km = (ymin, ymax) |
|
zlim_km = (zmin, zmax) |
|
|
|
config.update( |
|
{ |
|
"xlim_km": xlim_km, |
|
"ylim_km": ylim_km, |
|
"zlim_km": zlim_km, |
|
"proj": proj, |
|
} |
|
) |
|
|
|
|
|
zz = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 30.0] |
|
vp = [4.746, 4.793, 4.799, 5.045, 5.721, 5.879, 6.504, 6.708, 6.725, 7.800] |
|
vs = [2.469, 2.470, 2.929, 2.930, 3.402, 3.403, 3.848, 3.907, 3.963, 4.500] |
|
h = 0.3 |
|
|
|
vel = {"Z": zz, "P": vp, "S": vs} |
|
eikonal = { |
|
"vel": vel, |
|
"h": h, |
|
"xlim_km": xlim_km, |
|
"ylim_km": ylim_km, |
|
"zlim_km": zlim_km, |
|
} |
|
eikonal = init_eikonal2d(eikonal) |
|
config["eikonal"] = eikonal |
|
|
|
config["bfgs_bounds"] = ( |
|
(xlim_km[0] - 1, xlim_km[1] + 1), |
|
(ylim_km[0] - 1, ylim_km[1] + 1), |
|
(0, zlim_km[1] + 1), |
|
(None, None), |
|
) |
|
|
|
config["event_index"] = 0 |
|
|
|
return config |
|
|
|
config = set_config() |
|
|
|
|
|
|
|
def run_adloc(picks, stations, config_): |
|
|
|
|
|
|
|
config.update(config_) |
|
|
|
proj = config["proj"] |
|
|
|
|
|
stations[["x_km", "y_km"]] = stations.apply( |
|
lambda x: pd.Series(proj(longitude=x.longitude, latitude=x.latitude)), axis=1 |
|
) |
|
stations["z_km"] = stations["elevation_m"].apply(lambda x: -x / 1e3) |
|
|
|
|
|
|
|
mapping_phase_type_int = {"P": 0, "S": 1} |
|
picks["phase_type"] = picks["phase_type"].map(mapping_phase_type_int) |
|
if "phase_amplitude" in picks.columns: |
|
picks["phase_amplitude"] = picks["phase_amplitude"].apply(lambda x: np.log10(x) + 2.0) |
|
|
|
|
|
|
|
stations["idx_sta"] = np.arange(len(stations)) |
|
picks = picks.merge(stations[["station_id", "idx_sta"]], on="station_id") |
|
picks["idx_eve"] = config["event_index"] |
|
|
|
|
|
estimator = ADLoc(config, stations=stations[["x_km", "y_km", "z_km"]].values, eikonal=config["eikonal"]) |
|
|
|
|
|
picks, events = invert_location_iter(picks, stations, config, estimator, events_init=None, iter=0) |
|
|
|
if (picks is None) or (events is None): |
|
return None, None |
|
|
|
|
|
if "event_index" not in events.columns: |
|
events["event_index"] = events.merge(picks[["idx_eve", "event_index"]], on="idx_eve")["event_index"] |
|
events[["longitude", "latitude"]] = events.apply( |
|
lambda x: pd.Series(proj(x["x_km"], x["y_km"], inverse=True)), axis=1 |
|
) |
|
events["depth_km"] = events["z_km"] |
|
events.drop(["idx_eve", "x_km", "y_km", "z_km"], axis=1, inplace=True, errors="ignore") |
|
events.sort_values(["time"], inplace=True) |
|
|
|
picks.rename({"mask": "adloc_mask", "residual_s": "adloc_residual_s"}, axis=1, inplace=True) |
|
picks["phase_type"] = picks["phase_type"].map({0: "P", 1: "S"}) |
|
picks.drop(["idx_eve", "idx_sta"], axis=1, inplace=True, errors="ignore") |
|
picks.sort_values(["phase_time"], inplace=True) |
|
|
|
return picks, events |
|
|
|
|
|
|