Download predict_point.py from walbcglgh/TDN-IntensityGMM-TW: direct link, hf CLI and curl.
- Browser
- Download file 4.77 kB
-
https://huggingface.co/walbcglgh/TDN-IntensityGMM-TW/resolve/main/predict_point.py
- Command line
-
hf download hf://walbcglgh/TDN-IntensityGMM-TW/predict_point.py
-
curl -L -o predict_point.py https://huggingface.co/walbcglgh/TDN-IntensityGMM-TW/resolve/main/predict_point.py
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() | |