DASPy-toolbox 1.0.0__py3-none-any.whl
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.
- DASPy_toolbox-1.0.0.dist-info/LICENSE.txt +1 -0
- DASPy_toolbox-1.0.0.dist-info/METADATA +85 -0
- DASPy_toolbox-1.0.0.dist-info/RECORD +49 -0
- DASPy_toolbox-1.0.0.dist-info/WHEEL +5 -0
- DASPy_toolbox-1.0.0.dist-info/entry_points.txt +2 -0
- DASPy_toolbox-1.0.0.dist-info/top_level.txt +1 -0
- daspy/__init__.py +4 -0
- daspy/advanced_tools/__init__.py +0 -0
- daspy/advanced_tools/channel.py +354 -0
- daspy/advanced_tools/decomposition.py +165 -0
- daspy/advanced_tools/denoising.py +276 -0
- daspy/advanced_tools/fdct.py +789 -0
- daspy/advanced_tools/strain2vel.py +245 -0
- daspy/basic_tools/__init__.py +0 -0
- daspy/basic_tools/filter.py +257 -0
- daspy/basic_tools/freqattributes.py +117 -0
- daspy/basic_tools/preprocessing.py +238 -0
- daspy/basic_tools/visualization.py +186 -0
- daspy/core/__init__.py +4 -0
- daspy/core/collection.py +279 -0
- daspy/core/dasdatetime.py +72 -0
- daspy/core/example.pkl +0 -0
- daspy/core/make_example.py +32 -0
- daspy/core/read.py +544 -0
- daspy/core/section.py +1319 -0
- daspy/core/write.py +282 -0
- daspy/seismic_detection/__init__.py +1 -0
- daspy/seismic_detection/calc_travel_time.py +23 -0
- daspy/seismic_detection/core.py +119 -0
- daspy/seismic_detection/detection.py +12 -0
- daspy/seismic_detection/gamma/__init__.py +13 -0
- daspy/seismic_detection/gamma/_base.py +549 -0
- daspy/seismic_detection/gamma/_bayesian_mixture.py +875 -0
- daspy/seismic_detection/gamma/_gaussian_mixture.py +866 -0
- daspy/seismic_detection/gamma/app.py +192 -0
- daspy/seismic_detection/gamma/seismic_ops.py +478 -0
- daspy/seismic_detection/gamma/utils.py +512 -0
- daspy/seismic_detection/location.py +266 -0
- daspy/seismic_detection/magnitude.py +43 -0
- daspy/seismic_detection/phase_picking.py +67 -0
- daspy/structure_imaging/__init__.py +0 -0
- daspy/structure_imaging/ambient_noise.py +4 -0
- daspy/structure_imaging/dispersion.py +27 -0
- daspy/structure_imaging/fault_zone.py +59 -0
- daspy/structure_imaging/inversion.py +6 -0
- daspy/traffic_monitoring/JamDetection.py +6 -0
- daspy/traffic_monitoring/SpeedMeasurement.py +6 -0
- daspy/traffic_monitoring/VehicleDetection.py +6 -0
- daspy/traffic_monitoring/__init__.py +0 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Purpose: Locate earthquake location with body wave travel time
|
|
2
|
+
# Author: Minzhe Hu
|
|
3
|
+
# Date: 2024.6.5
|
|
4
|
+
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
|
+
import os
|
|
6
|
+
import multiprocessing
|
|
7
|
+
import numpy as np
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from numpy.lib.stride_tricks import as_strided
|
|
10
|
+
from tqdm import tqdm
|
|
11
|
+
from obspy.taup.tau_model import TauModel
|
|
12
|
+
from obspy.taup import TauPyModel
|
|
13
|
+
from obspy.taup.taup_create import build_taup_model
|
|
14
|
+
from obspy.geodetics import locations2degrees
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def calc_travel_time(model, elo, ela, edep, slo, sla, sdep):
|
|
19
|
+
deg = locations2degrees(ela, elo, sla, slo)
|
|
20
|
+
arrival = model.get_travel_times(source_depth_in_km=edep,
|
|
21
|
+
receiver_depth_in_km=sdep,
|
|
22
|
+
distance_in_degree=deg,
|
|
23
|
+
phase_list=['p','P','s','S'])
|
|
24
|
+
|
|
25
|
+
return arrival[0].time, arrival[1].time
|
|
26
|
+
|
|
27
|
+
def sliding_window_sum(matrix, half_win):
|
|
28
|
+
shape = matrix.shape
|
|
29
|
+
app = np.zeros((len(matrix), half_win))
|
|
30
|
+
matrix = np.hstack((app, matrix, app))
|
|
31
|
+
strides = (matrix.strides[1], matrix.strides[0], matrix.strides[1])
|
|
32
|
+
windowed = as_strided(matrix[0:-2*half_win],
|
|
33
|
+
shape= (shape[1], shape[0], 2*half_win+1),
|
|
34
|
+
strides=strides)
|
|
35
|
+
return np.sum(windowed, axis=(1,2))
|
|
36
|
+
|
|
37
|
+
def calc_max(pick_map, delay, mode='PS'):
|
|
38
|
+
nch, nt = pick_map.shape
|
|
39
|
+
if mode == 'P':
|
|
40
|
+
delay = delay[:, 0]
|
|
41
|
+
min_sp = delay.min()
|
|
42
|
+
max_sp = delay.max()
|
|
43
|
+
hotmap = np.zeros((nch, nt+max_sp-min_sp))
|
|
44
|
+
for ch in range(nch):
|
|
45
|
+
hotmap[ch, max_sp-delay[ch]:max_sp-delay[ch]+nt] = pick_map[ch]
|
|
46
|
+
elif mode == 'S':
|
|
47
|
+
delay = delay[:, 1]
|
|
48
|
+
min_sp = delay.min()
|
|
49
|
+
max_sp = delay.max()
|
|
50
|
+
hotmap = np.zeros((nch, nt+max_sp-min_sp))
|
|
51
|
+
for ch in range(nch):
|
|
52
|
+
hotmap[ch, max_sp-delay[ch]:max_sp-delay[ch]+nt] = pick_map[ch]
|
|
53
|
+
else:
|
|
54
|
+
min_sp = delay.min()
|
|
55
|
+
max_sp = delay.max()
|
|
56
|
+
hotmap = np.zeros((nch, nt+max_sp-min_sp))
|
|
57
|
+
for ch in range(nch):
|
|
58
|
+
hotmap[ch, max_sp-delay[ch,0]:max_sp-delay[ch,0]+nt] += pick_map[ch]
|
|
59
|
+
hotmap[ch, max_sp-delay[ch,1]:max_sp-delay[ch,1]+nt] += pick_map[ch]
|
|
60
|
+
|
|
61
|
+
intensity = sliding_window_sum(hotmap, 5)
|
|
62
|
+
return np.argmax(intensity)-max_sp, np.max(intensity)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class Gird_Searcher(object):
|
|
66
|
+
def __init__(self, geometry, grid_lon, grid_lat, grid_dep,
|
|
67
|
+
model=None, filepath='./'):
|
|
68
|
+
if isinstance(model, TauPyModel):
|
|
69
|
+
self.model = model
|
|
70
|
+
model = model.model
|
|
71
|
+
elif isinstance(model, TauModel):
|
|
72
|
+
self.model = TauPyModel()
|
|
73
|
+
self.model.model = model
|
|
74
|
+
else:
|
|
75
|
+
model = TauModel.from_file(model)
|
|
76
|
+
self.model = TauPyModel()
|
|
77
|
+
self.model.model = model
|
|
78
|
+
# model.serialize(os.path.join(filepath, 'model.npz'))
|
|
79
|
+
|
|
80
|
+
if len(geometry[0]) == 2:
|
|
81
|
+
geometry = np.insert(geometry, 2, values=0, axis=1).shape
|
|
82
|
+
|
|
83
|
+
min_depth = min(min(grid_dep), geometry[:,2].min())
|
|
84
|
+
if min_depth < 0 :
|
|
85
|
+
self.depth_correction = -geometry[:,2].min()
|
|
86
|
+
grid_dep += self.depth_correction
|
|
87
|
+
geometry[:,2] += self.depth_correction
|
|
88
|
+
else:
|
|
89
|
+
self.depth_correction = 0
|
|
90
|
+
np.save(os.path.join(filepath, 'geometry.npy'), geometry)
|
|
91
|
+
self.geometry = geometry
|
|
92
|
+
self.grid_lon = grid_lon
|
|
93
|
+
self.grid_lat = grid_lat
|
|
94
|
+
self.grid_dep = grid_dep
|
|
95
|
+
self.nx, self.ny, self.nz = len(grid_lon), len(grid_lat), len(grid_dep)
|
|
96
|
+
self.filepath = filepath
|
|
97
|
+
|
|
98
|
+
def fit_map(self, pick_map, fs, n=5, mode='PS'):
|
|
99
|
+
self.mode = mode
|
|
100
|
+
self.intensity = np.zeros((self.nx, self.ny, self.nz))
|
|
101
|
+
self.rel_time = np.zeros((self.nx, self.ny, self.nz))
|
|
102
|
+
if n:
|
|
103
|
+
self.n = n
|
|
104
|
+
self.idx_lon = np.round(np.linspace(0, self.nx-1, n)).astype(int)
|
|
105
|
+
self.idx_lat = np.round(np.linspace(0, self.ny-1, n)).astype(int)
|
|
106
|
+
self.idx_dep = np.round(np.linspace(0, self.nz-1, n)).astype(int)
|
|
107
|
+
rd = 1
|
|
108
|
+
while len(self.idx_lon) + len(self.idx_lat) + len(self.idx_lon) > 9:
|
|
109
|
+
print(f'Grid seaching... Round {rd}')
|
|
110
|
+
self.calc_grid(pick_map, fs)
|
|
111
|
+
self._update_index()
|
|
112
|
+
rd += 1
|
|
113
|
+
print(f'Grid seaching... Round {rd}')
|
|
114
|
+
self.calc_grid(pick_map, fs)
|
|
115
|
+
else:
|
|
116
|
+
self.idx_lon = range(self.nx)
|
|
117
|
+
self.idx_lat = range(self.ny)
|
|
118
|
+
self.idx_dep = range(self.nz)
|
|
119
|
+
self.calc_grid(pick_map, fs)
|
|
120
|
+
idx = np.unravel_index(np.argmax(self.intensity), self.intensity.shape)
|
|
121
|
+
self.loc_idx = idx
|
|
122
|
+
return *self.ijk2lonlatdep(*idx), self.rel_time[idx]
|
|
123
|
+
|
|
124
|
+
def calc_grid(self, pick_map, fs):
|
|
125
|
+
for i in self.idx_lon:
|
|
126
|
+
elo = self.grid_lon[i]
|
|
127
|
+
for j in self.idx_lat:
|
|
128
|
+
ela = self.grid_lat[j]
|
|
129
|
+
for k in self.idx_dep:
|
|
130
|
+
edep = self.grid_dep[k]
|
|
131
|
+
if self.intensity[i, j, k] != 0:
|
|
132
|
+
continue
|
|
133
|
+
filename = os.path.join(self.filepath, f'{i}_{j}_{k}.npy')
|
|
134
|
+
if not os.path.exists(filename):
|
|
135
|
+
print(f'Calculating travel time for grid {i}, {j}, {k}.')
|
|
136
|
+
pyfile = Path(__file__).parent / 'calc_travel_time.py'
|
|
137
|
+
os.system(f'python {pyfile} {self.filepath} {elo} {ela} {edep} {i}_{j}_{k}.npy')
|
|
138
|
+
|
|
139
|
+
delay = np.load(filename)
|
|
140
|
+
delay = np.round(delay * fs).astype(int)
|
|
141
|
+
dsp, itst = calc_max(pick_map, delay, mode=self.mode)
|
|
142
|
+
self.intensity[i, j, k] = itst
|
|
143
|
+
self.rel_time[i, j, k] = dsp / fs
|
|
144
|
+
|
|
145
|
+
def _update_index(self):
|
|
146
|
+
idx = np.unravel_index(np.argmax(self.intensity), self.intensity.shape)
|
|
147
|
+
ii = list(self.idx_lon).index(idx[0])
|
|
148
|
+
s, e = self.idx_lon[max(ii-1, 0)], self.idx_lon[min(ii+1, len(self.idx_lon)-1)]
|
|
149
|
+
self.idx_lon = self._new_idx(s, e)
|
|
150
|
+
jj = list(self.idx_lat).index(idx[1])
|
|
151
|
+
s, e = self.idx_lat[max(jj-1, 0)], self.idx_lat[min(jj+1, len(self.idx_lat)-1)]
|
|
152
|
+
self.idx_lat = self._new_idx(s, e)
|
|
153
|
+
kk = list(self.idx_dep).index(idx[2])
|
|
154
|
+
s, e = self.idx_dep[max(kk-1, 0)], self.idx_dep[min(kk+1, len(self.idx_dep)-1)]
|
|
155
|
+
self.idx_dep = self._new_idx(s, e)
|
|
156
|
+
return self
|
|
157
|
+
|
|
158
|
+
def _new_idx(self, s, e):
|
|
159
|
+
n = e - s
|
|
160
|
+
if n >= 4:
|
|
161
|
+
return np.round(np.linspace(s, e, self.n)).astype(int)
|
|
162
|
+
else:
|
|
163
|
+
return list(range(s, e+1))
|
|
164
|
+
|
|
165
|
+
def lonlatdep2ijk(self, lon, lat, dep):
|
|
166
|
+
i = np.argmin(abs(self.grid_lon - lon))
|
|
167
|
+
j = np.argmin(abs(self.grid_lat - lat))
|
|
168
|
+
k = np.argmin(abs((self.grid_dep-self.depth_correction) - dep))
|
|
169
|
+
return i, j, k
|
|
170
|
+
|
|
171
|
+
def ijk2lonlatdep(self, i, j, k):
|
|
172
|
+
return self.grid_lon[i], self.grid_lat[j], self.grid_dep[k]-self.depth_correction
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def model_initialization(fname):
|
|
177
|
+
|
|
178
|
+
"""
|
|
179
|
+
Load a custom model. It will create a '.npz' file if imput file is '.nd' or
|
|
180
|
+
'.tvel'.
|
|
181
|
+
"""
|
|
182
|
+
suffix = os.path.splitext(fname)[0]
|
|
183
|
+
if not suffix[1] == '.npz':
|
|
184
|
+
build_taup_model(fname, output_folder=suffix[0] + '.npz')
|
|
185
|
+
fname = suffix[0] + '.npz'
|
|
186
|
+
print(fname + ' created')
|
|
187
|
+
|
|
188
|
+
model = TauPyModel()
|
|
189
|
+
model.model = TauModel.from_file(fname)
|
|
190
|
+
return model
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def xy2deg(y1, x1, y2, x2):
|
|
194
|
+
return np.sqrt((x1 - x2)**2 + (y1 - y2)**2) / 6371
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def time_difference(elo, ela, edep, geometry, t_obs,
|
|
198
|
+
phase, model, coordsys='lonlat'):
|
|
199
|
+
"""
|
|
200
|
+
Culculate the difference of observed arrival time and theoretical arrival
|
|
201
|
+
time list of 1 grid point.
|
|
202
|
+
"""
|
|
203
|
+
if coordsys == 'lonlat':
|
|
204
|
+
coord2deg = locations2degrees
|
|
205
|
+
elif coordsys == 'xy':
|
|
206
|
+
coord2deg = xy2deg
|
|
207
|
+
else:
|
|
208
|
+
raise ValueError("Coordsys must be one of 'lonlat', 'xy'")
|
|
209
|
+
stations = len(geometry)
|
|
210
|
+
dt = t_obs
|
|
211
|
+
for i in range(stations):
|
|
212
|
+
deg = coord2deg(ela, elo, geometry[i, 1], geometry[i, 0])
|
|
213
|
+
if phase[i] in ['p', 'P', 0]:
|
|
214
|
+
phs = ['p', 'P']
|
|
215
|
+
elif phase[i] in ['s', 'S', 1]:
|
|
216
|
+
phs = ['s', 'S']
|
|
217
|
+
else:
|
|
218
|
+
raise ValueError("Phase must be 'P' or 'S'")
|
|
219
|
+
arrivals = model.get_travel_times(source_depth_in_km=edep,
|
|
220
|
+
receiver_depth_in_km=geometry[i, 2],
|
|
221
|
+
distance_in_degree=deg,
|
|
222
|
+
phase_list=phs)
|
|
223
|
+
dt[i] -= arrivals[0].time
|
|
224
|
+
return dt
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def grid_search(lonlst, latlst, deplst, geometry, t_obs, phase, weight=None,
|
|
228
|
+
model=TauPyModel(model='iasp91'), coordsys='lonlat',
|
|
229
|
+
time_correction=True, norm_ord=1, parallel=True):
|
|
230
|
+
"""
|
|
231
|
+
Grid search for 1 event
|
|
232
|
+
"""
|
|
233
|
+
X, Y, Z = np.meshgrid(lonlst, latlst, deplst)
|
|
234
|
+
grid = np.array([X.flatten(), Y.flatten(), Z.flatten()]).transpose()
|
|
235
|
+
stations = len(geometry)
|
|
236
|
+
if len(geometry[0]) == 2:
|
|
237
|
+
geometry = np.concatenate((geometry, np.zeros((stations, 1))), axis=1)
|
|
238
|
+
if parallel:
|
|
239
|
+
cores = multiprocessing.cpu_count() - 1
|
|
240
|
+
pool = multiprocessing.Pool(processes=cores)
|
|
241
|
+
tasks = [[elo, ela, edep, geometry, t_obs, phase, model, coordsys] for
|
|
242
|
+
(elo, ela, edep) in grid]
|
|
243
|
+
dt = pool.starmap(time_difference, tasks)
|
|
244
|
+
else:
|
|
245
|
+
dt = np.zeros((len(grid), stations))
|
|
246
|
+
for i in tqdm(range(len(grid))):
|
|
247
|
+
dt[i] = time_difference(grid[i, 0], grid[i, 1], grid[i, 2],
|
|
248
|
+
geometry, t_obs, phase, model, coordsys)
|
|
249
|
+
if not weight:
|
|
250
|
+
weight = np.ones(len(geometry))
|
|
251
|
+
if time_correction:
|
|
252
|
+
if norm_ord == 1:
|
|
253
|
+
dtau = np.median(dt, axis=0)
|
|
254
|
+
elif norm_ord == 2:
|
|
255
|
+
dtau = np.mean(dt, axis=0)
|
|
256
|
+
else:
|
|
257
|
+
raise ValueError("Order of the norm must be 'P' or 'S'")
|
|
258
|
+
residual = np.linalg.norm(dt - dtau, ord=norm_ord, axis=0)
|
|
259
|
+
pos = np.argmin(residual)
|
|
260
|
+
elo, ela, edep = grid[pos]
|
|
261
|
+
return [elo, ela, edep, residual[pos], dtau[pos]]
|
|
262
|
+
else:
|
|
263
|
+
residual = np.linalg.norm(dt, ord=norm_ord, axis=0)
|
|
264
|
+
pos = np.argmin(residual)
|
|
265
|
+
elo, ela, edep = grid[pos]
|
|
266
|
+
return [elo, ela, edep, residual[pos]]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Purpose: Magnitude estimation
|
|
2
|
+
# Author: Minzhe Hu
|
|
3
|
+
# Date: 2023.8.26
|
|
4
|
+
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _phase2ab(phase):
|
|
9
|
+
if phase in ['p', 'P', 0]:
|
|
10
|
+
a = 0.437
|
|
11
|
+
b = 1.269
|
|
12
|
+
elif phase in ['s', 'S', 1]:
|
|
13
|
+
a = 0.690
|
|
14
|
+
b = 1.588
|
|
15
|
+
else:
|
|
16
|
+
raise ValueError("Phase must be 'P' or 'S'")
|
|
17
|
+
return a, b
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def fit_K(M, E, D, phase, ord=2):
|
|
21
|
+
'''
|
|
22
|
+
Fit Ki for n channels with m events.
|
|
23
|
+
:param M: 1D array. magnitude for each event
|
|
24
|
+
:param E: m*n array or float. peak amplitude of stain rate
|
|
25
|
+
:param D: m*n array or float. hypocentral distance
|
|
26
|
+
:param phase: only for p or s wave
|
|
27
|
+
'''
|
|
28
|
+
a, b = _phase2ab(phase)
|
|
29
|
+
M = np.tile(M, (len(D[0]), 1)).T
|
|
30
|
+
K = np.sum((np.log10(E) - a * M - b * D), 0)
|
|
31
|
+
return K
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def magnitude(E, D, K, phase):
|
|
35
|
+
'''
|
|
36
|
+
Estimate magnitude for one or multiple events.
|
|
37
|
+
:param E: 1-D array or float. peak amplitude of stain rate
|
|
38
|
+
:param D: 1-D array or float. hypocentral distance
|
|
39
|
+
:param K: 1-D array or float. site calibration terms
|
|
40
|
+
:param phase: only for p or s wave
|
|
41
|
+
'''
|
|
42
|
+
a, b = _phase2ab(phase)
|
|
43
|
+
return (np.log10(E) - b * np.log10(D) - K) / a
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Purpose: Pick phases for events
|
|
2
|
+
# Author: Minzhe Hu
|
|
3
|
+
# Date: 2024.8.30
|
|
4
|
+
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
|
+
import numpy as np
|
|
6
|
+
from scipy.stats import kurtosis, skew
|
|
7
|
+
from obspy.signal.trigger import classic_sta_lta, trigger_onset
|
|
8
|
+
from daspy.basic_tools.preprocessing import normalization
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def sta_lta_map(data, fs, sw=0.5, lw=5):
|
|
12
|
+
cft = np.zeros_like(data)
|
|
13
|
+
for ch, d in enumerate(data):
|
|
14
|
+
cft[ch] = classic_sta_lta(d, nsta=round(sw * fs), nlta=round(lw * fs))
|
|
15
|
+
|
|
16
|
+
return cft
|
|
17
|
+
|
|
18
|
+
def kurto_map(data, fs, win=3, diff=True, norm=True):
|
|
19
|
+
nch, nt = data.shape
|
|
20
|
+
w = round(win * fs)
|
|
21
|
+
kts = np.zeros((nch, nt-w))
|
|
22
|
+
for t in range(w, nt):
|
|
23
|
+
kts[:, t - w] = kurtosis(data[:, t - w:t], axis=1)
|
|
24
|
+
|
|
25
|
+
pre_nt = w
|
|
26
|
+
if diff:
|
|
27
|
+
kts = np.abs(np.diff(kts, axis=1))
|
|
28
|
+
pre_nt += 1
|
|
29
|
+
if norm:
|
|
30
|
+
kts = normalization(kts)
|
|
31
|
+
|
|
32
|
+
kts = np.hstack((np.zeros((nch, pre_nt)), kts))
|
|
33
|
+
return kts
|
|
34
|
+
|
|
35
|
+
def skew_map(data, fs, win=3, diff=True, norm=True):
|
|
36
|
+
nch, nt = data.shape
|
|
37
|
+
w = round(win * fs)
|
|
38
|
+
kts = np.zeros((nch, nt-w))
|
|
39
|
+
for t in range(w, nt):
|
|
40
|
+
kts[:, t - w] = skew(data[:, t - w:t], axis=1)
|
|
41
|
+
|
|
42
|
+
pre_nt = w
|
|
43
|
+
if diff:
|
|
44
|
+
kts = np.abs(np.diff(kts, axis=1))
|
|
45
|
+
pre_nt += 1
|
|
46
|
+
if norm:
|
|
47
|
+
kts = normalization(kts)
|
|
48
|
+
|
|
49
|
+
kts = np.hstack((np.zeros((nch, pre_nt)), kts))
|
|
50
|
+
return kts
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def map_picking(hot_map, thres1=5, thres2=5, choose_max=True, min_dsp=None):
|
|
54
|
+
pick = []
|
|
55
|
+
for ch, arr in enumerate(hot_map):
|
|
56
|
+
onsets = trigger_onset(arr, thres1=thres1, thres2=thres2)
|
|
57
|
+
if len(onsets) != 0:
|
|
58
|
+
for s, e in onsets:
|
|
59
|
+
if min_dsp is not None:
|
|
60
|
+
if e - s < min_dsp:
|
|
61
|
+
continue
|
|
62
|
+
if choose_max:
|
|
63
|
+
pick.append([ch, np.argmax(arr[s:e+1]) + s])
|
|
64
|
+
else:
|
|
65
|
+
pick.append([ch, s])
|
|
66
|
+
|
|
67
|
+
return np.array(pick)
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Purpose: Some preprocess methods
|
|
2
|
+
# Author: Minzhe Hu, Hanwen Zou
|
|
3
|
+
# Date: 2023.8.27
|
|
4
|
+
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
|
+
import numpy as np
|
|
6
|
+
from scipy.special import hankel1
|
|
7
|
+
from daspy.basic_tools.freqattributes import spectrum
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def fj_method(data, dx, fs, nfft='default', f=None, v=np.arange(100, 3000, 5),
|
|
11
|
+
verbose=False):
|
|
12
|
+
x = np.arange(1, len(data) + 1) * dx
|
|
13
|
+
spec, ff = spectrum(data, fs, tpad=nfft)
|
|
14
|
+
if f is None:
|
|
15
|
+
f = ff
|
|
16
|
+
|
|
17
|
+
# F-J method
|
|
18
|
+
dispen = np.zeros((len(f), len(v)), complex)
|
|
19
|
+
for i, fi in enumerate(f):
|
|
20
|
+
for j, vj in enumerate(v):
|
|
21
|
+
k = 2 * np.pi * fi / vj
|
|
22
|
+
bssh1 = hankel1(0, k * x)
|
|
23
|
+
c = spec[:, round(fi / (ff[1] - ff[0]))]
|
|
24
|
+
dispen[i, j] = np.sum(c * bssh1 * x)
|
|
25
|
+
if verbose:
|
|
26
|
+
return dispen, f, v
|
|
27
|
+
return dispen
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Purpose: Finding the source of the fault scattered waves from the waveforms recorded by DAS
|
|
2
|
+
# Author: Minzhe Hu
|
|
3
|
+
# Date: 2023.8.27
|
|
4
|
+
# Email: hmz2018@mail.ustc.edu.cn
|
|
5
|
+
import multiprocessing
|
|
6
|
+
import numpy as np
|
|
7
|
+
from math import ceil
|
|
8
|
+
from daspy.advanced_tools.decomposition import fk_filter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def csb_stacking(data, lent, slope):
|
|
12
|
+
"""
|
|
13
|
+
Use coherence- and symmetry-based method to stack scattered wave from 1
|
|
14
|
+
channel location.
|
|
15
|
+
"""
|
|
16
|
+
nx, nt = data.shape
|
|
17
|
+
nv = len(slope)
|
|
18
|
+
lenx = int((nx - 1) / 2)
|
|
19
|
+
t_ex = lent + ceil(lenx * max(slope))
|
|
20
|
+
data = np.hstack((data, np.zeros((nx, t_ex))))
|
|
21
|
+
|
|
22
|
+
intensity = np.zeros(nt)
|
|
23
|
+
for i in range(nt):
|
|
24
|
+
left = np.zeros((nv, lent))
|
|
25
|
+
right = np.zeros((nv, lent))
|
|
26
|
+
for j, s in enumerate(slope):
|
|
27
|
+
for k in range(lenx + 1):
|
|
28
|
+
start = i + round(k * s)
|
|
29
|
+
left[j] += data[lenx - k, start:start + lent]
|
|
30
|
+
right[j] += data[lenx + k, start:start + lent]
|
|
31
|
+
intensity[i] = np.sum(abs(left * right), axis=1).max()
|
|
32
|
+
return intensity
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def fault_detection(raw_data, dx, fs, winx=200, wint=1, vmin=200, vmax=700,
|
|
36
|
+
nv=30, fk=True):
|
|
37
|
+
"""
|
|
38
|
+
Obtain the spatiotemporal distribution of scattering intensity and velocity
|
|
39
|
+
from the waveform.
|
|
40
|
+
"""
|
|
41
|
+
data = raw_data.copy()
|
|
42
|
+
if fk:
|
|
43
|
+
data = fk_filter(data, dx, fs, vmin=vmin, vmax=vmax)
|
|
44
|
+
|
|
45
|
+
nx, nt = data.shape
|
|
46
|
+
lenx = round(winx / dx)
|
|
47
|
+
lent = round(wint * fs)
|
|
48
|
+
data = np.r_[np.zeros((lenx, nt)), data, np.zeros((lenx, nt))]
|
|
49
|
+
|
|
50
|
+
cores = multiprocessing.cpu_count() - 1
|
|
51
|
+
pool = multiprocessing.Pool(processes=cores)
|
|
52
|
+
|
|
53
|
+
slope = 1 / np.linspace(vmin, vmax, nv) * dx * fs
|
|
54
|
+
tasks = []
|
|
55
|
+
for i in range(nx):
|
|
56
|
+
tasks.append((data[i:i + 2 * lenx + 1], lent, slope))
|
|
57
|
+
|
|
58
|
+
intensity = np.array(pool.starmap(csb_stacking, tasks))
|
|
59
|
+
return intensity
|
|
File without changes
|