TDN-IntensityGMM-TW / predict_point.py
walbcglgh's picture
Upload folder using huggingface_hub
3c5cae5 verified
Raw History Blame Contribute Delete
4.77 kB
"""Single-point Taiwan intensity prediction from the trained P-Alert GMM.
Input : earthquake (M, depth, epicenter lat/lon) + ONE target lat/lon
Output: PGA (gal), PGV (cm/s), CWA intensity
Self-contained: needs only numpy + onnxruntime. No dependency on the rest of
this repo, so you can drop the file (plus the .onnx) into your own program.
CLI:
python predict_point.py --M 6.5 --depth 10 --ev-lat 23.9 --ev-lon 121.6 \
--t-lat 24.0 --t-lon 121.7
Library:
from predict_point import predict_point
pga, pgv, level, label = predict_point(6.5, 10.0, 23.9, 121.6, 24.0, 121.7)
"""
import argparse
import math
import os
import sys
import numpy as np
import onnxruntime as ort
_HERE = os.path.dirname(os.path.abspath(__file__))
_MODEL_NAME = "TDN-IntensityGMM-TW-524k-v1.onnx"
# the artifact sits at the repo root when published, under data/ during development
DEFAULT_MODEL = next(
(p for p in (os.path.join(_HERE, _MODEL_NAME), os.path.join(_HERE, "data", _MODEL_NAME))
if os.path.exists(p)),
os.path.join(_HERE, _MODEL_NAME),
)
PGA_BOUNDS = [0.8, 2.5, 8, 25, 80, 140, 250, 440, 800] # gal
PGV_BOUNDS = [0.2, 0.7, 1.9, 5.7, 15, 30, 50, 80, 140] # cm/s
LEVELS = ["0", "1", "2", "3", "4", "5弱", "5強", "6弱", "6強", "7"]
R_EARTH_KM = 6371.0
def make_features(M, depth, ev_lat, ev_lon, t_lat, t_lon):
"""The model's 10 input features, in the exact trained order."""
p1, p2 = math.radians(ev_lat), math.radians(t_lat)
dl = math.radians(t_lon - ev_lon)
a = math.sin((p2 - p1) / 2) ** 2 + math.cos(p1) * math.cos(p2) * math.sin(dl / 2) ** 2
dist = 2 * R_EARTH_KM * math.asin(math.sqrt(min(1.0, a)))
lnR = math.log(max(math.hypot(dist, depth), 3.0))
az = math.atan2(
math.sin(dl) * math.cos(p2),
math.cos(p1) * math.sin(p2) - math.sin(p1) * math.cos(p2) * math.cos(dl),
)
return [M, depth, dist, lnR, ev_lat, ev_lon, t_lat, t_lon, math.sin(az), math.cos(az)]
def level_from_pga_pgv(pga, pgv):
"""CWA intensity index (0..9) = max of the PGA bin and the PGV bin."""
lv = sum(1 for b in PGA_BOUNDS if pga >= b)
lv = max(lv, sum(1 for b in PGV_BOUNDS if pgv >= b))
return lv
class IntensityGMM:
def __init__(self, onnx_path=DEFAULT_MODEL):
self.sess = ort.InferenceSession(onnx_path, providers=["CPUExecutionProvider"])
self.input_name = self.sess.get_inputs()[0].name
def predict_points(self, M, depth, ev_lat, ev_lon, points):
"""points = [(t_lat, t_lon), ...] -> (pga[], pgv[], level[])."""
X = np.array(
[make_features(M, depth, ev_lat, ev_lon, la, lo) for la, lo in points],
dtype=np.float32,
)
pga, pgv = self.sess.run(None, {self.input_name: X})
pga = pga.ravel().astype(float)
pgv = pgv.ravel().astype(float)
levels = [level_from_pga_pgv(float(p), float(v)) for p, v in zip(pga, pgv)]
return pga, pgv, levels
def predict_one(self, M, depth, ev_lat, ev_lon, t_lat, t_lon):
"""One target point -> (PGA gal, PGV cm/s, intensity index, intensity label)."""
pga, pgv, levels = self.predict_points(M, depth, ev_lat, ev_lon, [(t_lat, t_lon)])
lvl = levels[0]
return float(pga[0]), float(pgv[0]), lvl, LEVELS[lvl]
def predict_point(M, depth, ev_lat, ev_lon, t_lat, t_lon, model=None):
"""Convenience one-liner for a single point. Returns (pga, pgv, level, label)."""
return (model or IntensityGMM()).predict_one(M, depth, ev_lat, ev_lon, t_lat, t_lon)
def main():
try:
sys.stdout.reconfigure(encoding="utf-8")
except Exception:
pass
ap = argparse.ArgumentParser(description="Single-point PGA/PGV/intensity prediction")
ap.add_argument("--M", type=float, required=True, help="magnitude (ML)")
ap.add_argument("--depth", type=float, required=True, help="focal depth (km)")
ap.add_argument("--ev-lat", type=float, required=True, dest="ev_lat", help="epicenter latitude")
ap.add_argument("--ev-lon", type=float, required=True, dest="ev_lon", help="epicenter longitude")
ap.add_argument("--t-lat", type=float, required=True, dest="t_lat", help="target latitude")
ap.add_argument("--t-lon", type=float, required=True, dest="t_lon", help="target longitude")
ap.add_argument("--model", default=DEFAULT_MODEL, help="path to the ONNX model")
args = ap.parse_args()
pga, pgv, level, label = predict_point(
args.M, args.depth, args.ev_lat, args.ev_lon, args.t_lat, args.t_lon,
IntensityGMM(args.model),
)
print(f"target ({args.t_lat}, {args.t_lon})")
print(f"PGA {pga:.3f} gal")
print(f"PGV {pgv:.3f} cm/s")
print(f"震度 {label} ({level})")
if __name__ == "__main__":
main()