zhuwq0 commited on
Commit
d67d34a
·
1 Parent(s): c431e93
Files changed (2) hide show
  1. app.py +200 -3
  2. requirements.txt +2 -1
app.py CHANGED
@@ -1,7 +1,204 @@
 
 
 
 
 
 
1
  from fastapi import FastAPI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  app = FastAPI()
4
 
5
- @app.get("/")
6
- def greet_json():
7
- return {"Hello": "World!"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from json import dumps
3
+ from typing import Dict, List, Union
4
+
5
+ import numpy as np
6
+ import pandas as pd
7
  from fastapi import FastAPI
8
+ from gamma.utils import association
9
+ from kafka import KafkaProducer
10
+ from pydantic import BaseModel
11
+ from pyproj import Proj
12
+
13
+ # Kafak producer
14
+ use_kafka = False
15
+
16
+ try:
17
+ print("Connecting to k8s kafka")
18
+ BROKER_URL = "quakeflow-kafka-headless:9092"
19
+ # BROKER_URL = "34.83.137.139:9094"
20
+ producer = KafkaProducer(
21
+ bootstrap_servers=[BROKER_URL],
22
+ key_serializer=lambda x: dumps(x).encode("utf-8"),
23
+ value_serializer=lambda x: dumps(x).encode("utf-8"),
24
+ )
25
+ use_kafka = True
26
+ print("k8s kafka connection success!")
27
+ except BaseException:
28
+ print("k8s Kafka connection error")
29
+ try:
30
+ print("Connecting to local kafka")
31
+ producer = KafkaProducer(
32
+ bootstrap_servers=["localhost:9092"],
33
+ key_serializer=lambda x: dumps(x).encode("utf-8"),
34
+ value_serializer=lambda x: dumps(x).encode("utf-8"),
35
+ )
36
+ use_kafka = True
37
+ print("local kafka connection success!")
38
+ except BaseException:
39
+ print("local Kafka connection error")
40
+ print(f"Kafka status: {use_kafka}")
41
 
42
  app = FastAPI()
43
 
44
+ PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), ".."))
45
+ STATION_CSV = os.path.join(PROJECT_ROOT, "tests/stations_hawaii.csv")
46
+ # STATION_CSV = os.path.join(PROJECT_ROOT, "tests/stations.csv") ## ridgecrest
47
+
48
+
49
+ def default_config(config):
50
+ if "degree2km" not in config:
51
+ config["degree2km"] = 111.195
52
+ if "use_amplitude" not in config:
53
+ config["use_amplitude"] = True
54
+ if "use_dbscan" not in config:
55
+ config["use_dbscan"] = True
56
+ if "dbscan_eps" not in config:
57
+ config["dbscan_eps"] = 30.0
58
+ if "dbscan_min_samples" not in config:
59
+ config["dbscan_min_samples"] = 3
60
+ if "method" not in config:
61
+ config["method"] = "BGMM"
62
+ if "oversample_factor" not in config:
63
+ config["oversample_factor"] = 5
64
+ if "min_picks_per_eq" not in config:
65
+ config["min_picks_per_eq"] = 10
66
+ if "max_sigma11" not in config:
67
+ config["max_sigma11"] = 2.0
68
+ if "max_sigma22" not in config:
69
+ config["max_sigma22"] = 1.0
70
+ if "max_sigma12" not in config:
71
+ config["max_sigma12"] = 1.0
72
+ if "dims" not in config:
73
+ config["dims"] = ["x(km)", "y(km)", "z(km)"]
74
+ return config
75
+
76
+
77
+ ## set config
78
+ config = {"xlim_degree": [-156.32, -154.32], "ylim_degree": [18.39, 20.39], "z(km)": [0, 41]} ## hawaii
79
+ # config = {'xlim_degree': [-118.004, -117.004], 'ylim_degree': [35.205, 36.205], "z(km)": [0, 41]} ## ridgecrest
80
+
81
+ config = default_config(config)
82
+ config["center"] = [np.mean(config["xlim_degree"]), np.mean(config["ylim_degree"])]
83
+ config["x(km)"] = (np.array(config["xlim_degree"]) - config["center"][0]) * config["degree2km"]
84
+ config["y(km)"] = (np.array(config["ylim_degree"]) - config["center"][1]) * config["degree2km"]
85
+ config["bfgs_bounds"] = [list(config[x]) for x in config["dims"]] + [[None, None]]
86
+
87
+ for k, v in config.items():
88
+ print(f"{k}: {v}")
89
+
90
+ ## read stations
91
+ stations = pd.read_csv(STATION_CSV, delimiter="\t")
92
+ stations = stations.rename(columns={"station": "id"})
93
+ stations["x(km)"] = stations["longitude"].apply(lambda x: (x - config["center"][0]) * config["degree2km"])
94
+ stations["y(km)"] = stations["latitude"].apply(lambda x: (x - config["center"][1]) * config["degree2km"])
95
+ stations["z(km)"] = stations["elevation(m)"].apply(lambda x: -x / 1e3)
96
+
97
+ print(stations)
98
+
99
+
100
+ class Data(BaseModel):
101
+ picks: List[Dict[str, Union[float, str]]]
102
+ stations: List[Dict[str, Union[float, str]]]
103
+ config: Dict[str, Union[List[float], List[int], List[str], float, int, str]]
104
+
105
+
106
+ class Pick(BaseModel):
107
+ picks: List[Dict[str, Union[float, str]]]
108
+
109
+
110
+ def run_gamma(picks, config, stations):
111
+
112
+ proj = Proj(f"+proj=sterea +lon_0={config['center'][0]} +lat_0={config['center'][1]} +units=km")
113
+
114
+ stations[["x(km)", "y(km)"]] = stations.apply(
115
+ lambda x: pd.Series(proj(longitude=x.longitude, latitude=x.latitude)), axis=1
116
+ )
117
+ stations["z(km)"] = stations["elevation(m)"].apply(lambda x: -x / 1e3)
118
+
119
+ catalogs, assignments = association(picks, stations, config, 0, config["method"])
120
+
121
+ catalogs = pd.DataFrame(
122
+ catalogs,
123
+ columns=["time"]
124
+ + config["dims"]
125
+ + ["magnitude", "sigma_time", "sigma_amp", "cov_time_amp", "event_index", "gamma_score"],
126
+ )
127
+ catalogs[["longitude", "latitude"]] = catalogs.apply(
128
+ lambda x: pd.Series(proj(longitude=x["x(km)"], latitude=x["y(km)"], inverse=True)), axis=1
129
+ )
130
+ catalogs["depth(m)"] = catalogs["z(km)"].apply(lambda x: x * 1e3)
131
+
132
+ assignments = pd.DataFrame(assignments, columns=["pick_index", "event_index", "gamma_score"])
133
+ picks_gamma = picks.join(assignments.set_index("pick_index")).fillna(-1).astype({"event_index": int})
134
+
135
+ return catalogs, picks_gamma
136
+
137
+
138
+ @app.post("/predict_stream")
139
+ def predict(data: Pick):
140
+
141
+ picks = pd.DataFrame(data.picks)
142
+ if len(picks) == 0:
143
+ return {"catalog": [], "picks": []}
144
+
145
+ catalogs, picks_gamma = run_gamma(data, config, stations)
146
+
147
+ if use_kafka:
148
+ print("Push events to kafka...")
149
+ for event in catalogs.to_dict(orient="records"):
150
+ producer.send("gmma_events", key=event["time"], value=event)
151
+
152
+ return {"catalog": catalogs.to_dict(orient="records"), "picks": picks_gamma.to_dict(orient="records")}
153
+
154
+
155
+ @app.post("/predict")
156
+ def predict(data: Data):
157
+
158
+ picks = pd.DataFrame(data.picks)
159
+ if len(picks) == 0:
160
+ return {"catalog": [], "picks": []}
161
+
162
+ stations = pd.DataFrame(data.stations)
163
+ if len(stations) == 0:
164
+ return {"catalog": [], "picks": []}
165
+
166
+ assert "latitude" in stations
167
+ assert "longitude" in stations
168
+ assert "elevation(m)" in stations
169
+
170
+ config = data.config
171
+ config = default_config(config)
172
+
173
+ if "xlim_degree" not in config:
174
+ config["xlim_degree"] = (stations["longitude"].min(), stations["longitude"].max())
175
+ if "ylim_degree" not in config:
176
+ config["ylim_degree"] = (stations["latitude"].min(), stations["latitude"].max())
177
+ if "center" not in config:
178
+ config["center"] = [np.mean(config["xlim_degree"]), np.mean(config["ylim_degree"])]
179
+ if "x(km)" not in config:
180
+ config["x(km)"] = (
181
+ (np.array(config["xlim_degree"]) - config["center"][0])
182
+ * config["degree2km"]
183
+ * np.cos(np.deg2rad(config["center"][1]))
184
+ )
185
+ if "y(km)" not in config:
186
+ config["y(km)"] = (np.array(config["ylim_degree"]) - config["center"][1]) * config["degree2km"]
187
+ if "z(km)" not in config:
188
+ config["z(km)"] = (0, 41)
189
+ if "bfgs_bounds" not in config:
190
+ config["bfgs_bounds"] = [list(config[x]) for x in config["dims"]] + [[None, None]]
191
+
192
+ catalogs, picks_gamma = run_gamma(picks, config, stations)
193
+
194
+ if use_kafka:
195
+ print("Push events to kafka...")
196
+ for event in catalogs.to_dict(orient="records"):
197
+ producer.send("gamma_events", key=event["time"], value=event)
198
+
199
+ return {"catalog": catalogs.to_dict(orient="records"), "picks": picks_gamma.to_dict(orient="records")}
200
+
201
+
202
+ @app.get("/healthz")
203
+ def healthz():
204
+ return {"status": "ok"}
requirements.txt CHANGED
@@ -1,2 +1,3 @@
1
  fastapi
2
- uvicorn[standard]
 
 
1
  fastapi
2
+ uvicorn[standard]
3
+ gmma