def build_spectra(config, st): """ Build spectra and the spec_st object. Computes S-wave (displacement) spectra from accelerometers and velocimeters, uncorrected for attenuation, corrected for instrumental constants, normalized by hypocentral distance. """ logger.info('Building spectra...') spec_st = Stream() specnoise_st = Stream() # sort by trace id for trace in sorted(st, key=lambda tr: tr.id): try: _check_data_len(config, trace) trace_signal, trace_noise = _cut_signal_noise(config, trace) _check_noise_level(trace_signal, trace_noise) spec = _build_spectrum(config, trace_signal) specnoise = _build_spectrum(config, trace_noise) _check_spectral_sn_ratio(config, spec, specnoise) except RuntimeError as msg: # RuntimeError is for skipped spectra logger.warning(msg) continue except ValueError as msg: # ValueError is for ignored spectra, which are still stored logger.warning(msg) trace.stats.ignore = True spec.stats.ignore = True specnoise.stats.ignore = True spec_st.append(spec) specnoise_st.append(specnoise) if not spec_st: logger.error('No spectra left! Exiting.') ssp_exit() # build H component _build_H(spec_st, specnoise_st, config.wave_type) # convert the spectral amplitudes to moment magnitude for spec in spec_st: spec.data_mag = moment_to_mag(spec.data) spec.data_log_mag = moment_to_mag(spec.data_log) # apply station correction if a residual file is specified in config spec_st = station_correction(spec_st, config) # build the weight spectrum weight_st = _build_weight_st(config, spec_st, specnoise_st) logger.info('Building spectra: done') if config.weighting == 'noise': for specnoise in specnoise_st: specnoise.data_mag = moment_to_mag(specnoise.data) return spec_st, specnoise_st, weight_st else: return spec_st
def make_synth(config, spec_st, trace_spec=None): import math import numpy as np from copy import deepcopy from sourcespec.spectrum import Spectrum from sourcespec.ssp_spectral_model import spectral_model, objective_func from sourcespec.ssp_util import mag_to_moment, moment_to_mag fdelta = 0.01 fmin = config.options.fmin fmax = config.options.fmax + fdelta residuals = list() n = 0 for fc, mag, Mo, t_star, alpha in zip(config.options.fc, config.options.mag, config.options.Mo, config.options.t_star, config.options.alpha): spec = Spectrum() if trace_spec: spec.stats = deepcopy(trace_spec.stats) else: spec.stats.begin = fmin spec.stats.delta = fdelta spec.stats.npts = int((fmax - fmin) / fdelta) if math.isnan(Mo): Mo = mag_to_moment(mag) else: mag = moment_to_mag(Mo) spec.stats.station = 'S{:02d}'.format(n) n += 1 spec.stats.instrtype = 'Synth' spec.stats.channel = spec.stats.channel[:-1] + 'S' spec.stats.par = { 'Mw': mag, 'fc': fc, 't_star': t_star, 'alpha': alpha } freq = spec.get_freq() freq_log = trace_spec.freq_log spec.freq_log = freq_log spec.data_mag = spectral_model(freq, mag, fc, t_star, alpha) spec.data = mag_to_moment(spec.data_mag) spec.data_log_mag = spectral_model(freq_log, mag, fc, t_star, alpha) spec.data_log = mag_to_moment(spec.data_log_mag) spec_st.append(spec) if trace_spec: objective_func2 = objective_func(freq, trace_spec.data_mag, np.ones_like(trace_spec.data_mag)) print(Mo, mag, fc, t_star, alpha, objective_func2((mag, fc, t_star, alpha))) residuals.append([ Mo, mag, fc, t_star, objective_func2((mag, fc, t_star, alpha)) ])
def make_synth(config, spec_st, trace_spec=None): fdelta = 0.01 fmin = config.options.fmin fmax = config.options.fmax + fdelta residuals = list() for fc, mag, Mo, t_star, alpha in zip(config.options.fc, config.options.mag, config.options.Mo, config.options.t_star, config.options.alpha): spec = Spectrum() if trace_spec: spec.stats = deepcopy(trace_spec.stats) else: spec.stats.begin = fmin spec.stats.delta = fdelta spec.stats.npts = int((fmax - fmin) / fdelta) if math.isnan(Mo): Mo = mag_to_moment(mag) else: mag = moment_to_mag(Mo) spec.stats.station = 'Mw: %.1f fc: %.2fHz t*: %.2fs alpha: %.2f' %\ (mag, fc, t_star, alpha) spec.stats.instrtype = 'Synth' spec.stats.channel = spec.stats.channel[:-1] + 'S' spec.stats.par = { 'Mw': mag, 'fc': fc, 't_star': t_star, 'alpha': alpha } freq = spec.get_freq() spec.data_mag = spectral_model(freq, mag, fc, t_star, alpha) spec.data = mag_to_moment(spec.data_mag) spec_st.append(spec) if trace_spec: objective_func2 = objective_func(freq, trace_spec.data_mag, np.ones_like(trace_spec.data_mag)) print(Mo, mag, fc, t_star, alpha, objective_func2((mag, fc, t_star, alpha))) residuals.append([ Mo, mag, fc, t_star, objective_func2((mag, fc, t_star, alpha)) ])
def station_correction(spec_st, config): """ Correct spectra using station-average residuals. Residuals are obtained from a previous run. """ res_filepath = config.residuals_filepath if res_filepath is None: return spec_st try: with open(res_filepath, 'rb') as fp: residual = pickle.load(fp) except Exception as msg: logger.error(msg) ssp_exit(1) for spec in [spec for spec in spec_st if (spec.stats.channel[-1] == 'H')]: station = spec.stats.station if station in set(x.stats.station for x in residual): # apply correction corr = residual.select(station=station)[0] freq = spec.get_freq() fmin = freq.min() fmax = freq.max() corr = corr.slice(fmin, fmax) corr.data_mag = moment_to_mag(corr.data) spec_corr = spec.copy() # uncorrected spectrum will have component name 'h' spec.stats.channel = spec.stats.channel[:-1] + 'h' spec_corr.data_mag -= corr.data_mag # interpolate the corrected data_mag to log frequencies f = interp1d(freq, spec_corr.data_mag, fill_value='extrapolate') spec_corr.data_log_mag = f(spec_corr.freq_log) # convert mag to moment spec_corr.data = mag_to_moment(spec_corr.data_mag) spec_corr.data_log = mag_to_moment(spec_corr.data_log_mag) spec_st.append(spec_corr) logger.info( '{} corrected, frequency range is: {:.2f} {:.2f} Hz'.format( spec_corr.id, fmin, fmax)) return spec_st
def _set_plot_params(config, spec_st, specnoise_st, ncols, plot_params): """Determine the number of plots and axes min and max.""" nplots = 0 moment_minmax = None freq_minmax = None if not config.plot_spectra_ignored: _spec_st = Stream(sp for sp in spec_st if not sp.stats.ignore) else: _spec_st = spec_st specids = set('.'.join(sp.id.split('.')[:-1]) for sp in _spec_st) for specid in specids: network, station, location = specid.split('.') spec_st_sel = _spec_st.select( network=network, station=station, location=location) if specnoise_st: specnoise_sel = specnoise_st.select( network=network, station=station, location=location) spec_st_sel += specnoise_sel for spec in spec_st_sel: moment_minmax, freq_minmax =\ spec_minmax(spec.data, spec.get_freq(), moment_minmax, freq_minmax) # 'code' is band+instrument code for code in set(x.stats.channel[:-1] for x in spec_st_sel): nplots += 1 nlines = int(math.ceil(nplots/ncols)) maxlines = config.plot_spectra_maxrows if nlines > maxlines: nlines = maxlines if plot_params.plot_type != 'weight': moment_minmax[1] *= 10 mag_minmax = moment_to_mag(moment_minmax) else: mag_minmax = None plot_params.nlines = nlines plot_params.ncols = ncols plot_params.freq_minmax = freq_minmax plot_params.moment_minmax = moment_minmax plot_params.mag_minmax = mag_minmax
def station_correction(spec_st, config): """ Correct spectra using station-average residuals. Residuals are obtained from a previous run. """ res_filepath = config.residuals_filepath if res_filepath is None: msg = "'-C' option set, but 'residuals_filepath' not specified " msg += "in config file: ignoring station correction" logger.warning(msg) return spec_st with open(res_filepath, 'rb') as fp: residual = pickle.load(fp) for spec in [spec for spec in spec_st if (spec.stats.channel[-1] == 'H')]: station = spec.stats.station if station in set(x.stats.station for x in residual): # apply correction corr = residual.select(station=station)[0] freq = spec.get_freq() fmin = freq.min() fmax = freq.max() corr = corr.slice(fmin, fmax) corr.data_mag = moment_to_mag(corr.data) spec.data_mag -= corr.data_mag # interpolate the corrected data_mag to log frequencies f = interp1d(freq, spec.data_mag, fill_value='extrapolate') spec.data_log_mag = f(spec.freq_log) # convert mag to moment spec.data = mag_to_moment(spec.data_mag) spec.data_log = mag_to_moment(spec.data_log_mag) logger.info('%s corrected, frequency range is: %f %f' % (spec.id, fmin, fmax)) return spec_st
def build_spectra(config, st, noise_weight=False): """ Build spectra and the spec_st object. Computes S-wave (displacement) spectra from accelerometers and velocimeters, uncorrected for attenuation, corrected for instrumental constants, normalized by hypocentral distance. """ spec_st = Stream() specnoise_st = Stream() # sort by sampling rate: this limits the number of times on which # konno-ohmachi smoothing matrix is recomputed for trace in st.sort(keys=['sampling_rate', 'station']): try: _check_data_len(config, trace) trace_signal, trace_noise = _cut_signal_noise(config, trace) _check_noise_level(trace_signal, trace_noise) except (ValueError, RuntimeError): continue spec = _build_spectrum(config, trace_signal) if noise_weight: specnoise = _build_spectrum(config, trace_noise) weight = _build_weight(spec, specnoise) if config.spectral_sn_freq_range is not None: sn_fmin, sn_fmax = config.spectral_sn_freq_range freqs = weight.get_freq() idx = np.where((sn_fmin <= freqs)*(freqs <= sn_fmax)) else: idx = range(len(weight.data_raw)) spectral_snratio =\ weight.data_raw[idx].sum()/len(weight.data_raw[idx]) spec.stats.spectral_snratio = spectral_snratio logger.info('%s: spectral S/N: %.2f' % (spec.get_id(), spectral_snratio)) if config.spectral_sn_min: ssnmin = config.spectral_sn_min if spectral_snratio < ssnmin: logger.warning('%s: spectral S/N smaller than %.2f: ' 'ignoring spectrum' % (spec.get_id(), ssnmin)) trace.stats.ignore = True spec.stats.ignore = True specnoise.stats.ignore = True specnoise_st.append(specnoise) spec_st.append(spec) # build H component and weight_st weight_st = _build_H_and_weight(spec_st, specnoise_st, config.wave_type) # convert the spectral amplitudes to moment magnitude for spec in spec_st: spec.data_mag = moment_to_mag(spec.data) spec.data_log_mag = moment_to_mag(spec.data_log) # optionally, apply station correction if config.options.correction: spec_st = station_correction(spec_st, config) if noise_weight: for specnoise in specnoise_st: specnoise.data_mag = moment_to_mag(specnoise.data) return spec_st, specnoise_st, weight_st else: return spec_st
freqs_min = [spec.get_freq().min() for spec in res] freqs_max = [spec.get_freq().max() for spec in res] freq_min = min(freqs_min) freq_max = max(freqs_max) spec_mean = Spectrum() spec_mean.stats.begin = freq_min spec_mean.stats.delta = res[0].stats.delta spec_mean.stats.station = res[0].stats.station spec_mean.data_mag = None for spec in res: spec_slice = spec.slice(freq_min, freq_max, pad=True, fill_value=mag_to_moment(0)) spec_slice.data_mag = moment_to_mag(spec_slice.data) norm = (spec_slice.data_mag != 0).astype(int) if spec_mean.data_mag is None: spec_mean.data_mag = spec_slice.data_mag norm_mean = norm else: spec_mean.data_mag += spec_slice.data_mag norm_mean += norm spec_mean.data_mag /= norm_mean spec_mean.data = mag_to_moment(spec_mean.data_mag) residual_mean.append(spec_mean) # plot traces if options.plot: stnm = spec_mean.stats.station
def main(): usage = 'usage: %prog [options] residuals_dir' parser = OptionParser(usage=usage) parser.add_option('-m', '--min_spectra', dest='min_spectra', action='store', default='20', help='minimum number of spectra to ' 'compute residuals (default=20)', metavar='NUMBER') parser.add_option('-p', '--plot', dest='plot', action='store_true', default=False, help='save residuals plots to file') (options, args) = parser.parse_args() if len(args) < 1: parser.print_usage(file=sys.stderr) sys.stderr.write("\tUse '-h' for help\n\n") sys.exit(1) resdir = args[0] min_spectra = int(options.min_spectra) outdir = 'sspec_residuals' residual_dict = defaultdict(Stream) for resfile in glob(os.path.join(resdir, '*-res*.pickle')): print(resfile) with open(resfile, 'rb') as fp: residual_st = pickle.load(fp) for spec in residual_st: residual_dict[spec.id].append(spec) if not os.path.exists(outdir): os.makedirs(outdir) residual_mean = Stream() for stat_id in sorted(residual_dict.keys()): if len(residual_dict[stat_id]) < min_spectra: continue print(stat_id) res = residual_dict[stat_id] freqs_min = [spec.get_freq().min() for spec in res] freqs_max = [spec.get_freq().max() for spec in res] freq_min = min(freqs_min) freq_max = max(freqs_max) spec_mean = Spectrum() spec_mean.stats.begin = freq_min spec_mean.stats.delta = res[0].stats.delta spec_mean.stats.station = res[0].stats.station spec_mean.data_mag = None for spec in res: spec_slice = spec.slice(freq_min, freq_max, pad=True, fill_value=mag_to_moment(0)) spec_slice.data_mag = moment_to_mag(spec_slice.data) norm = (spec_slice.data_mag != 0).astype(int) if spec_mean.data_mag is None: spec_mean.data_mag = spec_slice.data_mag norm_mean = norm else: spec_mean.data_mag += spec_slice.data_mag norm_mean += norm spec_mean.data_mag /= norm_mean spec_mean.data = mag_to_moment(spec_mean.data_mag) residual_mean.append(spec_mean) # plot traces if options.plot: stnm = spec_mean.stats.station figurefile = os.path.join(outdir, stnm + '-res.png') fig = plt.figure(dpi=160) for spec in res: plt.semilogx(spec.get_freq(), spec.data_mag, 'b-') plt.semilogx(spec_mean.get_freq(), spec_mean.data_mag, 'r-') plt.xlabel('frequency (Hz)') plt.ylabel('residual amplitude (obs - synth) in magnitude units') plt.title('residuals : ' + stnm + ', ' + str(len(res)) + ' records') fig.savefig(figurefile, bbox_inches='tight') plt.close() # writes the mean residuals (the stations corrections) with open(os.path.join(outdir, 'residual_mean.pickle'), 'wb') as fp: pickle.dump(residual_mean, fp)