Skip to content

Commit 935ac26

Browse files
committed
New parameter: spectral_smooth_width_decades
Fixes #2
1 parent a0fc66b commit 935ac26

File tree

3 files changed

+44
-14
lines changed

3 files changed

+44
-14
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Earthquake source parameters from S-wave displacement spectra
2222
source radius, Brune stress drop, source radius, quality factor
2323
- Colored console output for log messages! (Not supported on Windows)
2424
- Processing:
25+
- New parameter for setting the width of the spectral smoothing window
26+
in terms of frequency decades: `spectral_smooth_width_decades`
2527
- Compute spectral weights after spectral correction (when a station
2628
residuals file is specified via `residuals_filepath`)
2729
- New approach for trace clipping detection (requires just one configuration

sourcespec/configspec.conf

+14
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,20 @@ taper_halfwidth = float(0, 0.5, default=0.05)
119119
# S-wave window as spectral window length.
120120
spectral_win_length = float(default=None)
121121

122+
# Spectral smoothing window width in frequency decades
123+
# (i.e., log10 frequency scale).
124+
# Example:
125+
# spectral_smooth_width_decades=1 means a width of 1 decade
126+
# (generally, too large, producing a spectrum which is too smooth).
127+
# spectrum(f0) is smoothed using values between f1 and f2, so that
128+
# log10(f1)=log10(f0)-0.5 and log10(f2)=log10(f0)+0.5
129+
# i.e.,
130+
# f1=f0/(10^0.5) and f2=f0*(10^0.5)
131+
# or,
132+
# f2/f1=10 (1 decade width)
133+
# Default value of 0.2 is generally a good choice
134+
spectral_smooth_width_decades = float(default=0.2)
135+
122136
# Residuals file path
123137
# (a pickle file with the mean residuals per station,
124138
# used for station correction):

sourcespec/ssp_build_spectra.py

+28-14
Original file line numberDiff line numberDiff line change
@@ -209,16 +209,29 @@ def _displacement_to_moment(stats, config):
209209
return coeff
210210

211211

212-
def _smooth_spectrum(spec, npts=5):
213-
"""Smooth spectrum in a log_freq space."""
212+
def _smooth_spectrum(spec, smooth_width_decades=0.2):
213+
"""Smooth spectrum in a log10-freq space."""
214+
# 1. Generate log10-spaced frequencies
214215
freq = spec.get_freq()
215-
f = interp1d(freq, spec.data, fill_value='extrapolate')
216-
freq_log = np.logspace(np.log10(freq[0]), np.log10(freq[-1]))
216+
_log_freq = np.log10(freq)
217+
# frequencies in logarithmic spacing
218+
freq_log = np.logspace(_log_freq[0], _log_freq[-1], len(_log_freq))
217219
spec.freq_log = freq_log
218-
spec.data_log = f(freq_log)
219-
spec.data_log = smooth(spec.data_log, window_len=npts)
220+
# 2. Reinterpolate data using log10 frequencies
221+
# make sure that extrapolation does not create negative values
222+
f = interp1d(freq, spec.data, fill_value='extrapolate')
223+
_data_log = f(freq_log)
224+
_data_log[_data_log <= 0] = np.min(spec.data)
225+
# 3. Smooth log10-spaced data points
226+
log_df = np.log10(freq_log[1]) - np.log10(freq_log[0])
227+
npts = max(1, int(round(smooth_width_decades/log_df)))
228+
spec.data_log = smooth(_data_log, window_len=npts)
229+
# 4. Reinterpolate to linear frequencies
230+
# make sure that extrapolation does not create negative values
220231
f = interp1d(freq_log, spec.data_log, fill_value='extrapolate')
221-
spec.data = f(freq)
232+
_data = f(freq)
233+
_data[_data <= 0] = np.min(spec.data)
234+
spec.data = _data
222235

223236

224237
def _build_spectrum(config, trace):
@@ -246,11 +259,11 @@ def _build_spectrum(config, trace):
246259
# for radiated_energy()
247260
spec.coeff = coeff
248261
# smooth
249-
_smooth_spectrum(spec, npts=5)
262+
_smooth_spectrum(spec, config.spectral_smooth_width_decades)
250263
return spec
251264

252265

253-
def _build_weight(spec, specnoise):
266+
def _build_weight(config, spec, specnoise):
254267
weight = spec.copy()
255268
if specnoise is not None:
256269
weight.data /= specnoise.data
@@ -259,7 +272,8 @@ def _build_weight(spec, specnoise):
259272
# The inversion is done in magnitude units,
260273
# so let's take log10 of weight
261274
weight.data = np.log10(weight.data)
262-
_smooth_spectrum(weight, npts=11)
275+
# Weight spectrum is smoothed once more
276+
_smooth_spectrum(weight, config.spectral_smooth_width_decades)
263277
weight.data /= np.max(weight.data)
264278
# slightly taper weight at low frequencies, to avoid overestimating
265279
# weight at low frequencies, in cases where noise is underestimated
@@ -277,7 +291,7 @@ def _build_weight(spec, specnoise):
277291
return weight
278292

279293

280-
def _build_weight_st(spec_st, specnoise_st):
294+
def _build_weight_st(config, spec_st, specnoise_st):
281295
"""Build the weight spectrum."""
282296
weight_st = Stream()
283297
spec_ids = set(sp.id[:-1] for sp in spec_st if not sp.stats.ignore)
@@ -287,7 +301,7 @@ def _build_weight_st(spec_st, specnoise_st):
287301
specnoise_h = _select_spectra(specnoise_st, specid + 'H')[0]
288302
except Exception:
289303
continue
290-
weight = _build_weight(spec_h, specnoise_h)
304+
weight = _build_weight(config, spec_h, specnoise_h)
291305
weight_st.append(weight)
292306
return weight_st
293307

@@ -323,7 +337,7 @@ def _build_H(spec_st, specnoise_st=None, wave_type='S'):
323337

324338

325339
def _check_spectral_sn_ratio(config, spec, specnoise):
326-
weight = _build_weight(spec, specnoise)
340+
weight = _build_weight(config, spec, specnoise)
327341
if config.spectral_sn_freq_range is not None:
328342
sn_fmin, sn_fmax = config.spectral_sn_freq_range
329343
freqs = weight.get_freq()
@@ -396,7 +410,7 @@ def build_spectra(config, st):
396410
spec_st = station_correction(spec_st, config)
397411

398412
# build the weight spectrum
399-
weight_st = _build_weight_st(spec_st, specnoise_st)
413+
weight_st = _build_weight_st(config, spec_st, specnoise_st)
400414

401415
logger.info('Building spectra: done')
402416
if config.weighting == 'noise':

0 commit comments

Comments
 (0)