ADLoc 1.0.2__tar.gz → 1.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {adloc-1.0.2 → adloc-1.0.4}/ADLoc.egg-info/PKG-INFO +1 -1
- {adloc-1.0.2 → adloc-1.0.4}/PKG-INFO +1 -1
- {adloc-1.0.2 → adloc-1.0.4}/adloc/eikonal2d.py +41 -0
- {adloc-1.0.2 → adloc-1.0.4}/adloc/sacloc2d.py +54 -49
- {adloc-1.0.2 → adloc-1.0.4}/adloc/utils.py +36 -8
- {adloc-1.0.2 → adloc-1.0.4}/setup.py +1 -1
- {adloc-1.0.2 → adloc-1.0.4}/ADLoc.egg-info/SOURCES.txt +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/ADLoc.egg-info/dependency_links.txt +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/ADLoc.egg-info/requires.txt +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/ADLoc.egg-info/top_level.txt +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/adloc/__init__.py +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/adloc/_ransac.py +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/adloc/adloc.py +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/adloc/data.py +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/adloc/eikonal3d.py +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/adloc/gmpe.py +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/adloc/inversion.py +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/adloc/sacloc3d.py +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/adloc/test_grad.py +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/setup.cfg +0 -0
- {adloc-1.0.2 → adloc-1.0.4}/tests/test_ad.py +0 -0
|
@@ -244,6 +244,47 @@ def calc_traveltime(event_locs, station_locs, phase_type, eikonal):
|
|
|
244
244
|
return tt
|
|
245
245
|
|
|
246
246
|
|
|
247
|
+
def calc_grad_traveltime(event_loc, station_loc, phase_type, eikonal):
|
|
248
|
+
"""
|
|
249
|
+
Gradient of the eikonal traveltime w.r.t. event location, computed directly from coordinates.
|
|
250
|
+
event_loc: (num_event, 3) or (1, 3) array of event locations
|
|
251
|
+
station_loc: (num_station, 3) array of station locations
|
|
252
|
+
phase_type: (num_station,) array of phase type (0 for P, 1 for S)
|
|
253
|
+
eikonal: dictionary of the eikonal timetable
|
|
254
|
+
"""
|
|
255
|
+
x = event_loc[:, 0] - station_loc[:, 0]
|
|
256
|
+
y = event_loc[:, 1] - station_loc[:, 1]
|
|
257
|
+
z = event_loc[:, 2] - station_loc[:, 2]
|
|
258
|
+
r = np.sqrt(x**2 + y**2)
|
|
259
|
+
|
|
260
|
+
rgrid0 = eikonal["rgrid"][0]
|
|
261
|
+
zgrid0 = eikonal["zgrid"][0]
|
|
262
|
+
nr = eikonal["nr"]
|
|
263
|
+
nz = eikonal["nz"]
|
|
264
|
+
h = eikonal["h"]
|
|
265
|
+
|
|
266
|
+
if isinstance(phase_type, list):
|
|
267
|
+
phase_type = np.array(phase_type)
|
|
268
|
+
|
|
269
|
+
dt_dr = np.zeros(len(phase_type))
|
|
270
|
+
dt_dz = np.zeros(len(phase_type))
|
|
271
|
+
|
|
272
|
+
p_index = phase_type == 0
|
|
273
|
+
s_index = phase_type == 1
|
|
274
|
+
|
|
275
|
+
dt_dr[p_index] = _interp(eikonal["grad_up"][0], r[p_index], z[p_index], rgrid0, zgrid0, nr, nz, h)
|
|
276
|
+
dt_dr[s_index] = _interp(eikonal["grad_us"][0], r[s_index], z[s_index], rgrid0, zgrid0, nr, nz, h)
|
|
277
|
+
dt_dz[p_index] = _interp(eikonal["grad_up"][1], r[p_index], z[p_index], rgrid0, zgrid0, nr, nz, h)
|
|
278
|
+
dt_dz[s_index] = _interp(eikonal["grad_us"][1], r[s_index], z[s_index], rgrid0, zgrid0, nr, nz, h)
|
|
279
|
+
|
|
280
|
+
dt_dx = dt_dr * x / (r + 1e-6)
|
|
281
|
+
dt_dy = dt_dr * y / (r + 1e-6)
|
|
282
|
+
|
|
283
|
+
grad = np.column_stack((dt_dx, dt_dy, dt_dz))
|
|
284
|
+
|
|
285
|
+
return grad
|
|
286
|
+
|
|
287
|
+
|
|
247
288
|
# def grad_traveltime(event_loc, station_loc, phase_type, eikonal):
|
|
248
289
|
def grad_traveltime(event_index, station_index, phase_type, events, stations, eikonal):
|
|
249
290
|
|
|
@@ -4,7 +4,7 @@ import scipy
|
|
|
4
4
|
from sklearn.base import BaseEstimator
|
|
5
5
|
|
|
6
6
|
from ._ransac import RANSACRegressor
|
|
7
|
-
from .eikonal2d import
|
|
7
|
+
from .eikonal2d import calc_grad_traveltime, calc_traveltime, init_eikonal2d
|
|
8
8
|
from .gmpe import calc_amp, calc_mag
|
|
9
9
|
|
|
10
10
|
# from sklearn.linear_model import RANSACRegressor
|
|
@@ -13,9 +13,12 @@ np.random.seed(0)
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class ADLoc(BaseEstimator):
|
|
16
|
-
def __init__(self, config,
|
|
16
|
+
def __init__(self, config, num_event=1, events=None, eikonal=None):
|
|
17
17
|
"""
|
|
18
18
|
events: [x, y, z, t]
|
|
19
|
+
Station locations are not stored on the estimator; they are carried in the
|
|
20
|
+
forward call via X columns [x_km, y_km, z_km] so that a per-event projection
|
|
21
|
+
stays consistent between the picks, the initial location and the inversion.
|
|
19
22
|
"""
|
|
20
23
|
xlim = config["xlim_km"]
|
|
21
24
|
ylim = config["ylim_km"]
|
|
@@ -27,7 +30,6 @@ class ADLoc(BaseEstimator):
|
|
|
27
30
|
if eikonal is None:
|
|
28
31
|
print(f"Using default velocity: {vel}")
|
|
29
32
|
self.config = config
|
|
30
|
-
self.stations = stations
|
|
31
33
|
self.vel = vel
|
|
32
34
|
|
|
33
35
|
self.num_event = num_event
|
|
@@ -48,19 +50,21 @@ class ADLoc(BaseEstimator):
|
|
|
48
50
|
self.use_amplitude = False
|
|
49
51
|
|
|
50
52
|
@staticmethod
|
|
51
|
-
def l2_loss_grad(event, X, y, vel={0: 6.0, 1: 6.0 / 1.75},
|
|
53
|
+
def l2_loss_grad(event, X, y, vel={0: 6.0, 1: 6.0 / 1.75}, eikonal=None, reduce_loss=False):
|
|
52
54
|
"""
|
|
53
|
-
X:
|
|
55
|
+
X: columns [station_index, phase_type, phase_weight, x_km, y_km, z_km]
|
|
56
|
+
Station locations are carried in X[:, 3:6] so they follow the (per-event) projection.
|
|
54
57
|
"""
|
|
55
58
|
station_index = X[:, 0].astype(int)
|
|
56
59
|
phase_type = X[:, 1].astype(int)
|
|
57
60
|
phase_weight = X[:, 2]
|
|
61
|
+
station_loc = X[:, 3:6]
|
|
58
62
|
|
|
59
63
|
if eikonal is None:
|
|
60
64
|
v = np.array([vel[t] for t in phase_type])
|
|
61
|
-
tt = np.linalg.norm(event[:3] -
|
|
65
|
+
tt = np.linalg.norm(event[:3] - station_loc, axis=-1) / v + event[3]
|
|
62
66
|
else:
|
|
63
|
-
tt =
|
|
67
|
+
tt = calc_traveltime(event[np.newaxis, :3], station_loc, phase_type, eikonal) + event[3]
|
|
64
68
|
|
|
65
69
|
if reduce_loss:
|
|
66
70
|
loss = 0.5 * np.sum((tt - y) ** 2 * phase_weight)
|
|
@@ -70,12 +74,12 @@ class ADLoc(BaseEstimator):
|
|
|
70
74
|
J = np.ones((len(X), 4))
|
|
71
75
|
if eikonal is None:
|
|
72
76
|
J[:, :3] = (
|
|
73
|
-
(event[:3] -
|
|
74
|
-
/ np.linalg.norm(event[:3] -
|
|
77
|
+
(event[:3] - station_loc)
|
|
78
|
+
/ np.linalg.norm(event[:3] - station_loc, axis=-1, keepdims=True)
|
|
75
79
|
/ v[:, np.newaxis]
|
|
76
80
|
)
|
|
77
81
|
else:
|
|
78
|
-
grad =
|
|
82
|
+
grad = calc_grad_traveltime(event[np.newaxis, :3], station_loc, phase_type, eikonal)
|
|
79
83
|
J[:, :3] = grad
|
|
80
84
|
|
|
81
85
|
if reduce_loss:
|
|
@@ -86,17 +90,18 @@ class ADLoc(BaseEstimator):
|
|
|
86
90
|
return loss, J
|
|
87
91
|
|
|
88
92
|
@staticmethod
|
|
89
|
-
def huber_loss_grad(event, X, y, vel={0: 6.0, 1: 6.0 / 1.75},
|
|
93
|
+
def huber_loss_grad(event, X, y, vel={0: 6.0, 1: 6.0 / 1.75}, eikonal=None, sigma=1.0):
|
|
90
94
|
|
|
91
95
|
station_index = X[:, 0].astype(int)
|
|
92
96
|
phase_type = X[:, 1].astype(int)
|
|
93
97
|
phase_weight = X[:, 2]
|
|
98
|
+
station_loc = X[:, 3:6]
|
|
94
99
|
|
|
95
100
|
if eikonal is None:
|
|
96
101
|
v = np.array([vel[t] for t in phase_type])
|
|
97
|
-
tt = np.linalg.norm(event[:3] -
|
|
102
|
+
tt = np.linalg.norm(event[:3] - station_loc, axis=-1) / v + event[3]
|
|
98
103
|
else:
|
|
99
|
-
tt =
|
|
104
|
+
tt = calc_traveltime(event[np.newaxis, :3], station_loc, phase_type, eikonal) + event[3]
|
|
100
105
|
|
|
101
106
|
t_diff = tt - y
|
|
102
107
|
l1 = np.squeeze((np.abs(t_diff) > sigma))
|
|
@@ -109,12 +114,12 @@ class ADLoc(BaseEstimator):
|
|
|
109
114
|
J = np.ones((len(X), 4))
|
|
110
115
|
if eikonal is None:
|
|
111
116
|
J[:, :3] = (
|
|
112
|
-
(event[:3] -
|
|
113
|
-
/ np.linalg.norm(event[:3] -
|
|
117
|
+
(event[:3] - station_loc)
|
|
118
|
+
/ np.linalg.norm(event[:3] - station_loc, axis=-1, keepdims=True)
|
|
114
119
|
/ v[:, np.newaxis]
|
|
115
120
|
)
|
|
116
121
|
else:
|
|
117
|
-
grad =
|
|
122
|
+
grad = calc_grad_traveltime(event[np.newaxis, :3], station_loc, phase_type, eikonal)
|
|
118
123
|
J[:, :3] = grad
|
|
119
124
|
|
|
120
125
|
J = np.sum(
|
|
@@ -137,7 +142,7 @@ class ADLoc(BaseEstimator):
|
|
|
137
142
|
x0=self.events[event_index],
|
|
138
143
|
method="L-BFGS-B",
|
|
139
144
|
jac=True,
|
|
140
|
-
args=(X, yt, self.vel, self.
|
|
145
|
+
args=(X, yt, self.vel, self.eikonal),
|
|
141
146
|
# bounds=[
|
|
142
147
|
# (self.config["xlim_km"][0], self.config["xlim_km"][1]),
|
|
143
148
|
# (self.config["ylim_km"][0], self.config["ylim_km"][1]),
|
|
@@ -153,8 +158,9 @@ class ADLoc(BaseEstimator):
|
|
|
153
158
|
if self.use_amplitude:
|
|
154
159
|
station_index = X[:, 0].astype(int)
|
|
155
160
|
phase_weight = X[:, 2:3]
|
|
161
|
+
station_loc = X[:, 3:6]
|
|
156
162
|
self.magnitudes[event_index] = calc_mag(
|
|
157
|
-
ya[:, np.newaxis], self.events[event_index, :3],
|
|
163
|
+
ya[:, np.newaxis], self.events[event_index, :3], station_loc, phase_weight
|
|
158
164
|
)
|
|
159
165
|
|
|
160
166
|
return self
|
|
@@ -170,7 +176,7 @@ class ADLoc(BaseEstimator):
|
|
|
170
176
|
# event0 = self.events[event_index]
|
|
171
177
|
# delta = np.inf
|
|
172
178
|
# for i in range(max_iter):
|
|
173
|
-
# loss, grad = self.l2_loss_grad(event0, X, yt, self.vel, self.
|
|
179
|
+
# loss, grad = self.l2_loss_grad(event0, X, yt, self.vel, self.eikonal)
|
|
174
180
|
# A, b = grad, loss
|
|
175
181
|
# dx = scipy.sparse.linalg.lsqr(A, b, damp=damp)[0]
|
|
176
182
|
# self.events[event_index] -= dx
|
|
@@ -180,36 +186,37 @@ class ADLoc(BaseEstimator):
|
|
|
180
186
|
# break
|
|
181
187
|
|
|
182
188
|
# if self.use_amplitude:
|
|
183
|
-
# station_index = X[:, 0].astype(int)
|
|
184
189
|
# phase_weight = X[:, 2:3]
|
|
190
|
+
# station_loc = X[:, 3:6]
|
|
185
191
|
# self.magnitudes[event_index] = calc_mag(
|
|
186
|
-
# ya[:, np.newaxis], self.events[event_index, :3],
|
|
192
|
+
# ya[:, np.newaxis], self.events[event_index, :3], station_loc, phase_weight
|
|
187
193
|
# )
|
|
188
194
|
|
|
189
195
|
# return self
|
|
190
196
|
|
|
191
197
|
def predict(self, X, event_index=0):
|
|
192
198
|
"""
|
|
193
|
-
X:
|
|
199
|
+
X: columns [station_index, phase_type, phase_weight, x_km, y_km, z_km]
|
|
194
200
|
"""
|
|
195
201
|
station_index = X[:, 0].astype(int)
|
|
196
202
|
phase_type = X[:, 1].astype(int)
|
|
203
|
+
station_loc = X[:, 3:6]
|
|
197
204
|
|
|
198
205
|
if self.eikonal is None:
|
|
199
206
|
v = np.array([self.vel[t] for t in phase_type])
|
|
200
207
|
tt = (
|
|
201
|
-
np.linalg.norm(self.events[event_index, :3] -
|
|
208
|
+
np.linalg.norm(self.events[event_index, :3] - station_loc, axis=-1) / v
|
|
202
209
|
+ self.events[event_index, 3]
|
|
203
210
|
)
|
|
204
211
|
else:
|
|
205
212
|
tt = (
|
|
206
|
-
|
|
213
|
+
calc_traveltime(self.events[event_index][np.newaxis, :3], station_loc, phase_type, self.eikonal)
|
|
207
214
|
+ self.events[event_index, 3]
|
|
208
215
|
)
|
|
209
216
|
|
|
210
217
|
if self.use_amplitude:
|
|
211
218
|
amp = calc_amp(
|
|
212
|
-
self.magnitudes[event_index], self.events[event_index, :3],
|
|
219
|
+
self.magnitudes[event_index], self.events[event_index, :3], station_loc
|
|
213
220
|
).squeeze()
|
|
214
221
|
return np.array([tt, amp]).T
|
|
215
222
|
else:
|
|
@@ -217,16 +224,17 @@ class ADLoc(BaseEstimator):
|
|
|
217
224
|
|
|
218
225
|
def weight(self, X, event_index=0):
|
|
219
226
|
"""
|
|
220
|
-
X:
|
|
227
|
+
X: columns [station_index, phase_type, phase_weight, x_km, y_km, z_km]
|
|
221
228
|
"""
|
|
222
229
|
station_index = X[:, 0].astype(int)
|
|
223
230
|
phase_type = X[:, 1].astype(int)
|
|
231
|
+
station_loc = X[:, 3:6]
|
|
224
232
|
|
|
225
233
|
if self.eikonal is None:
|
|
226
234
|
v = np.array([self.vel[t] for t in phase_type])
|
|
227
|
-
tt = np.linalg.norm(self.events[event_index, :3] -
|
|
235
|
+
tt = np.linalg.norm(self.events[event_index, :3] - station_loc, axis=-1) / v
|
|
228
236
|
else:
|
|
229
|
-
tt =
|
|
237
|
+
tt = calc_traveltime(self.events[event_index][np.newaxis, :3], station_loc, phase_type, self.eikonal)
|
|
230
238
|
|
|
231
239
|
return np.array([tt]).T
|
|
232
240
|
|
|
@@ -562,21 +570,20 @@ if __name__ == "__main__":
|
|
|
562
570
|
estimator = ADLoc(
|
|
563
571
|
config,
|
|
564
572
|
# events=events[["x_km", "y_km", "z_km", "event_time"]].values + np.random.rand(num_event, 4) * 10,
|
|
565
|
-
stations=stations[["x_km", "y_km", "z_km"]].values,
|
|
566
573
|
num_event=num_event,
|
|
567
574
|
eikonal=eikonal_config,
|
|
568
575
|
)
|
|
569
|
-
output = estimator.predict(X[["idx_sta", "type"]].values, event_index=event_index)
|
|
576
|
+
output = estimator.predict(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values, event_index=event_index)
|
|
570
577
|
tt_init = output[:, 0]
|
|
571
578
|
amp_init = output[:, 1]
|
|
572
|
-
estimator.score(X[["idx_sta", "type"]].values, y=X[["t_s", "amp"]].values, event_index=event_index)
|
|
579
|
+
estimator.score(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values, y=X[["t_s", "amp"]].values, event_index=event_index)
|
|
573
580
|
|
|
574
581
|
print(f"Init event loc: {estimator.events[event_index].round(3)}")
|
|
575
582
|
|
|
576
583
|
# %%
|
|
577
|
-
estimator.fit(X[["idx_sta", "type", "score", "
|
|
578
|
-
estimator.score(X[["idx_sta", "type"]].values, y=X[["t_s", "amp"]].values, event_index=event_index)
|
|
579
|
-
output = estimator.predict(X[["idx_sta", "type"]].values, event_index=event_index)
|
|
584
|
+
estimator.fit(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values, y=X[["t_s", "amp"]].values, event_index=event_index)
|
|
585
|
+
estimator.score(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values, y=X[["t_s", "amp"]].values, event_index=event_index)
|
|
586
|
+
output = estimator.predict(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values, event_index=event_index)
|
|
580
587
|
tt = output[:, 0]
|
|
581
588
|
amp = output[:, 1]
|
|
582
589
|
print("Basic:")
|
|
@@ -605,24 +612,23 @@ if __name__ == "__main__":
|
|
|
605
612
|
fig.savefig(f"{data_path}/picks_xt_{event_index}_adloc.png")
|
|
606
613
|
|
|
607
614
|
# %%
|
|
615
|
+
# RANSAC locates a single event, so drive it with a one-event estimator (slot 0).
|
|
608
616
|
reg = RANSACRegressor(
|
|
609
|
-
estimator=ADLoc(
|
|
610
|
-
config, stations=stations[["x_km", "y_km", "z_km"]].values, num_event=num_event, eikonal=eikonal_config
|
|
611
|
-
),
|
|
617
|
+
estimator=ADLoc(config, num_event=1, eikonal=eikonal_config),
|
|
612
618
|
random_state=0,
|
|
613
619
|
min_samples=4,
|
|
614
620
|
residual_threshold=[1.0, 0.5],
|
|
615
621
|
)
|
|
616
|
-
reg.fit(X[["idx_sta", "type", "score", "
|
|
622
|
+
reg.fit(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values, X[["t_s", "amp"]].values)
|
|
617
623
|
mask_invt = reg.inlier_mask_
|
|
618
624
|
estimator = reg.estimator_
|
|
619
|
-
estimator.score(X[["idx_sta", "type"]].values, y=X[["t_s", "amp"]].values
|
|
620
|
-
output = estimator.predict(X[["idx_sta", "type"]].values
|
|
625
|
+
estimator.score(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values, y=X[["t_s", "amp"]].values)
|
|
626
|
+
output = estimator.predict(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values)
|
|
621
627
|
tt = output[:, 0]
|
|
622
628
|
amp = output[:, 1]
|
|
623
629
|
print("RANSAC:")
|
|
624
630
|
print(f"True event loc: {event_loc[['x_km', 'y_km', 'z_km']].values.astype(float).round(3)}")
|
|
625
|
-
print(f"Invt event loc: {estimator.events[
|
|
631
|
+
print(f"Invt event loc: {estimator.events[0].round(3)}")
|
|
626
632
|
|
|
627
633
|
# %% add random picks
|
|
628
634
|
config["vel"] = {"P": 6.0, "S": 6.0 / 1.73}
|
|
@@ -680,24 +686,23 @@ if __name__ == "__main__":
|
|
|
680
686
|
plt.savefig(f"{data_path}/picks_xt_{event_index}_noise.png")
|
|
681
687
|
|
|
682
688
|
# %%
|
|
689
|
+
# RANSAC locates a single event, so drive it with a one-event estimator (slot 0).
|
|
683
690
|
reg = RANSACRegressor(
|
|
684
|
-
estimator=ADLoc(
|
|
685
|
-
config, stations=stations[["x_km", "y_km", "z_km"]].values, num_event=num_event, eikonal=eikonal_config
|
|
686
|
-
),
|
|
691
|
+
estimator=ADLoc(config, num_event=1, eikonal=eikonal_config),
|
|
687
692
|
random_state=0,
|
|
688
693
|
min_samples=4,
|
|
689
694
|
residual_threshold=[1.0, 0.5],
|
|
690
695
|
)
|
|
691
|
-
reg.fit(X[["idx_sta", "type", "score", "
|
|
696
|
+
reg.fit(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values, X[["t_s", "amp"]].values)
|
|
692
697
|
mask_invt = reg.inlier_mask_
|
|
693
698
|
estimator = reg.estimator_
|
|
694
|
-
estimator.score(X[["idx_sta", "type"]].values, y=X[["t_s", "amp"]].values
|
|
695
|
-
output = estimator.predict(X[["idx_sta", "type"]].values
|
|
699
|
+
estimator.score(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values, y=X[["t_s", "amp"]].values)
|
|
700
|
+
output = estimator.predict(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values)
|
|
696
701
|
tt = output[:, 0]
|
|
697
702
|
amp = output[:, 1]
|
|
698
703
|
print("RANSAC with noise picks:")
|
|
699
704
|
print(f"True event loc: {event_loc[['x_km', 'y_km', 'z_km']].values.astype(float).round(3)}")
|
|
700
|
-
print(f"Invt event loc: {estimator.events[
|
|
705
|
+
print(f"Invt event loc: {estimator.events[0].round(3)}")
|
|
701
706
|
|
|
702
707
|
# %%
|
|
703
708
|
fig, ax = plt.subplots(2, 2, figsize=(8, 8), squeeze=False)
|
|
@@ -719,7 +724,7 @@ if __name__ == "__main__":
|
|
|
719
724
|
ax[0, 1].scatter(np.log10(X["dist_km"][mask_invt]), amp[mask_invt], c="g", s=50, marker="x", label="Invert")
|
|
720
725
|
|
|
721
726
|
X["dist_km"] = X[["x_km", "y_km", "z_km"]].apply(
|
|
722
|
-
lambda x: np.linalg.norm(x - estimator.events[
|
|
727
|
+
lambda x: np.linalg.norm(x - estimator.events[0, :3]), axis=1
|
|
723
728
|
)
|
|
724
729
|
colors = lambda x: "r" if x == 0 else "b"
|
|
725
730
|
ax[1, 0].scatter(X["dist_km"], X["t_s"], c=X["type"].apply(colors), s=30, marker="o", label="Picks")
|
|
@@ -4,6 +4,7 @@ from contextlib import nullcontext
|
|
|
4
4
|
import numpy as np
|
|
5
5
|
import pandas as pd
|
|
6
6
|
from tqdm import tqdm
|
|
7
|
+
from pyproj import Proj
|
|
7
8
|
|
|
8
9
|
from ._ransac import RANSACRegressor
|
|
9
10
|
|
|
@@ -57,10 +58,22 @@ def invert(picks, stations, config, estimator, event_index, event_init):
|
|
|
57
58
|
if config["use_amplitude"] and ("station_term_amplitude" not in stations.columns):
|
|
58
59
|
stations["station_term_amplitude"] = 0.0
|
|
59
60
|
|
|
61
|
+
|
|
62
|
+
stations_ = stations[stations["station_id"].isin(picks["station_id"].unique())].copy()
|
|
63
|
+
if "proj" not in config or config["proj"] is None:
|
|
64
|
+
lat0 = stations_["latitude"].mean()
|
|
65
|
+
lon0 = stations_["longitude"].mean()
|
|
66
|
+
proj = Proj(f"+proj=aeqd +lon_0={lon0} +lat_0={lat0} +units=km")
|
|
67
|
+
else:
|
|
68
|
+
proj = config["proj"]
|
|
69
|
+
x_km, y_km = proj(stations_["longitude"].values, stations_["latitude"].values)
|
|
70
|
+
stations_["x_km"] = x_km
|
|
71
|
+
stations_["y_km"] = y_km
|
|
72
|
+
|
|
60
73
|
if config["use_amplitude"]:
|
|
61
74
|
X = picks.merge(
|
|
62
75
|
# stations[["x_km", "y_km", "z_km", "station_id", "station_term_time", "station_term_amplitude"]],
|
|
63
|
-
|
|
76
|
+
stations_[
|
|
64
77
|
[
|
|
65
78
|
"x_km",
|
|
66
79
|
"y_km",
|
|
@@ -76,7 +89,7 @@ def invert(picks, stations, config, estimator, event_index, event_init):
|
|
|
76
89
|
else:
|
|
77
90
|
X = picks.merge(
|
|
78
91
|
# stations[["x_km", "y_km", "z_km", "station_id", "station_term_time"]],
|
|
79
|
-
|
|
92
|
+
stations_[
|
|
80
93
|
["x_km", "y_km", "z_km", "station_id", "station_term_time_p", "station_term_time_s"]
|
|
81
94
|
], ## Separate P and S station term
|
|
82
95
|
on="station_id",
|
|
@@ -113,6 +126,13 @@ def invert(picks, stations, config, estimator, event_index, event_init):
|
|
|
113
126
|
|
|
114
127
|
## Location using RANSAC
|
|
115
128
|
num_picks = len(X)
|
|
129
|
+
if num_picks < max(MIN_PICKS, int(MIN_PICKS_RATIO * num_picks)):
|
|
130
|
+
picks["mask"] = 0
|
|
131
|
+
picks["residual_time"] = 0.0
|
|
132
|
+
if config["use_amplitude"]:
|
|
133
|
+
picks["residual_amplitude"] = 0.0
|
|
134
|
+
return picks, None
|
|
135
|
+
|
|
116
136
|
reg = RANSACRegressor(
|
|
117
137
|
estimator=estimator,
|
|
118
138
|
random_state=0,
|
|
@@ -124,15 +144,14 @@ def invert(picks, stations, config, estimator, event_index, event_init):
|
|
|
124
144
|
)
|
|
125
145
|
try:
|
|
126
146
|
if config["use_amplitude"]:
|
|
127
|
-
reg.fit(X[["idx_sta", "type", "score", "
|
|
147
|
+
reg.fit(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values, X[["t_s", "amp"]].values)
|
|
128
148
|
else:
|
|
129
|
-
reg.fit(X[["idx_sta", "type", "score"]].values, X[["t_s"]].values)
|
|
149
|
+
reg.fit(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values, X[["t_s"]].values)
|
|
130
150
|
except Exception as e:
|
|
131
151
|
# print(f"No valid model for event_index {event_index}.")
|
|
132
152
|
message = "RANSAC could not find a valid consensus set."
|
|
133
153
|
if str(e)[: len(message)] != message:
|
|
134
154
|
print(e)
|
|
135
|
-
raise
|
|
136
155
|
picks["mask"] = 0
|
|
137
156
|
picks["residual_time"] = 0.0
|
|
138
157
|
if config["use_amplitude"]:
|
|
@@ -143,7 +162,7 @@ def invert(picks, stations, config, estimator, event_index, event_init):
|
|
|
143
162
|
inlier_mask = reg.inlier_mask_
|
|
144
163
|
|
|
145
164
|
## Predict travel time
|
|
146
|
-
output = estimator.predict(X[["idx_sta", "type"]].values)
|
|
165
|
+
output = estimator.predict(X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values)
|
|
147
166
|
if config["use_amplitude"]:
|
|
148
167
|
tt = output[:, 0]
|
|
149
168
|
amp = output[:, 1]
|
|
@@ -151,10 +170,13 @@ def invert(picks, stations, config, estimator, event_index, event_init):
|
|
|
151
170
|
tt = output[:, 0]
|
|
152
171
|
if config["use_amplitude"]:
|
|
153
172
|
score = estimator.score(
|
|
154
|
-
X[["idx_sta", "type", "score", "
|
|
173
|
+
X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values[inlier_mask],
|
|
174
|
+
y=X[["t_s", "amp"]].values[inlier_mask],
|
|
155
175
|
)
|
|
156
176
|
else:
|
|
157
|
-
score = estimator.score(
|
|
177
|
+
score = estimator.score(
|
|
178
|
+
X[["idx_sta", "type", "score", "x_km", "y_km", "z_km"]].values[inlier_mask], y=X[["t_s"]].values[inlier_mask]
|
|
179
|
+
)
|
|
158
180
|
|
|
159
181
|
if (np.sum(inlier_mask) > MIN_PICKS) and (score > config["min_score"]):
|
|
160
182
|
mean_residual_time = np.sum(np.abs(X["t_s"].values - tt) * inlier_mask) / np.sum(inlier_mask)
|
|
@@ -166,11 +188,16 @@ def invert(picks, stations, config, estimator, event_index, event_init):
|
|
|
166
188
|
if config["use_amplitude"]:
|
|
167
189
|
mag = estimator.magnitudes[0]
|
|
168
190
|
|
|
191
|
+
longitude, latitude = proj(x, y, inverse=True)
|
|
192
|
+
|
|
169
193
|
event = {
|
|
170
194
|
"idx_eve": event_index, ## inside adloc, idx_eve is used which starts from 0 to N events
|
|
171
195
|
"x_km": x,
|
|
172
196
|
"y_km": y,
|
|
173
197
|
"z_km": z,
|
|
198
|
+
"longitude": longitude,
|
|
199
|
+
"latitude": latitude,
|
|
200
|
+
"depth_km": z,
|
|
174
201
|
"time": t0 + pd.Timedelta(t, unit="s"),
|
|
175
202
|
"adloc_score": score,
|
|
176
203
|
"adloc_residual_time": mean_residual_time,
|
|
@@ -228,6 +255,7 @@ def invert_location(picks, stations, config, estimator, events_init=None, iter=0
|
|
|
228
255
|
NCPU = config["ncpu"]
|
|
229
256
|
else:
|
|
230
257
|
NCPU = min(64, mp.cpu_count() * 2 - 1)
|
|
258
|
+
print(f"Using {NCPU} CPUs for ADLoc inversion.")
|
|
231
259
|
|
|
232
260
|
jobs = []
|
|
233
261
|
num_events = len(picks["idx_eve"].unique())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|