def _build_weight(config, spec, specnoise): weight = spec.copy() if specnoise is not None: weight.data /= specnoise.data # save data to raw_data weight.data_raw = weight.data.copy() # The inversion is done in magnitude units, # so let's take log10 of weight weight.data = np.log10(weight.data) # Weight spectrum is smoothed once more _smooth_spectrum(weight, config.spectral_smooth_width_decades) weight.data /= np.max(weight.data) # slightly taper weight at low frequencies, to avoid overestimating # weight at low frequencies, in cases where noise is underestimated cosine_taper(weight.data, spec.stats.delta / 4, left_taper=True) # Make sure weight is positive weight.data[weight.data <= 0] = 0.001 else: msg = '{}: No available noise window: a uniform weight will be applied' msg = msg.format(weight.get_id()[0:-1]) logger.warning(msg) weight.data = np.ones(len(spec.data)) # interpolate to log-frequencies f = interp1d(weight.get_freq(), weight.data, fill_value='extrapolate') weight.data_log = f(weight.freq_log) weight.data_log /= np.max(weight.data_log) # Make sure weight is positive weight.data_log[weight.data_log <= 0] = 0.001 return weight
def _get_cut_times(config, tr): """Get trace cut times between P arrival and end of envelope coda.""" tr_env = tr.copy() # remove the mean... tr_env.detrend(type='constant') # ...and the linear trend... tr_env.detrend(type='linear') # ...filter freqmin = 1. freqmax = 20. nyquist = 1./(2. * tr.stats.delta) if freqmax >= nyquist: freqmax = nyquist * 0.999 msg = '%s: maximum frequency for bandpass filtering ' % tr.id msg += 'in local magnitude computation is larger than or equal ' msg += 'to Nyquist. Setting it to %s Hz' % freqmax logger.warning(msg) cosine_taper(tr_env.data, width=config.taper_halfwidth) tr_env.filter(type='bandpass', freqmin=freqmin, freqmax=freqmax) tr_env.data = envelope(tr_env.data) tr_env.data = smooth(tr_env.data, 100) # Skip traces which do not have arrivals try: p_arrival_time = tr.stats.arrivals['P'][1] except Exception: logger.warning('%s: Trace has no P arrival: skipping trace' % tr.id) raise RuntimeError t1 = p_arrival_time - config.win_length t2 = p_arrival_time + config.win_length tr_noise = tr_env.copy() tr_signal = tr_env.copy() tr_noise.trim(starttime=t1, endtime=p_arrival_time, pad=True, fill_value=0) tr_signal.trim(starttime=p_arrival_time, endtime=t2, pad=True, fill_value=0) ampmin = tr_noise.data.mean() ampmax = tr_signal.data.mean() if ampmax <= ampmin: logger.warning( '%s: Trace has too high noise before P arrival: ' 'skipping trace' % tr.id) raise RuntimeError trigger = trigger_onset(tr_env.data, ampmax, ampmin, max_len=9e99, max_len_delete=False)[0] t0 = p_arrival_time t1 = t0 + trigger[-1] * tr.stats.delta if t1 > tr.stats.endtime: t1 = tr.stats.endtime return t0, t1
def _process_trace(config, tr, t0, t1): """Convert to Wood-Anderson, filter, trim.""" tr_process = tr.copy() # Do a preliminary trim, in order to check if there is enough # data within the selected time window tr_process.trim(starttime=t0, endtime=t1, pad=True, fill_value=0) npts = len(tr_process.data) if npts == 0: logger.warning('%s: No data for the selected cut interval: ' 'skipping trace' % tr.id) raise RuntimeError nzeros = len(np.where(tr_process.data == 0)[0]) if nzeros > npts / 4: logger.warning('%s: Too many gaps for the selected cut ' 'interval: skipping trace' % tr.id) raise RuntimeError # If the check is ok, recover the full trace # (it will be cutted later on) tr_process = tr.copy() # remove the mean... tr_process.detrend(type='constant') # ...and the linear trend... tr_process.detrend(type='linear') freqmin = config.ml_bp_freqmin freqmax = config.ml_bp_freqmax # ...remove response... pre_filt = (freqmin, freqmin * 1.1, freqmax * 0.9, freqmax) remove_instr_response(tr_process, config.correct_instrumental_response, pre_filt) # ...filter tr_process.filter(type='bandpass', freqmin=freqmin, freqmax=freqmax) # Convert to Wood-Anderson # note: conversion to Wood-Anderson integrates the signal once if tr.stats.instrtype == 'acc': WA_double_int = deepcopy(WOODANDERSON) # we remove a zero to add an integration step WA_double_int['zeros'].pop() tr_process.simulate(paz_simulate=WA_double_int) else: tr_process.simulate(paz_simulate=WOODANDERSON) # trim... tr_process.trim(starttime=t0, endtime=t1, pad=True, fill_value=0) # ...and taper cosine_taper(tr_process.data, width=config.taper_halfwidth) return tr_process
def _cut_signal_noise(config, trace): trace_signal = trace.copy() trace_noise = trace.copy() # Integrate in time domain, if required. # (otherwhise frequency-domain integration is # performed later) if config.time_domain_int: _time_integrate(config, trace_signal) _time_integrate(config, trace_noise) # trim... t1 = trace.stats.arrivals['S1'][1] t2 = trace.stats.arrivals['S2'][1] trace_signal.trim(starttime=t1, endtime=t2, pad=True, fill_value=0) # Noise time window for weighting function: noise_t1 = trace.stats.arrivals['N1'][1] noise_t2 = trace.stats.arrivals['N2'][1] trace_noise.trim(starttime=noise_t1, endtime=noise_t2, pad=True, fill_value=0) # ...taper... cosine_taper(trace_signal.data, width=config.taper_halfwidth) cosine_taper(trace_noise.data, width=config.taper_halfwidth) if config.spectral_win_length is not None: # ...and zero pad to spectral_win_length trace_signal.trim(starttime=t1, endtime=t1 + config.spectral_win_length, pad=True, fill_value=0) trace_noise.trim(starttime=noise_t1, endtime=noise_t1 + config.spectral_win_length, pad=True, fill_value=0) # Be sure that both traces have same length: if len(trace_signal) != len(trace_noise): npts = min(len(trace_signal), len(trace_noise)) trace_signal.data = trace_signal.data[:npts] trace_noise.data = trace_noise.data[:npts] return trace_signal, trace_noise