def KonnoSmoothSpec(tr, bandwidth, plot): """ Smooth spectrum of a given trace with the Konno_omachi method * inputs : - tr: type: trace; seimic trace - bandwidth : type int; number of point over wich the spectrum is smoothed, after testing 110 seems good - plot : type Bool; if True smooth, and un smooth spectrum are plotted * outputs : - fr : type array; frequencies of the spectrum - SmoothSpectre : type array; Smooth spectrum * exemple : """ # 0. Spectre of the trace ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ t = np.arange(0, tr.stats.npts / tr.stats.sampling_rate, tr.stats.delta) trFreq= np.fft.fft(tr.data, n= 10000) fs = tr.stats.sampling_rate N = len(trFreq) #half spectre since fft is symmetric over is center xF = trFreq[0:N/2] # #frequencies fr = np.linspace(0,fs/2,N/2) #1. smoothing Spectrum ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #CAREFULL SMOTTHING GONNA CHANGE A LOT THE SPECTRUM BUT DOING THE SPECTRUML RATIO IS OK! SmoothSpectre = konno_ohmachi_smoothing(np.abs(xF), fr, bandwidth=bandwidth, count=1, enforce_no_matrix=False, normalize=False) if plot== True : plt.figure() plt.plot(fr,np.abs(xF), 'r',label = 'non Smooth') plt.plot(fr,SmoothSpectre , 'k', label='Smooth') plt.ylabel("Amplitude") plt.xlabel("Frequency Hz") plt.legend() return fr, SmoothSpectre
def test_konno_ohmachi_smoothing(self): """ Tests the actual smoothing matrix. """ # Create some random spectra. np.random.seed(1111) spectra = np.random.ranf((5, 200)) * 50 frequencies = np.logspace(-3.0, 2.0, 200) spectra = np.require(spectra, dtype=np.float32) frequencies = np.require(frequencies, dtype=np.float64) # Wrong dtype raises. self.assertRaises(ValueError, konno_ohmachi_smoothing, spectra, np.arange(200)) # Differing float dtypes raise a warning. with warnings.catch_warnings(record=True): warnings.simplefilter('error', UserWarning) self.assertRaises(UserWarning, konno_ohmachi_smoothing, spectra, frequencies) # Correct the dtype. frequencies = np.require(frequencies, dtype=np.float32) # The first one uses the matrix method, the second one the non matrix # method. smoothed_1 = konno_ohmachi_smoothing(spectra, frequencies, count=3) smoothed_2 = konno_ohmachi_smoothing(spectra, frequencies, count=3, max_memory_usage=0) # XXX: Why are the numerical inaccuracies quite large? np.testing.assert_almost_equal(smoothed_1, smoothed_2, 3) # Test using a pre-computed smoothing matrix smoothing_matrix = calculate_smoothing_matrix(frequencies) smoothed_3 = apply_smoothing_matrix(spectra, smoothing_matrix, count=3) np.testing.assert_almost_equal(smoothed_1, smoothed_3, 3) # Test the non-matrix mode for single spectra. smoothed_4 = konno_ohmachi_smoothing( np.require(spectra[0], dtype=np.float64), np.require(frequencies, dtype=np.float64)) smoothed_5 = konno_ohmachi_smoothing(np.require(spectra[0], dtype=np.float64), np.require(frequencies, dtype=np.float64), normalize=True) # The normalized and not normalized should not be the same. That the # normalizing works has been tested before. self.assertFalse(np.all(smoothed_4 == smoothed_5)) # Input dtype should be output dtype. self.assertEqual(smoothed_4.dtype, np.float64)
def test_konno_ohmachi_smoothing(self): """ Tests the actual smoothing matrix. """ # Create some random spectra. np.random.seed(1111) spectra = np.random.ranf((5, 200)) * 50 frequencies = np.logspace(-3.0, 2.0, 200) spectra = np.require(spectra, dtype=np.float32) frequencies = np.require(frequencies, dtype=np.float64) # Wrong dtype raises. self.assertRaises(ValueError, konno_ohmachi_smoothing, spectra, np.arange(200)) # Differing float dtypes raise a warning. with warnings.catch_warnings(record=True): warnings.simplefilter('error', UserWarning) self.assertRaises(UserWarning, konno_ohmachi_smoothing, spectra, frequencies) # Correct the dtype. frequencies = np.require(frequencies, dtype=np.float32) # The first one uses the matrix method, the second one the non matrix # method. smoothed_1 = konno_ohmachi_smoothing(spectra, frequencies, count=3) smoothed_2 = konno_ohmachi_smoothing(spectra, frequencies, count=3, max_memory_usage=0) # XXX: Why are the numerical inaccuracies quite large? np.testing.assert_almost_equal(smoothed_1, smoothed_2, 3) # Test using a pre-computed smoothing matrix smoothing_matrix = calculate_smoothing_matrix(frequencies) smoothed_3 = apply_smoothing_matrix(spectra, smoothing_matrix, count=3) np.testing.assert_almost_equal(smoothed_1, smoothed_3, 3) # Test the non-matrix mode for single spectra. smoothed_4 = konno_ohmachi_smoothing( np.require(spectra[0], dtype=np.float64), np.require(frequencies, dtype=np.float64)) smoothed_5 = konno_ohmachi_smoothing( np.require(spectra[0], dtype=np.float64), np.require(frequencies, dtype=np.float64), normalize=True) # The normalized and not normalized should not be the same. That the # normalizing works has been tested before. self.assertFalse(np.all(smoothed_4 == smoothed_5)) # Input dtype should be output dtype. self.assertEqual(smoothed_4.dtype, np.float64)
def fft_smooth(trace, nfft): """ Pads a trace to the nearest upper power of 2, takes the FFT, and smooths the amplitude spectra following the algorithm of Konno and Ohmachi. Args: trace (obspy.core.trace.Trace): Trace of strong motion data. nfft (int): Number of data points for the fourier transform. Returns: numpy.ndarray: Smoothed amplitude data and frequencies. """ # Compute the FFT, normalizing by the number of data points spec = abs(np.fft.rfft(trace.data, n=nfft)) / nfft # Get the frequencies associated with the FFT freqs = np.fft.rfftfreq(nfft, 1 / trace.stats.sampling_rate) # Konno Omachi Smoothing using 20 for bandwidth parameter spec_smooth = konno_ohmachi_smoothing(spec.astype(float), freqs, 20) return spec_smooth, freqs
def interp_smooth(path, singlecomp=False, ext=None, maxF=25, dt=1 / 100): """ """ if not singlecomp: # take geometric mean of everything exts = [".EW2.gz", ".NS2.gz", ".EW1.gz", ".NS1.gz"] wfms = [readKiknet(path + f) for f in exts] [calcFAS(wf) for wf in wfms] s_b = (wfms[0]['FAS'] * wfms[1]['FAS'])**0.5 / (wfms[2]['FAS'] * wfms[3]['FAS'])**0.5 else: #calc for only the specified component exts = [ext + "2.gz", ext + "1.gz"] wfms = [readKiknet(path + f) for f in exts] [calcFAS(wf) for wf in wfms] s_b = wfms[0]['FAS'] / wfms[1]['FAS'] freqs = np.linspace(0 + dt, maxF + dt, maxF / dt)[9:] #start counting from 0.1Hz s_b = np.interp(freqs, wfms[0]['FASfreqs'], s_b) s_b = konno_ohmachi_smoothing(s_b, freqs, normalize=True) return (s_b, freqs)
def rel_calib_stack(st1, st2, calib_file, window_len, overlap_frac=0.5, smooth=0, save_data=True): """ Method for relative calibration of sensors using a sensor with known transfer function :param st1: Stream or Trace object, (known) :param st2: Stream or Trace object, (unknown) :type calib_file: str :param calib_file: file name of calibration file containing the PAZ of the known instrument in GSE2 standard. :type window_len: float :param window_len: length of sliding window in seconds :type overlap_frac: float :param overlap_frac: fraction of overlap, defaults to fifty percent (0.5) :type smooth: float :param smooth: variable that defines if the Konno-Ohmachi taper is used or not. default = 0 -> no taper generally used in geopsy: smooth = 40 :type save_data: bool :param save_data: Whether or not to save the result to a file. If True, two output files will be created: * The new response in station_name.window_length.resp * The ref response in station_name.refResp Defaults to True :returns: frequency, amplitude and phase spectrum implemented after rel_calib_stack.c by M.Ohrnberger and J.Wassermann. """ # transform given trace objects to streams if isinstance(st1, Trace): st1 = Stream([st1]) if isinstance(st2, Trace): st2 = Stream([st2]) # check if sampling rate and trace length is the same if st1[0].stats.npts != st2[0].stats.npts: msg = "Traces don't have the same length!" raise ValueError(msg) elif st1[0].stats.sampling_rate != st2[0].stats.sampling_rate: msg = "Traces don't have the same sampling rate!" raise ValueError(msg) else: ndat1 = st1[0].stats.npts sampfreq = st1[0].stats.sampling_rate # read waveforms tr1 = st1[0].data.astype(np.float64) tr2 = st2[0].data.astype(np.float64) # get window length, nfft and frequency step ndat = int(window_len * sampfreq) nfft = next_pow_2(ndat) # read calib file and calculate response function gg, _freq = _calc_resp(calib_file, nfft, sampfreq) # calculate number of windows and overlap nwin = int(np.floor((ndat1 - nfft) / (nfft / 2)) + 1) noverlap = nfft * overlap_frac auto, _freq, _t = \ spectral_helper(tr1, tr1, NFFT=nfft, Fs=sampfreq, noverlap=noverlap) cross, freq, _t = \ spectral_helper(tr2, tr1, NFFT=nfft, Fs=sampfreq, noverlap=noverlap) res = (cross / auto).sum(axis=1) * gg # The first item might be zero. Problems with phase calculations. res = res[1:] freq = freq[1:] gg = gg[1:] res /= nwin # apply Konno-Ohmachi smoothing taper if chosen if smooth > 0: # Write in one matrix for performance reasons. spectra = np.empty((2, len(res.real))) spectra[0] = res.real spectra[1] = res.imag new_spectra = \ konno_ohmachi_smoothing(spectra, freq, bandwidth=smooth, count=1, max_memory_usage=1024, normalize=True) res.real = new_spectra[0] res.imag = new_spectra[1] amp = np.abs(res) # include phase unwrapping phase = np.unwrap(np.angle(res)) # + 2.0 * np.pi ra = np.abs(gg) rpha = np.unwrap(np.angle(gg)) if save_data: trans_new = (st2[0].stats.station + "." + st2[0].stats.channel + "." + str(window_len) + ".resp") trans_ref = st1[0].stats.station + ".refResp" # Create empty array for easy saving temp = np.empty((len(freq), 3)) temp[:, 0] = freq temp[:, 1] = amp temp[:, 2] = phase np.savetxt(trans_new, temp, fmt=native_str('%.10f')) temp[:, 1] = ra temp[:, 2] = rpha np.savetxt(trans_ref, temp, fmt=native_str('%.10f')) return freq, amp, phase
def rel_calib_stack(st1, st2, calib_file, window_len, overlap_frac=0.5, smooth=0, save_data=True): """ Method for relative calibration of sensors using a sensor with known transfer function :param st1: Stream or Trace object, (known) :param st2: Stream or Trace object, (unknown) :type calib_file: str :param calib_file: file name of calibration file containing the PAZ of the known instrument in GSE2 standard. :type window_len: float :param window_len: length of sliding window in seconds :type overlap_frac: float :param overlap_frac: fraction of overlap, defaults to fifty percent (0.5) :type smooth: float :param smooth: variable that defines if the Konno-Ohmachi taper is used or not. default = 0 -> no taper generally used in geopsy: smooth = 40 :type save_data: bool :param save_data: Whether or not to save the result to a file. If True, two output files will be created: * The new response in station_name.window_length.resp * The ref response in station_name.refResp Defaults to True :returns: frequency, amplitude and phase spectrum implemented after rel_calib_stack.c by M.Ohrnberger and J.Wassermann. """ # transform given trace objects to streams if isinstance(st1, Trace): st1 = Stream([st1]) if isinstance(st2, Trace): st2 = Stream([st2]) # check if sampling rate and trace length is the same if st1[0].stats.npts != st2[0].stats.npts: msg = "Traces don't have the same length!" raise ValueError(msg) elif st1[0].stats.sampling_rate != st2[0].stats.sampling_rate: msg = "Traces don't have the same sampling rate!" raise ValueError(msg) else: ndat1 = st1[0].stats.npts sampfreq = st1[0].stats.sampling_rate # read waveforms tr1 = st1[0].data.astype(np.float64) tr2 = st2[0].data.astype(np.float64) # get window length, nfft and frequency step ndat = int(window_len * sampfreq) nfft = next_pow_2(ndat) # read calib file and calculate response function gg, _freq = _calc_resp(calib_file, nfft, sampfreq) # calculate number of windows and overlap nwin = int(np.floor((ndat1 - nfft) / (nfft / 2)) + 1) noverlap = nfft * overlap_frac auto, _freq, _t = spectral_helper(tr1, tr1, NFFT=nfft, Fs=sampfreq, noverlap=noverlap) cross, freq, _t = spectral_helper(tr2, tr1, NFFT=nfft, Fs=sampfreq, noverlap=noverlap) res = (cross / auto).sum(axis=1) * gg # The first item might be zero. Problems with phase calculations. res = res[1:] freq = freq[1:] gg = gg[1:] res /= nwin # apply Konno-Ohmachi smoothing taper if chosen if smooth > 0: # Write in one matrix for performance reasons. spectra = np.empty((2, len(res.real))) spectra[0] = res.real spectra[1] = res.imag new_spectra = konno_ohmachi_smoothing( spectra, freq, bandwidth=smooth, count=1, max_memory_usage=1024, normalize=True ) res.real = new_spectra[0] res.imag = new_spectra[1] amp = np.abs(res) # include phase unwrapping phase = np.unwrap(np.angle(res)) # + 2.0 * np.pi ra = np.abs(gg) rpha = np.unwrap(np.angle(gg)) if save_data: trans_new = st2[0].stats.station + "." + st2[0].stats.channel + "." + str(window_len) + ".resp" trans_ref = st1[0].stats.station + ".refResp" # Create empty array for easy saving temp = np.empty((len(freq), 3)) temp[:, 0] = freq temp[:, 1] = amp temp[:, 2] = phase np.savetxt(trans_new, temp, fmt=native_str("%.10f")) temp[:, 1] = ra temp[:, 2] = rpha np.savetxt(trans_ref, temp, fmt=native_str("%.10f")) return freq, amp, phase