Included rotation of seismograms using Obspys stream.rotation for a more reliable estimation of source spectra.
This commit is contained in:
parent
77ad274f8f
commit
d6ae82e070
@ -12,7 +12,7 @@ from obspy.core import Stream, UTCDateTime
|
|||||||
from pylot.core.pick.utils import getsignalwin, crossings_nonzero_all
|
from pylot.core.pick.utils import getsignalwin, crossings_nonzero_all
|
||||||
from pylot.core.util.utils import getPatternLine
|
from pylot.core.util.utils import getPatternLine
|
||||||
from scipy.optimize import curve_fit
|
from scipy.optimize import curve_fit
|
||||||
from scipy import integrate
|
from scipy import integrate, signal
|
||||||
from pylot.core.read.data import Data
|
from pylot.core.read.data import Data
|
||||||
|
|
||||||
class Magnitude(object):
|
class Magnitude(object):
|
||||||
@ -200,7 +200,7 @@ class WApp(Magnitude):
|
|||||||
class M0Mw(Magnitude):
|
class M0Mw(Magnitude):
|
||||||
'''
|
'''
|
||||||
Method to calculate seismic moment Mo and moment magnitude Mw.
|
Method to calculate seismic moment Mo and moment magnitude Mw.
|
||||||
Requires results of class w0fc for calculating plateau w0
|
Requires results of class calcsourcespec for calculating plateau w0
|
||||||
and corner frequency fc of source spectrum, respectively. Uses
|
and corner frequency fc of source spectrum, respectively. Uses
|
||||||
subfunction calcMoMw.py. Returns modified dictionary of picks including
|
subfunction calcMoMw.py. Returns modified dictionary of picks including
|
||||||
Dc-value, corner frequency fc, seismic moment Mo and
|
Dc-value, corner frequency fc, seismic moment Mo and
|
||||||
@ -212,17 +212,12 @@ class M0Mw(Magnitude):
|
|||||||
picks = self.getpicks()
|
picks = self.getpicks()
|
||||||
nllocfile = self.getNLLocfile()
|
nllocfile = self.getNLLocfile()
|
||||||
wfdat = self.getwfstream()
|
wfdat = self.getwfstream()
|
||||||
# get vertical component data only
|
self.picdic = None
|
||||||
zdat = wfdat.select(component="Z")
|
|
||||||
if len(zdat) == 0: # check for other components
|
|
||||||
zdat = wfdat.select(component="3")
|
|
||||||
|
|
||||||
for key in picks:
|
for key in picks:
|
||||||
if picks[key]['P']['weight'] < 4:
|
if picks[key]['P']['weight'] < 4:
|
||||||
# select waveform
|
# select waveform
|
||||||
selwf = zdat.select(station=key)
|
selwf = wfdat.select(station=key)
|
||||||
# get hypocentral distance of station
|
|
||||||
# from NLLoc-location file
|
|
||||||
if len(key) > 4:
|
if len(key) > 4:
|
||||||
Ppattern = '%s ? ? ? P' % key
|
Ppattern = '%s ? ? ? P' % key
|
||||||
elif len(key) == 4:
|
elif len(key) == 4:
|
||||||
@ -230,15 +225,22 @@ class M0Mw(Magnitude):
|
|||||||
elif len(key) < 4:
|
elif len(key) < 4:
|
||||||
Ppattern = '%s ? ? ? P' % key
|
Ppattern = '%s ? ? ? P' % key
|
||||||
nllocline = getPatternLine(nllocfile, Ppattern)
|
nllocline = getPatternLine(nllocfile, Ppattern)
|
||||||
|
# get hypocentral distance, station azimuth and
|
||||||
|
# angle of incidence from NLLoc-location file
|
||||||
delta = float(nllocline.split(None)[21])
|
delta = float(nllocline.split(None)[21])
|
||||||
|
az = float(nllocline.split(None)[22])
|
||||||
|
inc = float(nllocline.split(None)[24])
|
||||||
# call subfunction to estimate source spectrum
|
# call subfunction to estimate source spectrum
|
||||||
# and to derive w0 and fc
|
# and to derive w0 and fc
|
||||||
[w0, fc] = calcsourcespec(selwf, picks[key]['P']['mpp'], \
|
[w0, fc] = calcsourcespec(selwf, picks[key]['P']['mpp'], \
|
||||||
self.getiplot(), self.getinvdir())
|
self.getinvdir(), az, inc, self.getiplot())
|
||||||
|
|
||||||
if w0 is not None:
|
if w0 is not None:
|
||||||
# call subfunction to calculate Mo and Mw
|
# call subfunction to calculate Mo and Mw
|
||||||
[Mo, Mw] = calcMoMw(selwf, w0, self.getrho(), self.getvp(), \
|
zdat = selwf.select(component="Z")
|
||||||
|
if len(zdat) == 0: # check for other components
|
||||||
|
zdat = selwf.select(component="3")
|
||||||
|
[Mo, Mw] = calcMoMw(zdat, w0, self.getrho(), self.getvp(), \
|
||||||
delta, self.getinvdir())
|
delta, self.getinvdir())
|
||||||
else:
|
else:
|
||||||
Mo = None
|
Mo = None
|
||||||
@ -276,131 +278,180 @@ def calcMoMw(wfstream, w0, rho, vp, delta, inv):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def calcsourcespec(wfstream, onset, iplot, inventory):
|
def calcsourcespec(wfstream, onset, inventory, azimuth, incidence, iplot):
|
||||||
'''
|
'''
|
||||||
Subfunction to calculate the source spectrum and to derive from that the plateau
|
Subfunction to calculate the source spectrum and to derive from that the plateau
|
||||||
(usually called omega0) and the corner frequency assuming Aki's omega-square
|
(usually called omega0) and the corner frequency assuming Aki's omega-square
|
||||||
source model. Has to be derived from instrument corrected displacement traces,
|
source model. Has to be derived from instrument corrected displacement traces,
|
||||||
thus restitution and integration necessary!
|
thus restitution and integration necessary! Integrated traces have to be rotated
|
||||||
|
into ray-coordinate system ZNE => LQT!
|
||||||
'''
|
'''
|
||||||
print ("Calculating source spectrum ....")
|
print ("Calculating source spectrum ....")
|
||||||
|
|
||||||
fc = None
|
fc = None
|
||||||
w0 = None
|
w0 = None
|
||||||
data = Data()
|
data = Data()
|
||||||
z_copy = wfstream.copy()
|
wf_copy = wfstream.copy()
|
||||||
|
|
||||||
[corzdat, restflag] = data.restituteWFData(inventory, z_copy)
|
|
||||||
|
|
||||||
|
[cordat, restflag] = data.restituteWFData(inventory, wf_copy)
|
||||||
if restflag == 1:
|
if restflag == 1:
|
||||||
# integrate to displacment
|
zdat = cordat.select(component="Z")
|
||||||
corintzdat = integrate.cumtrapz(corzdat[0], None, corzdat[0].stats.delta)
|
if len(zdat) == 0:
|
||||||
z_copy[0].data = corintzdat
|
zdat = cordat.select(component="3")
|
||||||
tr = z_copy[0]
|
cordat_copy = cordat.copy()
|
||||||
# get window after P pulse for
|
# get equal time stamps and lengths of traces
|
||||||
# calculating source spectrum
|
# necessary for rotation of traces
|
||||||
if tr.stats.sampling_rate <= 100:
|
tr0start = cordat_copy[0].stats.starttime
|
||||||
winzc = tr.stats.sampling_rate
|
tr0start = tr0start.timestamp
|
||||||
elif tr.stats.sampling_rate > 100 and \
|
tr0end = cordat_copy[0].stats.endtime
|
||||||
tr.stats.sampling_rate <= 200:
|
tr0end = tr0end.timestamp
|
||||||
winzc = 0.5 * tr.stats.sampling_rate
|
tr1start = cordat_copy[1].stats.starttime
|
||||||
elif tr.stats.sampling_rate > 200 and \
|
tr1start = tr1start.timestamp
|
||||||
tr.stats.sampling_rate <= 400:
|
tr1end = cordat_copy[1].stats.endtime
|
||||||
winzc = 0.2 * tr.stats.sampling_rate
|
tr1end = tr1end.timestamp
|
||||||
elif tr.stats.sampling_rate > 400:
|
tr2start = cordat_copy[2].stats.starttime
|
||||||
winzc = tr.stats.sampling_rate
|
tr2start = tr2start.timestamp
|
||||||
tstart = UTCDateTime(tr.stats.starttime)
|
tr2end = cordat_copy[0].stats.endtime
|
||||||
tonset = onset.timestamp -tstart.timestamp
|
tr2end = tr2end.timestamp
|
||||||
impickP = tonset * tr.stats.sampling_rate
|
trstart = UTCDateTime(max([tr0start, tr1start, tr2start]))
|
||||||
wfzc = tr.data[impickP : impickP + winzc]
|
trend = UTCDateTime(min([tr0end, tr1end, tr2end]))
|
||||||
# get time array
|
cordat_copy.trim(trstart, trend)
|
||||||
t = np.arange(0, len(tr) * tr.stats.delta, tr.stats.delta)
|
minlen = min([len(cordat_copy[0]), len(cordat_copy[1]), len(cordat_copy[2])])
|
||||||
# calculate spectrum using only first cycles of
|
cordat_copy[0].data = cordat_copy[0].data[0:minlen]
|
||||||
# waveform after P onset!
|
cordat_copy[1].data = cordat_copy[1].data[0:minlen]
|
||||||
zc = crossings_nonzero_all(wfzc)
|
cordat_copy[2].data = cordat_copy[2].data[0:minlen]
|
||||||
if np.size(zc) == 0 or len(zc) <= 3:
|
try:
|
||||||
print ("Something is wrong with the waveform, "
|
# rotate into LQT (ray-coordindate-) system using Obspy's rotate
|
||||||
"no zero crossings derived!")
|
# L: P-wave direction
|
||||||
print ("No calculation of source spectrum possible!")
|
# Q: SV-wave direction
|
||||||
plotflag = 0
|
# T: SH-wave direction
|
||||||
else:
|
LQT=cordat_copy.rotate('ZNE->LQT',azimuth, incidence)
|
||||||
plotflag = 1
|
ldat = LQT.select(component="L")
|
||||||
index = min([3, len(zc) - 1])
|
if len(ldat) == 0:
|
||||||
calcwin = (zc[index] - zc[0]) * z_copy[0].stats.delta
|
# yet Obspy's rotate can not handle channels 3/2/1
|
||||||
iwin = getsignalwin(t, tonset, calcwin)
|
ldat = LQT.select(component="Z")
|
||||||
xdat = tr.data[iwin]
|
|
||||||
|
|
||||||
# fft
|
# integrate to displacement
|
||||||
fny = tr.stats.sampling_rate / 2
|
# unrotated vertical component (for copmarison)
|
||||||
l = len(xdat) / tr.stats.sampling_rate
|
inttrz = signal.detrend(integrate.cumtrapz(zdat[0].data, None, \
|
||||||
n = tr.stats.sampling_rate * l # number of fft bins after Bath
|
zdat[0].stats.delta))
|
||||||
# find next power of 2 of data length
|
# rotated component Z => L
|
||||||
m = pow(2, np.ceil(np.log(len(xdat)) / np.log(2)))
|
Ldat = signal.detrend(integrate.cumtrapz(ldat[0].data, None, \
|
||||||
N = int(np.power(m, 2))
|
ldat[0].stats.delta))
|
||||||
y = tr.stats.delta * np.fft.fft(xdat, N)
|
|
||||||
Y = abs(y[: N/2])
|
|
||||||
L = (N - 1) / tr.stats.sampling_rate
|
|
||||||
f = np.arange(0, fny, 1/L)
|
|
||||||
|
|
||||||
# remove zero-frequency and frequencies above
|
# get window after P pulse for
|
||||||
# corner frequency of seismometer (assumed
|
# calculating source spectrum
|
||||||
# to be 100 Hz)
|
if zdat[0].stats.sampling_rate <= 100:
|
||||||
fi = np.where((f >= 1) & (f < 100))
|
winzc = zdat[0].stats.sampling_rate
|
||||||
F = f[fi]
|
elif zdat[0].stats.sampling_rate > 100 and \
|
||||||
YY = Y[fi]
|
zdat[0].stats.sampling_rate <= 200:
|
||||||
# get plateau (DC value) and corner frequency
|
winzc = 0.5 * zdat[0].stats.sampling_rate
|
||||||
# initial guess of plateau
|
elif zdat[0].stats.sampling_rate > 200 and \
|
||||||
w0in = np.mean(YY[0:100])
|
zdat[0].stats.sampling_rate <= 400:
|
||||||
# initial guess of corner frequency
|
winzc = 0.2 * zdat[0].stats.sampling_rate
|
||||||
# where spectral level reached 50% of flat level
|
elif zdat[0].stats.sampling_rate > 400:
|
||||||
iin = np.where(YY >= 0.5 * w0in)
|
winzc = zdat[0].stats.sampling_rate
|
||||||
Fcin = F[iin[0][np.size(iin) - 1]]
|
tstart = UTCDateTime(zdat[0].stats.starttime)
|
||||||
|
tonset = onset.timestamp -tstart.timestamp
|
||||||
|
impickP = tonset * zdat[0].stats.sampling_rate
|
||||||
|
wfzc = Ldat[impickP : impickP + winzc]
|
||||||
|
# get time array
|
||||||
|
t = np.arange(0, len(inttrz) * zdat[0].stats.delta, \
|
||||||
|
zdat[0].stats.delta)
|
||||||
|
# calculate spectrum using only first cycles of
|
||||||
|
# waveform after P onset!
|
||||||
|
zc = crossings_nonzero_all(wfzc)
|
||||||
|
if np.size(zc) == 0 or len(zc) <= 3:
|
||||||
|
print ("calcsourcespec: Something is wrong with the waveform, "
|
||||||
|
"no zero crossings derived!")
|
||||||
|
print ("No calculation of source spectrum possible!")
|
||||||
|
plotflag = 0
|
||||||
|
else:
|
||||||
|
plotflag = 1
|
||||||
|
index = min([3, len(zc) - 1])
|
||||||
|
calcwin = (zc[index] - zc[0]) * zdat[0].stats.delta
|
||||||
|
iwin = getsignalwin(t, tonset, calcwin)
|
||||||
|
xdat = Ldat[iwin]
|
||||||
|
|
||||||
# use of implicit scipy otimization function
|
# fft
|
||||||
fit = synthsourcespec(F, w0in, Fcin)
|
fny = zdat[0].stats.sampling_rate / 2
|
||||||
[optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [w0in, Fcin])
|
l = len(xdat) / zdat[0].stats.sampling_rate
|
||||||
w01 = optspecfit[0]
|
# number of fft bins after Bath
|
||||||
fc1 = optspecfit[1]
|
n = zdat[0].stats.sampling_rate * l
|
||||||
print ("w0fc: Determined w0-value: %e m/Hz, \n"
|
# find next power of 2 of data length
|
||||||
"Determined corner frequency: %f Hz" % (w01, fc1))
|
m = pow(2, np.ceil(np.log(len(xdat)) / np.log(2)))
|
||||||
|
N = int(np.power(m, 2))
|
||||||
|
y = zdat[0].stats.delta * np.fft.fft(xdat, N)
|
||||||
|
Y = abs(y[: N/2])
|
||||||
|
L = (N - 1) / zdat[0].stats.sampling_rate
|
||||||
|
f = np.arange(0, fny, 1/L)
|
||||||
|
|
||||||
|
# remove zero-frequency and frequencies above
|
||||||
|
# corner frequency of seismometer (assumed
|
||||||
|
# to be 100 Hz)
|
||||||
|
fi = np.where((f >= 1) & (f < 100))
|
||||||
|
F = f[fi]
|
||||||
|
YY = Y[fi]
|
||||||
|
# get plateau (DC value) and corner frequency
|
||||||
|
# initial guess of plateau
|
||||||
|
w0in = np.mean(YY[0:100])
|
||||||
|
# initial guess of corner frequency
|
||||||
|
# where spectral level reached 50% of flat level
|
||||||
|
iin = np.where(YY >= 0.5 * w0in)
|
||||||
|
Fcin = F[iin[0][np.size(iin) - 1]]
|
||||||
|
|
||||||
|
# use of implicit scipy otimization function
|
||||||
|
fit = synthsourcespec(F, w0in, Fcin)
|
||||||
|
[optspecfit, pcov] = curve_fit(synthsourcespec, F, YY.real, [w0in, Fcin])
|
||||||
|
w01 = optspecfit[0]
|
||||||
|
fc1 = optspecfit[1]
|
||||||
|
print ("calcsourcespec: Determined w0-value: %e m/Hz, \n"
|
||||||
|
"Determined corner frequency: %f Hz" % (w01, fc1))
|
||||||
|
|
||||||
# use of conventional fitting
|
# use of conventional fitting
|
||||||
[w02, fc2] = fitSourceModel(F, YY.real, Fcin, iplot)
|
[w02, fc2] = fitSourceModel(F, YY.real, Fcin, iplot)
|
||||||
|
|
||||||
# get w0 and fc as median
|
# get w0 and fc as median
|
||||||
w0 = np.median([w01, w02])
|
w0 = np.median([w01, w02])
|
||||||
fc = np.median([fc1, fc2])
|
fc = np.median([fc1, fc2])
|
||||||
print("w0fc: Using w0-value = %e m/Hz and fc = %f Hz" % (w0, fc))
|
print("calcsourcespec: Using w0-value = %e m/Hz and fc = %f Hz" % (w0, fc))
|
||||||
|
|
||||||
|
except TypeError as er:
|
||||||
|
raise TypeError('''{0}'''.format(er))
|
||||||
|
|
||||||
if iplot > 1:
|
if iplot > 1:
|
||||||
f1 = plt.figure()
|
f1 = plt.figure()
|
||||||
plt.subplot(2,1,1)
|
tLdat = np.arange(0, len(Ldat) * zdat[0].stats.delta, \
|
||||||
# show displacement in mm
|
zdat[0].stats.delta)
|
||||||
plt.plot(t, np.multiply(tr, 1000), 'k')
|
plt.subplot(2,1,1)
|
||||||
if plotflag == 1:
|
# show displacement in mm
|
||||||
plt.plot(t[iwin], np.multiply(xdat, 1000), 'g')
|
p1, = plt.plot(t, np.multiply(inttrz, 1000), 'k')
|
||||||
plt.title('Seismogram and P Pulse, Station %s-%s' \
|
p2, = plt.plot(tLdat, np.multiply(Ldat, 1000))
|
||||||
% (tr.stats.station, tr.stats.channel))
|
plt.legend([p1, p2], ['Displacement', 'Rotated Displacement'])
|
||||||
else:
|
if plotflag == 1:
|
||||||
plt.title('Seismogram, Station %s-%s' \
|
plt.plot(t[iwin], np.multiply(xdat, 1000), 'g')
|
||||||
% (tr.stats.station, tr.stats.channel))
|
plt.title('Seismogram and P Pulse, Station %s-%s' \
|
||||||
plt.xlabel('Time since %s' % tr.stats.starttime)
|
% (zdat[0].stats.station, zdat[0].stats.channel))
|
||||||
plt.ylabel('Displacement [mm]')
|
else:
|
||||||
|
plt.title('Seismogram, Station %s-%s' \
|
||||||
|
% (zdat[0].stats.station, zdat[0].stats.channel))
|
||||||
|
plt.xlabel('Time since %s' % zdat[0].stats.starttime)
|
||||||
|
plt.ylabel('Displacement [mm]')
|
||||||
|
|
||||||
if plotflag == 1:
|
if plotflag == 1:
|
||||||
plt.subplot(2,1,2)
|
plt.subplot(2,1,2)
|
||||||
plt.loglog(f, Y.real, 'k')
|
plt.loglog(f, Y.real, 'k')
|
||||||
plt.loglog(F, YY.real)
|
plt.loglog(F, YY.real)
|
||||||
plt.loglog(F, fit, 'g')
|
plt.loglog(F, fit, 'g')
|
||||||
plt.loglog([fc, fc], [w0/100, w0], 'g')
|
plt.loglog([fc, fc], [w0/100, w0], 'g')
|
||||||
plt.title('Source Spectrum from P Pulse, w0=%e m/Hz, fc=%6.2f Hz' \
|
plt.title('Source Spectrum from P Pulse, w0=%e m/Hz, fc=%6.2f Hz' \
|
||||||
% (w0, fc))
|
% (w0, fc))
|
||||||
plt.xlabel('Frequency [Hz]')
|
plt.xlabel('Frequency [Hz]')
|
||||||
plt.ylabel('Amplitude [m/Hz]')
|
plt.ylabel('Amplitude [m/Hz]')
|
||||||
plt.grid()
|
plt.grid()
|
||||||
plt.show()
|
plt.show()
|
||||||
raw_input()
|
raw_input()
|
||||||
plt.close(f1)
|
plt.close(f1)
|
||||||
|
|
||||||
return w0, fc
|
return w0, fc
|
||||||
|
|
||||||
@ -474,8 +525,13 @@ def fitSourceModel(f, S, fc0, iplot):
|
|||||||
STD.append(stddc + stdFC)
|
STD.append(stddc + stdFC)
|
||||||
|
|
||||||
# get best found w0 anf fc from minimum
|
# get best found w0 anf fc from minimum
|
||||||
fc = fc[np.argmin(STD)]
|
if len(STD) > 0:
|
||||||
w0 = w0[np.argmin(STD)]
|
fc = fc[np.argmin(STD)]
|
||||||
|
w0 = w0[np.argmin(STD)]
|
||||||
|
elif len(STD) == 0:
|
||||||
|
fc = fc0
|
||||||
|
w0 = max(S)
|
||||||
|
|
||||||
print("fitSourceModel: best fc: %fHz, best w0: %e m/Hz" \
|
print("fitSourceModel: best fc: %fHz, best w0: %e m/Hz" \
|
||||||
% (fc, w0))
|
% (fc, w0))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user