def test_SVD_mag(self): """Test the SVD magnitude calculator.""" # Do the set-up testing_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'test_data', 'similar_events_processed') stream_files = glob.glob(os.path.join(testing_path, '*DFDPC*')) stream_list = [read(stream_file) for stream_file in stream_files] event_list = [] for i, stream in enumerate(stream_list): st_list = [] for tr in stream: if (tr.stats.station, tr.stats.channel) not in\ [('WHAT2', 'SH1'), ('WV04', 'SHZ'), ('GCSZ', 'EHZ')]: stream.remove(tr) continue st_list.append(i) event_list.append(st_list) event_list = np.asarray(event_list).T.tolist() SVectors, SValues, Uvectors, stachans = svd(stream_list=stream_list) M, events_out = svd_moments(u=Uvectors, s=SValues, v=SVectors, stachans=stachans, event_list=event_list) self.assertEqual(len(M), len(stream_list)) self.assertEqual(len(events_out), len(stream_list))
def test_svd_plot(self): from eqcorrscan.utils.clustering import svd, svd_to_stream uvec, sval, svec, stachans = svd(stream_list=self.stream_list) svstreams = svd_to_stream(uvectors=uvec, stachans=stachans, k=3, sampling_rate=100) fig = svd_plot(svstreams=svstreams, svalues=sval, stachans=stachans, show=False, return_figure=True) return fig[0]
def test_svd_to_stream(self): """Test the conversion of SVD to stream.""" samp_rate = 100 testing_path = os.path.join( self.testing_path, 'similar_events_processed') stream_files = glob.glob(os.path.join(testing_path, '*')) stream_list = [read(stream_file) for stream_file in stream_files] SVectors, SValues, Uvectors, stachans = svd(stream_list=stream_list) svstreams = svd_to_stream(uvectors=SVectors, stachans=stachans, k=4, sampling_rate=samp_rate) self.assertEqual(len(svstreams), 4)
def test_svd(self): """Test the svd method.""" testing_path = os.path.join(self.testing_path, 'similar_events_processed') stream_files = glob.glob(os.path.join(testing_path, '*')) stream_list = [read(stream_file) for stream_file in stream_files] UVectors, SValues, SVectors, stachans = svd(stream_list=stream_list) self.assertEqual(len(SVectors), len(stachans)) self.assertEqual(len(SValues), len(stachans)) self.assertEqual(len(UVectors), len(stachans)) for SVec in SVectors: self.assertEqual(len(SVec), len(stream_list))
def test_svd(self): """Test the svd method.""" testing_path = os.path.join(self.testing_path, 'similar_events') stream_files = glob.glob(os.path.join(testing_path, '*')) stream_list = [read(stream_file) for stream_file in stream_files] for stream in stream_list: for tr in stream: if tr.stats.station not in ['WHAT2', 'WV04', 'GCSZ']: stream.remove(tr) continue tr.detrend('simple') tr.filter('bandpass', freqmin=5.0, freqmax=15.0) tr.trim(tr.stats.starttime + 40, tr.stats.endtime - 45) UVectors, SValues, SVectors, stachans = svd(stream_list=stream_list) self.assertEqual(len(SVectors), len(stachans)) self.assertEqual(len(SValues), len(stachans)) self.assertEqual(len(UVectors), len(stachans)) for SVec in SVectors: self.assertEqual(len(SVec), len(stream_list)) with warnings.catch_warnings(record=True) as w: SVD(stream_list=stream_list) self.assertEqual(len(w), 1) self.assertTrue('Depreciated' in str(w[0].message))
def SVD_sim(sp, lowcut, highcut, samp_rate, amp_range=np.arange(-10, 10, 0.01)): """ Generate basis vectors of a set of simulated seismograms. Inputs should have a range of S-P amplitude ratios, in theory to simulate \ a range of focal mechanisms. :type sp: int :param sp: S-P time in seconds - will be converted to samples according \ to samp_rate. :type lowcut: float :param lowcut: Low-cut for bandpass filter in Hz :type highcut: float :param highcut: High-cut for bandpass filter in Hz :type samp_rate: float :param samp_rate: Sampling rate in Hz :type amp_range: numpy.ndarray :param amp_range: Amplitude ratio range to generate synthetics for. :returns: set of output basis vectors :rtype: :class:`numpy.ndarray` """ # Convert SP to samples sp = int(sp * samp_rate) # Scan through a range of amplitude ratios synthetics = [Stream(Trace(seis_sim(sp, a))) for a in amp_range] for st in synthetics: for tr in st: tr.stats.station = 'SYNTH' tr.stats.channel = 'SH1' tr.stats.sampling_rate = samp_rate tr.filter('bandpass', freqmin=lowcut, freqmax=highcut) # We have a list of obspy Trace objects, we can pass this to EQcorrscan's # SVD functions U, s, V, stachans = clustering.svd(synthetics) return U, s, V, stachans
def test_svd_to_stream(self): """Test the conversion of SVD to stream.""" samp_rate = 100 testing_path = os.path.join(self.testing_path, 'similar_events') stream_files = glob.glob(os.path.join(testing_path, '*')) stream_list = [read(stream_file) for stream_file in stream_files] for stream in stream_list: for tr in stream: if tr.stats.station not in ['WHAT2', 'WV04', 'GCSZ']: stream.remove(tr) continue tr.detrend('simple') tr.filter('bandpass', freqmin=5.0, freqmax=15.0) tr.resample(sampling_rate=samp_rate) tr.trim(tr.stats.starttime + 40, tr.stats.endtime - 45) SVectors, SValues, Uvectors, stachans = svd(stream_list=stream_list) svstreams = svd_to_stream(uvectors=SVectors, stachans=stachans, k=4, sampling_rate=samp_rate) self.assertEqual(len(svstreams), 4) with warnings.catch_warnings(record=True) as w: SVD_2_stream(uvectors=SVectors, stachans=stachans, k=4, sampling_rate=samp_rate) self.assertEqual(len(w), 1) self.assertTrue('Depreciated' in str(w[0].message))
def construct(self, streams, lowcut, highcut, filt_order, sampling_rate, multiplex, name, align, shift_len=0, reject=0.3, no_missed=True, plot=False): """ Construct a subspace detector from a list of streams, full rank. Subspace detector will be full-rank, further functions can be used \ to select the desired dimensions. :type streams: list :param streams: List of :class:`obspy.core.stream.Stream` to be used to generate the subspace detector. These should be pre-clustered and aligned. :type lowcut: float :param lowcut: Lowcut in Hz, can be None to not apply filter :type highcut: float :param highcut: Highcut in Hz, can be None to not apply filter :type filt_order: int :param filt_order: Number of corners for filter. :type sampling_rate: float :param sampling_rate: Desired sampling rate in Hz :type multiplex: bool :param multiplex: Whether to multiplex the data or not. Data are multiplexed according to the method of Harris, see the multi function for details. :type name: str :param name: Name of the detector, used for book-keeping. :type align: bool :param align: Whether to align the data or not - needs to be done at some point :type shift_len: float :param shift_len: Maximum shift allowed for alignment in seconds. :type reject: float :param reject: Minimum correlation to include traces - only used if align=True. :type no_missed: bool :param no_missed: Reject streams with missed traces, defaults to True. A missing trace from lots of events will reduce the quality of the subspace detector if multiplexed. Only used when multi is set to True. :type plot: bool :param plot: Whether to plot the alignment stage or not. .. note:: The detector will be normalized such that the data, before computing the singular-value decomposition, will have unit energy. e.g. We divide the amplitudes of the data by the L1 norm of the data. .. warning:: EQcorrscan's alignment will attempt to align over the whole data window given. For long (more than 2s) chunks of data this can give poor results and you might be better off using the :func:`eqcorrscan.utils.stacking.align_traces` function externally, focusing on a smaller window of data. To do this you would align the data prior to running construct. """ self.lowcut = lowcut self.highcut = highcut self.filt_order = filt_order self.sampling_rate = sampling_rate self.name = name self.multiplex = multiplex # Pre-process data p_streams, stachans = _subspace_process(streams=copy.deepcopy(streams), lowcut=lowcut, highcut=highcut, filt_order=filt_order, sampling_rate=sampling_rate, multiplex=multiplex, align=align, shift_len=shift_len, reject=reject, plot=plot, no_missed=no_missed) # Compute the SVD, use the cluster.SVD function u, sigma, v, svd_stachans = svd(stream_list=p_streams, full=True) self.stachans = stachans # self.delays = delays self.u = u self.v = v self.sigma = sigma self.data = copy.deepcopy(u) # Set the data matrix to be full rank U. self.dimension = np.inf return self
def relative_mag_calc(cat, template_dict, n_SVs=4, plot=False, debug=1): """ Now we're going to loop through templates, filter out poorly correlated waveforms, compute SVD and relative magnitudes using EQcorrscan functions, then map relative mags to real magnitudes using template local magnitudes """ from eqcorrscan.utils.plotting import multi_trace_plot from obspy.core.event import ResourceIdentifier import matplotlib.pyplot as plt # Assign shifts for detections to template dictionary new_cat = Catalog() # Random sample of template ids for plotting samp_ids = [ id for i, id in enumerate(template_dict.keys()) if i in np.random.choice( range(len(template_dict)), len(template_dict) // 20, replace=False) ] for tid, det_dict in template_dict.iteritems(): # Perform some checks on the dictionary first if len(det_dict) <= 1: print('%s has <= one detection. No magnitude will be calculated.' % str(tid)) continue else: print('Working on detections for template: %s' % str(tid)) if 'self' not in [ str(key).split('/')[-1].split('_')[-1] for key in det_dict.keys() ]: print( 'Self detection not located in catalog. Moving to next template.' ) continue inds = det_dict['aligned_inds'] stream_list = det_dict['aligned'] # Do SVD if len(stream_list) <= n_SVs: warnings.warn( 'Fewer streams then nSVs passed to SVD. Moving to next template' ) continue svd_dict = clustering.svd(stream_list, full=True) if plot: if tid in samp_ids: for stachan in svd_dict: if 'svectors' in svd_dict[stachan]: if len(svd_dict[stachan]['svectors']) > 0: if len(svd_dict[stachan]['events']) < 5: # we will not plot stachans with only one event continue fig, axes = plt.subplots(len( svd_dict[stachan]['events']), 1, sharex=True, figsize=(14, 24), squeeze=False) first_SV = svd_dict[stachan]['svectors'][0] first_SVal = svd_dict[stachan]['svalues'][0] for i, ev_ind in enumerate( svd_dict[stachan]['events']): data_tr = stream_list[ev_ind].select( station=stachan.split('.')[0], channel=stachan.split('.')[1])[0] samp_rate = data_tr.stats.sampling_rate SV_y = first_SV * first_SVal SV_x = np.arange(len(SV_y)) SV_x = SV_x / samp_rate dat_y = data_tr.data U_wt = np.matrix( copy.deepcopy( svd_dict[stachan]['uvectors'])) svd_wts = np.array( U_wt[:, 0]).reshape(-1).tolist() axes[i, 0].plot(SV_x, SV_y * svd_wts[i], color='r') axes[i, 0].plot(SV_x, dat_y, color='k') axes[i, 0].text(0.9, 0.15, str(svd_wts[i]), bbox=dict(facecolor='white', alpha=0.95), transform=axes[i, 0].transAxes) axes[i, 0].text( 0.7, 0.85, data_tr.stats.starttime.datetime. strftime('%Y/%m/%d %H:%M:%S'), bbox=dict(facecolor='white', alpha=0.95), transform=axes[i, 0].transAxes) fig.suptitle('%s\nChannel: %s First SVal: %f' % (str(tid), stachan, first_SVal)) fig.show() # Feed output vectors and values to mag_calc.SVD_moments M, events_out = mag_calc.SVD_moments(svd_dict, n_SVs, debug=debug) # Find rel_amp of self detection try: rel_amp_t = [ M[i] for i, cat_ind in enumerate(inds) if i in events_out and cat_ind == det_dict['temp_ind'] ][0] except: warnings.warn( 'Relative amp not calculated for template in this case....investigate' ) continue # Convert relative values to template values Mls = [ np.log10(rel_amp_i / rel_amp_t) + det_dict['temp_mag'] for rel_amp_i in M ] if len(Mls) != len(events_out): warnings.warn('Not same number of local mags and out events') for i, cat_ind in enumerate(inds): if i in events_out: Mls_ind = [ k for k, ev in enumerate(events_out) if ev == i ][0] if cat_ind == det_dict['temp_ind']: event = cat[cat_ind].copy() event.magnitudes.append( Magnitude(mag=det_dict['temp_mag'], creation_info=(CreationInfo( author='SeisComp')))) new_cat.append(event) else: event = cat[cat_ind].copy() event.magnitudes.append( Magnitude( mag=Mls[Mls_ind], creation_info=(CreationInfo( author= 'eqcorrscan.utils.mag_calc.SVD_moment')))) new_cat.append(event) return new_cat
def party_relative_mags(party, self_files, shift_len, align_len, svd_len, reject, sac_dir, min_amps, calibrate=False, method='PCA'): """ Calculate the relative moments for detections in a Family using mag_calc.svd_moments() :param party: Party of detections :param shift_len: Maximum shift length used in waveform alignment :param align_len: Length of waveform used for correlation in alignment :param svd_len: Length of waveform used in relative amplitude calc :param reject: Min cc threshold for accepted measurement :param sac_dir: Root directory of waveforms :param min_amps: Minimum number of relative measurements per pair :param calibrate: Flag for calibration to a priori Ml's :param method: 'PCA' or 'LSQR' :return: """ # First read-in self detection names selfs = [] for self_file in self_files: with open(self_file, 'r') as f: rdr = csv.reader(f) for row in rdr: selfs.append(str(row[0])) for fam in party.families: print('Starting work on family %s' % fam.template.name) if len(fam) == 1: print('Only self-detection. Moving on.') continue temp = fam.template prepick = temp.prepick events = [det.event for det in fam.detections] # Here we'll read in the waveforms and trim from stefan's directory # of SAC files so as not to duplicate data ev_dirs = ['%s%s' % (sac_dir, str(ev.resource_id).split('/')[-1]) for ev in events] streams = [] if len([i for i, ev_dir in enumerate(ev_dirs) if ev_dir.split('/')[-1] in selfs]) == 0: print('Family %s has no self detection. Investigate' % fam.template.name) continue self_ind = [i for i, ev_dir in enumerate(ev_dirs) if ev_dir.split('/')[-1] in selfs][0] # Read in Z components of events which we wrote for stefan # Many of these ev_dirs will not exist! for i, ev_dir in enumerate(ev_dirs): raw_st = Stream() print('Reading %s' % ev_dir) for wav_file in glob('%s/*Z.sac' % ev_dir): print('...file %s' % wav_file) raw_tr = read(wav_file)[0] start = raw_tr.stats.starttime + raw_tr.stats.sac['a'] - 3. end = start + 10 raw_tr.trim(starttime=start, endtime=end) raw_st.traces.append(raw_tr) streams.append(raw_st) print('Moved self detection to top of list') # Move the self detection to the first element streams.insert(0, streams.pop(self_ind)) print('Template Stream: %s' % str(streams[0])) if len(streams[0]) == 0: print('Template %s waveforms did not get written to SAC.' % temp.name) continue # Front/back clip hardcoded relative to wavs starting 3 s before pick front_clip = 3.0 - shift_len - 0.05 - prepick back_clip = front_clip + align_len + (2 * shift_len) + 0.05 wrk_streams = [] # For aligning # Process streams then copy to both ccc_streams and svd_streams bad_streams = [] for i, st in enumerate(list(streams)): try: shortproc(st=streams[i], lowcut=temp.lowcut, highcut=temp.highcut, filt_order=temp.filt_order, samp_rate=temp.samp_rate) wrk_streams.append(st.copy()) except ValueError as e: print('ValueError reads:') print(str(e)) print('Attempting to remove bad trace at {}'.format( str(e).split(' ')[-1])) bad_tr = str(e).split(' ')[-1][:-1] # Eliminate trailing "'" print('Sta and chan names: {}'.format(bad_tr.split('.'))) try: tr = streams[i].select(station=bad_tr.split('.')[0], channel=bad_tr.split('.')[1])[0] streams[i].traces.remove(tr) shortproc(st=streams[i], lowcut=temp.lowcut, highcut=temp.highcut, filt_order=temp.filt_order, samp_rate=temp.samp_rate) wrk_streams.append(st.copy()) except IndexError as e: print(str(e)) print('Funkyness. Removing entire stream') bad_streams.append(st) if len(bad_streams) > 0: for bst in bad_streams: streams.remove(bst) svd_streams = copy.deepcopy(streams) # For svd ccc_streams = copy.deepcopy(streams) # work out cccoh for each event with template cccohs = cc_coh_dets(streams=ccc_streams, shift=shift_len, length=svd_len, wav_prepick=3., corr_prepick=0.05) for st in wrk_streams: for tr in st: tr.trim(starttime=tr.stats.starttime + front_clip, endtime=tr.stats.starttime + back_clip) st_chans = list(set([(tr.stats.station, tr.stats.channel) for st in wrk_streams for tr in st])) st_chans.sort() # Align streams with just P arrivals, then use longer st for svd print('Now aligning svd_streams') shift_inds = int(shift_len * fam.template.samp_rate) for st_chan in st_chans: trs = [] for i, st in enumerate(wrk_streams): if len(st.select(station=st_chan[0], channel=st_chan[-1])) > 0: trs.append((i, st.select(station=st_chan[0], channel=st_chan[-1])[0])) inds, traces = zip(*trs) shifts, ccs = stacking.align_traces(trace_list=list(traces), shift_len=shift_inds, positive=True, master=traces[0].copy()) # We now have shifts based on P correlation, shift and trim # larger wavs for svd for j, shift in enumerate(shifts): st = svd_streams[inds[j]] if ccs[j] < reject: svd_streams[inds[j]].remove(st.select( station=st_chan[0], channel=st_chan[-1])[0]) print('Removing trace due to low cc value: %s' % ccs[j]) continue strt_tr = st.select( station=st_chan[0], channel=st_chan[-1])[0].stats.starttime strt_tr += (3.0 - prepick - shift) st.select(station=st_chan[0], channel=st_chan[-1])[0].trim(strt_tr,strt_tr + svd_len) if method == 'LSQR': print('Using least-squares method') event_list = [] for stachan in st_chans: st_list = [] for i, st in enumerate(svd_streams): if len(st.select(station=stachan[0], channel=stachan[-1])) > 0: st_list.append(i) event_list.append(st_list) # event_list = np.asarray(event_list).tolist() u, sigma, v, sta_chans = svd(stream_list=svd_streams, full=True) try: M, events_out = svd_moments(u, sigma, v, sta_chans, event_list) except IOError as e: print('Family %s raised error %s' % (fam.template.name, e)) continue elif method == 'PCA': print('Using principal component method') # Now loop over all detections and do svd for each matching # chan with temp events_out = [] template = svd_streams[0] M = [] for i, st in enumerate(svd_streams): if len(st) == 0: print('Event not located, skipping') continue ev_r_amps = [] # For each pair of template:detection (including temp:temp) for tr in template: if len(st.select(station=tr.stats.station, channel=tr.stats.channel)) > 0: det_tr = st.select(station=tr.stats.station, channel=tr.stats.channel)[0] # Convoluted way of getting two 'vert' vectors data_mat = np.vstack((tr.data, det_tr.data)).T U, sig, Vt = scipy.linalg.svd(data_mat, full_matrices=True) # Vt is 2x2 for two events # Per Shelly et al., 2016 eq. 4 ev_r_amps.append(Vt[0][1] / Vt[0][0]) if len(ev_r_amps) < min_amps: print('Fewer than 4 amplitude picks, skipping.') continue M.append(np.median(ev_r_amps)) events_out.append(i) # If we have a Mag for template, calibrate moments if calibrate and len(fam.template.event.magnitudes) > 0: # Convert the template magnitude to seismic moment temp_mag = fam.template.event.magnitudes[-1].mag temp_mo = local_to_moment(temp_mag) # Extrapolate from the template moment - relative moment relationship to # Get the moment for relative moment = 1.0 norm_mo = temp_mo / M[0] # Template is the last event in the list # Now these are weights which we can multiple the moments by moments = np.multiply(M, norm_mo) # Now convert to Mw Mw = [2.0 / 3.0 * (np.log10(m) - 9.0) for m in moments] Mw2, evs2 = remove_outliers(Mw, events_out) # Convert to local Ml = [0.88 * m + 0.73 for m in Mw2] #Normalize moments to template mag # Add calibrated mags to detection events for i, eind in enumerate(evs2): fam.detections[eind-1].event.magnitudes = [ Magnitude(mag=Mw2[i], magnitude_type='Mw')] fam.detections[eind-1].event.comments.append( Comment(text=str(cccohs[eind-1]))) fam.detections[eind-1].event.magnitudes.append( Magnitude(mag=Ml[i], magnitude_type='ML')) fam.catalog = Catalog(events=[det.event for det in fam.detections]) return party, cccohs
def party_relative_mags(party, self_files, shift_len, align_len, svd_len, reject, wav_dir, min_amps, m, c, calibrate=False, method='PCA', plot_svd=False): """ Calculate the relative moments for detections in a Family using mag_calc.svd_moments() :param party: Party of detections :param self_files: List of self-detection wav files (in order of families) :param shift_len: Maximum shift length used in waveform alignment :param align_len: Length of waveform used for correlation in alignment :param svd_len: Length of waveform used in relative amplitude calc :param reject: Min cc threshold for accepted measurement :param wav_dir: Root directory of waveforms :param min_amps: Minimum number of relative measurements per pair :param m: m in Mw = (m * ML) + c regression between Ml and Mw :param c: c in Mw = (m * ML) + c regression between Ml and Mw :param calibrate: Flag for calibration to a priori Ml's :param method: 'PCA' or 'LSQR' :param plot_svd: Bool to plot results of svd relative amplitude calcs :return: """ pty = party.copy() # sort self files and parties by template name pty.families.sort(key=lambda x: x.template.name) self_files.sort() ev_files = glob('{}/*'.format(wav_dir)) ev_files.sort() ev_files = {os.path.basename(f).rstrip('.ms'): f for f in ev_files} for i, fam in enumerate(pty.families): temp_wav = read(self_files[i]) print('Starting work on family %s' % fam.template.name) if len(fam) == 0: print('No detections. Moving on.') continue temp = fam.template prepick = temp.prepick det_ids = [d.id for d in fam] # Read in waveforms for detections in family streams = [read(ev_files[id]) for id in det_ids] # Add template wav as the first element streams.insert(0, temp_wav) print('Template Stream: %s' % str(streams[0])) if len(streams[0]) == 0: print('Template %s waveforms did not get written. Investigate.' % temp.name) continue # Process streams then copy to both ccc_streams and svd_streams print('Shortproc-ing streams') breakit = False for st in streams: # rms = [tr for tr in st if tr.stats.sampling_rate < temp.samp_rate] # for rm in rms: # st.traces.remove(rm) try: shortproc(st=st, lowcut=temp.lowcut, highcut=temp.highcut, filt_order=temp.filt_order, samp_rate=temp.samp_rate) except ValueError as e: breakit = True if breakit: print('Something wrong in shortproc. Skip family') continue # Remove all traces with no picks before copying for str_ind, st in enumerate(streams): if str_ind == 0: event = temp.event else: event = fam.detections[str_ind-1].event rms = [] for tr in st: try: [pk for pk in event.picks if pk.waveform_id.get_seed_string() == tr.id][0] except IndexError: rms.append(tr) for rm in rms: st.traces.remove(rm) print('Copying streams') wrk_streams = copy.deepcopy(streams) svd_streams = copy.deepcopy(streams) # For svd ccc_streams = copy.deepcopy(streams) event_list = [temp.event] + [d.event for d in fam.detections] try: # work out cccoh for each event with template cccohs = cc_coh_dets(streams=ccc_streams, events=event_list, length=svd_len, corr_prepick=prepick, shift=shift_len) except (AssertionError, ValueError) as e: # Issue with trimming above? print(e) continue for eind, st in enumerate(wrk_streams): if eind == 0: event = temp.event else: event = fam.detections[eind-1].event for tr in st: pk = [pk for pk in event.picks if pk.waveform_id.get_seed_string() == tr.id][0] tr.trim(starttime=pk.time - prepick - shift_len, endtime=pk.time + shift_len + align_len) st_seeds = list(set([tr.id for st in wrk_streams for tr in st])) st_seeds.sort() # Align streams with just P arrivals, then use longer st for svd print('Now aligning svd_streams') shift_inds = int(shift_len * fam.template.samp_rate) for st_seed in st_seeds: trs = [] for i, st in enumerate(wrk_streams): if len(st.select(id=st_seed)) > 0: trs.append((i, st.select(id=st_seed)[0])) inds, traces = zip(*trs) shifts, ccs = stacking.align_traces(trace_list=list(traces), shift_len=shift_inds, positive=True, master=traces[0].copy()) # We now have shifts based on P correlation, shift and trim # larger wavs for svd for j, shift in enumerate(shifts): st = svd_streams[inds[j]] if inds[j] == 0: event = temp.event else: event = fam.detections[inds[j]-1].event if ccs[j] < reject: svd_streams[inds[j]].remove(st.select(id=st_seed)[0]) print('Removing trace due to low cc value: %s' % ccs[j]) continue pk = [pk for pk in event.picks if pk.waveform_id.get_seed_string() == st_seed][0] strt_tr = pk.time - prepick - shift st.select(id=st_seed)[0].trim(strt_tr, strt_tr + svd_len) if method == 'LSQR': print('Using least-squares method') event_list = [] for st_id in st_seeds: st_list = [] for stind, st in enumerate(svd_streams): if len(st.select(id=st_id)) > 0: st_list.append(stind) event_list.append(st_list) # event_list = np.asarray(event_list).tolist() u, sigma, v, sta_chans = svd(stream_list=svd_streams, full=True) try: M, events_out = svd_moments(u, sigma, v, sta_chans, event_list) except IOError as e: print('Family %s raised error %s' % (fam.template.name, e)) return elif method == 'PCA': print('Using principal component method') M, events_out = svd_relative_amps(fam, svd_streams, min_amps, plot=plot_svd) print(M, events_out) if len(M) == 0: print('No amplitudes calculated, skipping') continue else: print('{} not valid argument for mag calc method'.format(method)) return # If we have a Mag for template, calibrate moments if calibrate and len(fam.template.event.magnitudes) > 0: print('Converting relative amps to magnitudes') # Convert the template magnitude to seismic moment temp_mag = fam.template.event.magnitudes[-1].mag temp_Mw = ML_to_Mw(temp_mag, m, c) temp_mo = Mw_to_M0(temp_Mw) # Extrapolate from the template moment - relative moment relationship to # Get the moment for relative moment = 1.0 norm_mo = temp_mo / M[0] # Template is the last event in the list # Now these are weights which we can multiple the moments by moments = np.multiply(M, norm_mo) # Now convert to Mw Mw = [Mw_to_M0(mo, inverse=True) for mo in moments] # Convert to local Ml = [ML_to_Mw(mm, m, c, inverse=True) for mm in Mw] #Normalize moments to template mag # Add calibrated mags to detection events for jabba, eind in enumerate(events_out): # Skip template waveform if eind == 0: continue fam.detections[eind].event.magnitudes = [ Magnitude(mag=Mw[jabba], magnitude_type='Mw')] fam.detections[eind].event.comments.append( Comment(text=str(cccohs[eind]))) fam.detections[eind].event.magnitudes.append( Magnitude(mag=Ml[jabba], magnitude_type='ML')) fam.detections[eind].event.preferred_magnitude_id = ( fam.detections[eind].event.magnitudes[-1].resource_id.id) return pty, cccohs