pysmo.aimbat 1.0.8__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.
- pysmo/aimbat/__init__.py +46 -0
- pysmo/aimbat/algiccs.py +502 -0
- pysmo/aimbat/algmccc.py +676 -0
- pysmo/aimbat/filtering.py +86 -0
- pysmo/aimbat/pickphase.py +977 -0
- pysmo/aimbat/plotphase.py +800 -0
- pysmo/aimbat/plotstations.py +186 -0
- pysmo/aimbat/plotutils.py +243 -0
- pysmo/aimbat/prepdata.py +366 -0
- pysmo/aimbat/prepplot.py +451 -0
- pysmo/aimbat/qualctrl.py +1578 -0
- pysmo/aimbat/qualsort.py +203 -0
- pysmo/aimbat/sacpickle.py +709 -0
- pysmo/aimbat/stationmapper.bat +9 -0
- pysmo/aimbat/stationmapper.sh +5 -0
- pysmo/aimbat/stationmapping.py +93 -0
- pysmo/aimbat/ttconfig.py +292 -0
- pysmo/aimbat/ttdefaults.conf +103 -0
- pysmo/aimbat/ttguiqt.py +1116 -0
- pysmo/aimbat/xcorr.py +191 -0
- pysmo_aimbat-1.0.8.dist-info/METADATA +122 -0
- pysmo_aimbat-1.0.8.dist-info/RECORD +24 -0
- pysmo_aimbat-1.0.8.dist-info/WHEEL +4 -0
- pysmo_aimbat-1.0.8.dist-info/entry_points.txt +13 -0
pysmo/aimbat/__init__.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIMBAT
|
|
3
|
+
======
|
|
4
|
+
|
|
5
|
+
AIMBAT (Automated and Interactive Measurement of Body wave Arrival Times)
|
|
6
|
+
is an open-source software package for efficiently measuring teleseismic
|
|
7
|
+
body wave arrival times for large seismic arrays (Lou et al., 2012). It is
|
|
8
|
+
based on a widely used method called MCCC (Multi-Channel Cross-Correlation)
|
|
9
|
+
developed by VanDecar and Crosson (1990). The package is automated in the
|
|
10
|
+
sense of initially aligning seismograms for MCCC which is achieved by an
|
|
11
|
+
ICCS (Iterative Cross Correlation and Stack) algorithm. Meanwhile, a
|
|
12
|
+
graphical user interface is built to perform seismogram quality control
|
|
13
|
+
interactively. Therefore, user processing time is reduced while valuable
|
|
14
|
+
input from a user\'s expertise is retained. As a byproduct, SAC (Goldstein
|
|
15
|
+
et al., 2003) plotting and phase picking functionalities are replicated
|
|
16
|
+
and enhanced.
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import sys
|
|
21
|
+
import warnings
|
|
22
|
+
|
|
23
|
+
name = "aimbat"
|
|
24
|
+
|
|
25
|
+
_MIGRATION_NOTICE = (
|
|
26
|
+
"pysmo.aimbat is the legacy AIMBAT 1 line and is no longer actively "
|
|
27
|
+
"developed. It has been superseded by AIMBAT 2, which is distributed as "
|
|
28
|
+
"'aimbat' (pip install aimbat) and imported as 'aimbat', no longer under "
|
|
29
|
+
"the pysmo namespace. See https://github.com/pysmo/aimbat."
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
warnings.warn(_MIGRATION_NOTICE, DeprecationWarning, stacklevel=2)
|
|
33
|
+
|
|
34
|
+
_cli_notice_shown = False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def cli_deprecation_notice():
|
|
38
|
+
"""Print the AIMBAT 1 migration notice to stderr, once per process.
|
|
39
|
+
|
|
40
|
+
Called by the console-script entry points, where a DeprecationWarning
|
|
41
|
+
would otherwise be hidden by Python's default warning filters.
|
|
42
|
+
"""
|
|
43
|
+
global _cli_notice_shown
|
|
44
|
+
if not _cli_notice_shown:
|
|
45
|
+
print("WARNING: " + _MIGRATION_NOTICE, file=sys.stderr)
|
|
46
|
+
_cli_notice_shown = True
|
pysmo/aimbat/algiccs.py
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# ------------------------------------------------
|
|
3
|
+
# Filename: algiccs.py
|
|
4
|
+
# Author: Xiaoting Lou
|
|
5
|
+
# Email: xlou@u.northwestern.edu
|
|
6
|
+
#
|
|
7
|
+
# Copyright (c) 2009 Xiaoting Lou
|
|
8
|
+
# ------------------------------------------------
|
|
9
|
+
"""
|
|
10
|
+
Python module for the ICCS (iterative cross-correlation and stack) algorithm.
|
|
11
|
+
|
|
12
|
+
:copyright:
|
|
13
|
+
Xiaoting Lou
|
|
14
|
+
|
|
15
|
+
:license:
|
|
16
|
+
GNU General Public License, Version 3 (GPLv3)
|
|
17
|
+
http://www.gnu.org/licenses/gpl.html
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import copy
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
from optparse import OptionParser
|
|
24
|
+
|
|
25
|
+
from numpy import array, corrcoef, dot, linspace, mean, ones, sqrt, transpose, zeros
|
|
26
|
+
from numpy import linalg as LA
|
|
27
|
+
|
|
28
|
+
from pysmo.aimbat import cli_deprecation_notice, qualsort, ttconfig
|
|
29
|
+
from pysmo.aimbat import prepdata as pdata
|
|
30
|
+
from pysmo.aimbat import sacpickle as sacpkl
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def getOptions():
|
|
34
|
+
"""Parse arguments and options."""
|
|
35
|
+
usage = "Usage: %prog [options] <sacfile(s) or a picklefile>"
|
|
36
|
+
parser = OptionParser(usage=usage)
|
|
37
|
+
twcorr = -15, 15
|
|
38
|
+
ipick = "t0"
|
|
39
|
+
wpick = "t1"
|
|
40
|
+
minccc = 0.5
|
|
41
|
+
minsnr = 0.5
|
|
42
|
+
mincoh = 0.0
|
|
43
|
+
minqual = minccc, minsnr, mincoh
|
|
44
|
+
minnsel = 5
|
|
45
|
+
parser.set_defaults(twcorr=twcorr)
|
|
46
|
+
parser.set_defaults(ipick=ipick)
|
|
47
|
+
parser.set_defaults(wpick=wpick)
|
|
48
|
+
parser.set_defaults(minqual=minqual)
|
|
49
|
+
parser.set_defaults(minnsel=minnsel)
|
|
50
|
+
parser.add_option(
|
|
51
|
+
"-S",
|
|
52
|
+
"--srate",
|
|
53
|
+
dest="srate",
|
|
54
|
+
type="float",
|
|
55
|
+
help="Sampling rate to load SAC data. Default is None, "
|
|
56
|
+
"use the original rate of first files.",
|
|
57
|
+
)
|
|
58
|
+
parser.add_option(
|
|
59
|
+
"-i",
|
|
60
|
+
"--ipick",
|
|
61
|
+
dest="ipick",
|
|
62
|
+
type="str",
|
|
63
|
+
help="SAC header variable to read input time pick.",
|
|
64
|
+
)
|
|
65
|
+
parser.add_option(
|
|
66
|
+
"-w",
|
|
67
|
+
"--wpick",
|
|
68
|
+
dest="wpick",
|
|
69
|
+
type="str",
|
|
70
|
+
help="SAC header variable to write output time pick.",
|
|
71
|
+
)
|
|
72
|
+
parser.add_option(
|
|
73
|
+
"-t",
|
|
74
|
+
"--twcorr",
|
|
75
|
+
dest="twcorr",
|
|
76
|
+
type="float",
|
|
77
|
+
nargs=2,
|
|
78
|
+
help="Time window for cross-correlation. Default is "
|
|
79
|
+
f"[{twcorr[0]:.1f}, {twcorr[1]:.1f}] s.",
|
|
80
|
+
)
|
|
81
|
+
parser.add_option(
|
|
82
|
+
"-f",
|
|
83
|
+
"--fstack",
|
|
84
|
+
dest="fstack",
|
|
85
|
+
type="str",
|
|
86
|
+
help="SAC file name to save final array stack.",
|
|
87
|
+
)
|
|
88
|
+
parser.add_option(
|
|
89
|
+
"-p",
|
|
90
|
+
"--plotiter",
|
|
91
|
+
action="store_true",
|
|
92
|
+
dest="plotiter",
|
|
93
|
+
help="Plot array stack of each iteration.",
|
|
94
|
+
)
|
|
95
|
+
parser.add_option(
|
|
96
|
+
"-a",
|
|
97
|
+
"--auto_on",
|
|
98
|
+
action="store_true",
|
|
99
|
+
dest="auto_on",
|
|
100
|
+
help="Run ICCS and select/delete seismograms automatically.",
|
|
101
|
+
)
|
|
102
|
+
parser.add_option(
|
|
103
|
+
"-A",
|
|
104
|
+
"--auto_on_all",
|
|
105
|
+
action="store_true",
|
|
106
|
+
dest="auto_on_all",
|
|
107
|
+
help="Run ICCS with -a option but initially use all seismograms.",
|
|
108
|
+
)
|
|
109
|
+
parser.add_option(
|
|
110
|
+
"-q",
|
|
111
|
+
"--minqual",
|
|
112
|
+
dest="minqual",
|
|
113
|
+
type="float",
|
|
114
|
+
nargs=3,
|
|
115
|
+
help="Minimum quality factor (ccc,snr,coh) for auto selection. "
|
|
116
|
+
f"Defaults are {minccc:.2f} {minsnr:.2f} {mincoh:.2f}.",
|
|
117
|
+
)
|
|
118
|
+
parser.add_option(
|
|
119
|
+
"-n",
|
|
120
|
+
"--minnsel",
|
|
121
|
+
dest="minnsel",
|
|
122
|
+
type="int",
|
|
123
|
+
help="Minimum number of selected seismograms for auto selection. "
|
|
124
|
+
f"Default is {minnsel:d}.",
|
|
125
|
+
)
|
|
126
|
+
opts, files = parser.parse_args(sys.argv[1:])
|
|
127
|
+
if not files:
|
|
128
|
+
print(parser.usage)
|
|
129
|
+
sys.exit()
|
|
130
|
+
return opts, files
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def corrmax(datai, dataj, delta, xcorr, shift):
|
|
134
|
+
"""Calculate time lag at maximum cross correlation between two time series."""
|
|
135
|
+
delay, ccmax, ccpol = xcorr(datai, dataj, shift)
|
|
136
|
+
return delay * delta, ccmax, ccpol
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def meanStack(data, taperwidth, tapertype):
|
|
140
|
+
"""Calculate array stack by averaging without weighting."""
|
|
141
|
+
sdata = mean(data, 0)
|
|
142
|
+
sdata = sacpkl.taper(sdata, taperwidth, tapertype)
|
|
143
|
+
return sdata
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def weightStack(data, wgts, taperwidth, tapertype):
|
|
147
|
+
"""Calculate array stack by averaging with weighting."""
|
|
148
|
+
sdata = mean(transpose(data) * wgts, 1)
|
|
149
|
+
sdata = sacpkl.taper(sdata, taperwidth, tapertype)
|
|
150
|
+
return sdata
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def normWeightStack(data, wgts, taperwidth, tapertype):
|
|
154
|
+
"""Calculate array stack by averaging with weighting and normalization."""
|
|
155
|
+
mdata = [d / max(d) for d in data]
|
|
156
|
+
sdata = mean(transpose(mdata) * wgts, 1)
|
|
157
|
+
sdata = sacpkl.taper(sdata, taperwidth, tapertype)
|
|
158
|
+
return sdata
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def ccWeightStack(saclist, opts):
|
|
162
|
+
"""
|
|
163
|
+
Align seismograms by the iterative cross-correlation and stack algorithm.
|
|
164
|
+
|
|
165
|
+
Parameters
|
|
166
|
+
----------
|
|
167
|
+
opts.delta : sample time interval
|
|
168
|
+
opts.ccpara : a class instance for ICCS parameters
|
|
169
|
+
* qqhdrs : SAC headers to save quality factors: ccc, snr, coh
|
|
170
|
+
* maxiter : maximum number of iteration
|
|
171
|
+
* converg : convergence critrion
|
|
172
|
+
* cchdrs : inputand output picks of cross-correlation
|
|
173
|
+
* twcorr : time window for cross-correlation
|
|
174
|
+
"""
|
|
175
|
+
ccpara = opts.ccpara
|
|
176
|
+
delta = opts.delta
|
|
177
|
+
maxiter = ccpara.maxiter
|
|
178
|
+
convtype = ccpara.convtype
|
|
179
|
+
convepsi = ccpara.convepsi
|
|
180
|
+
taperwidth = ccpara.taperwidth
|
|
181
|
+
tapertype = ccpara.tapertype
|
|
182
|
+
xcorr = ccpara.xcorr
|
|
183
|
+
shift = ccpara.shift
|
|
184
|
+
twhdrs = ccpara.twhdrs
|
|
185
|
+
qqhdrs = ccpara.qheaders
|
|
186
|
+
cchdrs = ccpara.cchdrs
|
|
187
|
+
twcorr = ccpara.twcorr
|
|
188
|
+
(
|
|
189
|
+
hdrccc,
|
|
190
|
+
hdrsnr,
|
|
191
|
+
hdrcoh,
|
|
192
|
+
) = qqhdrs[:3]
|
|
193
|
+
cchdr0, cchdr1 = cchdrs
|
|
194
|
+
if convtype == "coef":
|
|
195
|
+
convergence = coConverg
|
|
196
|
+
elif convtype == "resi":
|
|
197
|
+
convergence = reConverg
|
|
198
|
+
else:
|
|
199
|
+
print(f"Unknown convergence criterion: {convtype:s}. Exit.")
|
|
200
|
+
sys.exit()
|
|
201
|
+
|
|
202
|
+
out = "\n--> Run ICCS at window [{0:5.1f}, {1:5.1f}] wrt {2:s}. Write to header: {3:s}"
|
|
203
|
+
print(out.format(twcorr[0], twcorr[1], cchdr0, cchdr1))
|
|
204
|
+
print(f" Convergence criterion: {convtype:s}")
|
|
205
|
+
if ccpara.stackwgt == "coef":
|
|
206
|
+
wgtcoef = True
|
|
207
|
+
else:
|
|
208
|
+
wgtcoef = False
|
|
209
|
+
taperwindow = sacpkl.taperWindow(twcorr, taperwidth)
|
|
210
|
+
# get initial time picks
|
|
211
|
+
tinis = array([sacdh.gethdr(cchdr0) for sacdh in saclist])
|
|
212
|
+
tfins = tinis.copy()
|
|
213
|
+
nseis = len(saclist)
|
|
214
|
+
ccc = zeros(nseis)
|
|
215
|
+
snr = zeros(nseis)
|
|
216
|
+
coh = zeros(nseis)
|
|
217
|
+
wgts = ones(nseis)
|
|
218
|
+
stkdata = []
|
|
219
|
+
datatype = "datamem"
|
|
220
|
+
for it in range(maxiter):
|
|
221
|
+
# recut data and update array stack
|
|
222
|
+
nstart, ntotal = sacpkl.windowIndex(saclist, tfins, twcorr, taperwindow)
|
|
223
|
+
windata = sacpkl.windowData(
|
|
224
|
+
saclist, nstart, ntotal, taperwidth, tapertype, datatype
|
|
225
|
+
)
|
|
226
|
+
sdata = normWeightStack(windata, wgts, taperwidth, tapertype)
|
|
227
|
+
stkdata.append(sdata)
|
|
228
|
+
if it == 0:
|
|
229
|
+
print(f"=== Iteration {it:d} : epsilon")
|
|
230
|
+
else:
|
|
231
|
+
conv = convergence(stkdata[it], stkdata[it - 1])
|
|
232
|
+
print(f"=== Iteration {it:d} : {conv:8.6f}")
|
|
233
|
+
if conv <= convepsi:
|
|
234
|
+
print(
|
|
235
|
+
f" Array stack converged... Done. Mean corrcoef={mean(ccc):.3f}"
|
|
236
|
+
)
|
|
237
|
+
break
|
|
238
|
+
# Find time lag at peak correlation between each trace and the array stack.
|
|
239
|
+
# Calculate cross correlation coefficient, signal/noise ratio and temporal coherence
|
|
240
|
+
sdatanorm = sdata / LA.norm(sdata)
|
|
241
|
+
for i in range(nseis):
|
|
242
|
+
datai = windata[i]
|
|
243
|
+
delay, ccmax, ccpol = corrmax(sdata, datai, delta, xcorr, shift)
|
|
244
|
+
tfins[i] += delay
|
|
245
|
+
sacdh = saclist[i]
|
|
246
|
+
sacdh.sethdr(cchdr1, tfins[i])
|
|
247
|
+
if wgtcoef: # update weight only when stackwgt == coef
|
|
248
|
+
wgts[i] = ccpol * ccmax
|
|
249
|
+
ccc[i] = ccmax
|
|
250
|
+
sacdh.sethdr(hdrccc, ccc[i])
|
|
251
|
+
snr[i] = snratio(datai, delta, twcorr)
|
|
252
|
+
sacdh.sethdr(hdrsnr, snr[i])
|
|
253
|
+
coh[i] = coherence(datai * ccpol, sdatanorm)
|
|
254
|
+
sacdh.sethdr(hdrcoh, coh[i])
|
|
255
|
+
# get maximum time window for plot (excluding taperwindow)
|
|
256
|
+
bb, ee = [], []
|
|
257
|
+
for i in range(nseis):
|
|
258
|
+
sacdh = saclist[i]
|
|
259
|
+
b = sacdh.b - tfins[i]
|
|
260
|
+
e = b + (sacdh.npts - 1) * delta
|
|
261
|
+
bb.append(b + delta)
|
|
262
|
+
ee.append(e - delta)
|
|
263
|
+
b = max(bb)
|
|
264
|
+
e = min(ee)
|
|
265
|
+
d = (e - b) * taperwidth / 2
|
|
266
|
+
twplot = [b + d, e - d]
|
|
267
|
+
# calculate final stack at twplot, save to a sacdh object: stkdh
|
|
268
|
+
# set time picks of stkdh as mean of tinis and tfins
|
|
269
|
+
taperwindow = sacpkl.taperWindow(twplot, taperwidth)
|
|
270
|
+
nstart, ntotal = sacpkl.windowIndex(saclist, tfins, twplot, taperwindow)
|
|
271
|
+
windata = sacpkl.windowData(
|
|
272
|
+
saclist, nstart, ntotal, taperwidth, tapertype, datatype
|
|
273
|
+
)
|
|
274
|
+
sdatamem = normWeightStack(windata, wgts, taperwidth, tapertype)
|
|
275
|
+
# also create stack from original data
|
|
276
|
+
datatype = "data"
|
|
277
|
+
windata = sacpkl.windowData(
|
|
278
|
+
saclist, nstart, ntotal, taperwidth, tapertype, datatype
|
|
279
|
+
)
|
|
280
|
+
sdata = normWeightStack(windata, wgts, taperwidth, tapertype)
|
|
281
|
+
tinimean = mean(tinis)
|
|
282
|
+
tfinmean = mean(tfins)
|
|
283
|
+
stkdh = copy.copy(saclist[0])
|
|
284
|
+
stkdh.thdrs = [
|
|
285
|
+
-12345.0,
|
|
286
|
+
] * 10
|
|
287
|
+
stkdh.users = [
|
|
288
|
+
-12345.0,
|
|
289
|
+
] * 10
|
|
290
|
+
stkdh.kusers = [
|
|
291
|
+
"-1234567",
|
|
292
|
+
] * 3
|
|
293
|
+
stkdh.b = twplot[0] - taperwindow * 0.5 + tfinmean
|
|
294
|
+
stkdh.npts = len(sdata)
|
|
295
|
+
stkdh.data = sdata
|
|
296
|
+
stkdh.sethdr(cchdr0, tinimean)
|
|
297
|
+
stkdh.sethdr(cchdr1, tfinmean)
|
|
298
|
+
stkdh.knetwk = "Array"
|
|
299
|
+
stkdh.kstnm = "Stack"
|
|
300
|
+
stkdh.netsta = "Array.Stack"
|
|
301
|
+
stkdh.gcarc = -1
|
|
302
|
+
stkdh.dist = -1
|
|
303
|
+
stkdh.baz = -1
|
|
304
|
+
stkdh.az = -1
|
|
305
|
+
stkdh.stla = 0
|
|
306
|
+
stkdh.stlo = 0
|
|
307
|
+
stkdh.stel = 0
|
|
308
|
+
stkdh.delta = delta
|
|
309
|
+
stkdh.e = stkdh.b + (stkdh.npts - 1) * delta
|
|
310
|
+
stkdh.time = linspace(stkdh.b, stkdh.b + (stkdh.npts - 1) * stkdh.delta, stkdh.npts)
|
|
311
|
+
stkdh.datamem = sdatamem
|
|
312
|
+
# set time window
|
|
313
|
+
stkdh.sethdr(twhdrs[0], twcorr[0] + tfinmean)
|
|
314
|
+
stkdh.sethdr(twhdrs[1], twcorr[1] + tfinmean)
|
|
315
|
+
stkdh.twindow = twcorr[0] + tfinmean, twcorr[1] + tfinmean
|
|
316
|
+
if opts.fstack is None:
|
|
317
|
+
stkdh.filename = ccpara.fstack
|
|
318
|
+
else:
|
|
319
|
+
stkdh.filename = opts.fstack
|
|
320
|
+
for sacdh, tfin in zip(saclist, tfins):
|
|
321
|
+
sacdh.sethdr(twhdrs[0], tfin + twcorr[0])
|
|
322
|
+
sacdh.sethdr(twhdrs[1], tfin + twcorr[1])
|
|
323
|
+
sacdh.twindow = tfin + twcorr[0], tfin + twcorr[1]
|
|
324
|
+
quas = array([ccc, snr, coh])
|
|
325
|
+
return stkdh, stkdata, quas
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def coConverg(stack0, stack1):
|
|
329
|
+
"""
|
|
330
|
+
Calcuate criterion of convergence by correlation coefficient.
|
|
331
|
+
stack0 and stack1 are current stack and stack from last iteration.
|
|
332
|
+
"""
|
|
333
|
+
return 1 - corrcoef(stack0, stack1)[0][1]
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def reConverg(stack0, stack1):
|
|
337
|
+
"""
|
|
338
|
+
Calcuate criterion of convergence by change of stack.
|
|
339
|
+
stack0 and stack1 are current stack and stack from last iteration.
|
|
340
|
+
"""
|
|
341
|
+
return LA.norm(stack0 - stack1, 1) / LA.norm(stack0, 2) / len(stack0)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def snratio(data, delta, timewindow):
|
|
345
|
+
"""
|
|
346
|
+
Calculate signal/noise ratio within the given time window.
|
|
347
|
+
Time window is relative, such as [-10, 20], to the onset of the arrival.
|
|
348
|
+
"""
|
|
349
|
+
tw0, tw1 = timewindow
|
|
350
|
+
nn = int(round(-tw0 / delta))
|
|
351
|
+
yn = data[:nn]
|
|
352
|
+
ys = data[nn:]
|
|
353
|
+
ns = len(ys)
|
|
354
|
+
rr = LA.norm(ys) / LA.norm(yn) * sqrt(nn) / sqrt(ns)
|
|
355
|
+
if LA.norm(yn) == 0:
|
|
356
|
+
print("snr", LA.norm(yn))
|
|
357
|
+
# the same as:
|
|
358
|
+
# rr = sqrt(sum(square(ys))/sum(square(yn))*nn/ns)
|
|
359
|
+
# shoud signal be the whole time seris?
|
|
360
|
+
# yw = data[:]
|
|
361
|
+
# nw = len(yw)
|
|
362
|
+
# rw = sqrt(sum(square(yw))/sum(square(yn))*nn/nw)
|
|
363
|
+
return rr
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def coherence(datai, datas):
|
|
367
|
+
"""
|
|
368
|
+
Calculate time domain coherence.
|
|
369
|
+
Coherence is 1 - sin of the angle made by two vectors: di and ds.
|
|
370
|
+
Di is data vector, and ds is the unit vector of array stack.
|
|
371
|
+
res(di) = di - (di . ds) ds
|
|
372
|
+
coh(di) = 1 - res(di) / ||di||
|
|
373
|
+
"""
|
|
374
|
+
return 1 - LA.norm(datai - dot(datai, datas) * datas) / LA.norm(datai)
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def plotiter(stkdata):
|
|
378
|
+
import matplotlib.pyplot as plt
|
|
379
|
+
|
|
380
|
+
plt.figure()
|
|
381
|
+
for i in range(len(stkdata)):
|
|
382
|
+
plt.plot(stkdata[i], label="iter" + str(i))
|
|
383
|
+
plt.legend()
|
|
384
|
+
plt.show()
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def autoiccs(gsac, opts):
|
|
388
|
+
"""Run ICCS and delete low quality seismograms automatically."""
|
|
389
|
+
saclist = gsac.saclist
|
|
390
|
+
hdrsel = opts.ccpara.hdrsel
|
|
391
|
+
minqual = opts.minqual
|
|
392
|
+
minnsel = opts.minnsel
|
|
393
|
+
minccc, minsnr, mincoh = minqual
|
|
394
|
+
|
|
395
|
+
selist, _ = qualsort.seleSeis(saclist)
|
|
396
|
+
print(
|
|
397
|
+
f"\n*** Run ICCS until all low quality seismograms removed: Min_ccc={minccc:.2f} Min_snr={minsnr:.1f} Min_coh={mincoh:.2f} *** "
|
|
398
|
+
)
|
|
399
|
+
rerun = True
|
|
400
|
+
while rerun and len(selist) >= minnsel:
|
|
401
|
+
stkdh, _, quas = ccWeightStack(selist, opts)
|
|
402
|
+
tquas = transpose(quas)
|
|
403
|
+
indsel, inddel = [], []
|
|
404
|
+
for i in range(len(selist)):
|
|
405
|
+
sacdh = selist[i]
|
|
406
|
+
ccc, snr, coh = tquas[i]
|
|
407
|
+
if ccc < minccc or snr < minsnr or coh < mincoh:
|
|
408
|
+
inddel.append(i)
|
|
409
|
+
sacdh.sethdr(hdrsel, "False")
|
|
410
|
+
sacdh.selected = False
|
|
411
|
+
print(
|
|
412
|
+
f"--> Seismogram: {sacdh.filename:s} quality factors {ccc:.2f} {snr:.2f} {coh:.2f} < min. Deleted. "
|
|
413
|
+
)
|
|
414
|
+
else:
|
|
415
|
+
indsel.append(i)
|
|
416
|
+
if len(inddel) > 0:
|
|
417
|
+
selist = [selist[i] for i in indsel]
|
|
418
|
+
else:
|
|
419
|
+
rerun = False
|
|
420
|
+
gsac.stkdh = stkdh
|
|
421
|
+
gsac.selist = selist
|
|
422
|
+
nsel = len(selist)
|
|
423
|
+
print(f"\nDone selecting seismograms: {nsel:d} out of {len(saclist):d} selected.")
|
|
424
|
+
|
|
425
|
+
save = input("Save to file? [y/n] \n")
|
|
426
|
+
if save[0].lower() == "y":
|
|
427
|
+
if opts.filemode == "sac":
|
|
428
|
+
for sacdh in saclist:
|
|
429
|
+
sacdh.writeHdrs()
|
|
430
|
+
gsac.stkdh.savesac()
|
|
431
|
+
elif opts.filemode == "pkl":
|
|
432
|
+
print(" Saving gsac to pickle file...")
|
|
433
|
+
sacpkl.writePickle(gsac, opts.pklfile, opts.zipmode)
|
|
434
|
+
if opts.zipmode is not None:
|
|
435
|
+
pklfile = opts.pklfile + "." + opts.zipmode
|
|
436
|
+
else:
|
|
437
|
+
pklfile = opts.pklfile
|
|
438
|
+
if nsel < minnsel:
|
|
439
|
+
os.rename(pklfile, "deleted." + pklfile)
|
|
440
|
+
print(f" Less than {minnsel:d} seismograms selected. Remove pkl.")
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def checkCoverage(gsac, opts, textra=0.0):
|
|
444
|
+
"""Check if each seismogram has enough samples around the time window relative to ipick."""
|
|
445
|
+
ipick = opts.ipick
|
|
446
|
+
tw0, tw1 = opts.twcorr
|
|
447
|
+
saclist = gsac.saclist
|
|
448
|
+
nsac = len(saclist)
|
|
449
|
+
indsel, inddel = [], []
|
|
450
|
+
for i in range(nsac):
|
|
451
|
+
sacdh = saclist[i]
|
|
452
|
+
t0 = sacdh.gethdr(ipick)
|
|
453
|
+
b = sacdh.b
|
|
454
|
+
e = b + (sacdh.npts - 1) * sacdh.delta
|
|
455
|
+
if b - textra > t0 + tw0 or e + textra < t0 + tw1:
|
|
456
|
+
inddel.append(i)
|
|
457
|
+
print(
|
|
458
|
+
f"Seismogram {sacdh.filename:s} does not have enough sample. Deleted."
|
|
459
|
+
)
|
|
460
|
+
elif LA.norm(sacdh.data) == 0.0:
|
|
461
|
+
inddel.append(i)
|
|
462
|
+
print(f"Seismogram {sacdh.filename:s} has zero L2 norm. Deleted.")
|
|
463
|
+
else:
|
|
464
|
+
indsel.append(i)
|
|
465
|
+
if inddel != []:
|
|
466
|
+
gsac.saclist = [saclist[i] for i in indsel]
|
|
467
|
+
# print ('Updating gsac pickle file..')
|
|
468
|
+
# writePickle(gsac, opts.pklfile, opts.zipmode)
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def main():
|
|
472
|
+
cli_deprecation_notice()
|
|
473
|
+
opts, ifiles = getOptions()
|
|
474
|
+
ccpara = ttconfig.CCConfig()
|
|
475
|
+
gsac = sacpkl.loadData(ifiles, opts, ccpara)
|
|
476
|
+
opts.ccpara = ccpara
|
|
477
|
+
ccpara.twcorr = opts.twcorr
|
|
478
|
+
ccpara.cchdrs = [opts.ipick, opts.wpick]
|
|
479
|
+
# check data coverage, initialize quality factors
|
|
480
|
+
checkCoverage(gsac, opts)
|
|
481
|
+
qualsort.initQual(gsac.saclist, opts.ccpara.hdrsel, opts.ccpara.qheaders)
|
|
482
|
+
pdata.seisTimeData(gsac.saclist)
|
|
483
|
+
|
|
484
|
+
if opts.auto_on:
|
|
485
|
+
autoiccs(gsac, opts)
|
|
486
|
+
elif opts.auto_on_all:
|
|
487
|
+
print("Selecting all seismograms..")
|
|
488
|
+
hdrsel = opts.ccpara.hdrsel
|
|
489
|
+
for sacdh in gsac.saclist:
|
|
490
|
+
sacdh.selected = True
|
|
491
|
+
sacdh.sethdr(hdrsel, "True")
|
|
492
|
+
autoiccs(gsac, opts)
|
|
493
|
+
else:
|
|
494
|
+
stkdh, stkdata, _ = ccWeightStack(gsac.saclist, opts)
|
|
495
|
+
gsac.stkdh = stkdh
|
|
496
|
+
sacpkl.saveData(gsac, opts)
|
|
497
|
+
if opts.plotiter:
|
|
498
|
+
plotiter(stkdata)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
if __name__ == "__main__":
|
|
502
|
+
main()
|