ADLoc / app.py
zhuwq0's picture
update
2642322
raw
history blame
5.07 kB
# %%
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,
}
# ## Domain
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,
}
)
## Eikonal for 1D velocity model
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), # x
(ylim_km[0] - 1, ylim_km[1] + 1), # y
(0, zlim_km[1] + 1), # z
(None, None), # t
)
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) # convert to log10(cm/s)
# %%
# reindex in case the index does not start from 0 or is not continuous
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