def Amp_pick_sfile(sfile, datapath, respdir, chans=['Z'], var_wintype=True, \ winlen=0.9, pre_pick=0.2, pre_filt=True, lowcut=1.0,\ highcut=20.0, corners=4): """ Function to read information from a SEISAN s-file, load the data and the picks, cut the data for the channels given around the S-window, simulate a Wood Anderson seismometer, then pick the maximum peak-to-trough amplitude. Output will be put into a mag_calc.out file which will be in full S-file format and can be copied to a REA database. :type sfile: String :type datapath: String :param datapath: Path to the waveform files - usually the path to the WAV directory :type respdir: String :param respdir: Path to the response information directory :type chans: List of strings :param chans: List of the channels to pick on, defaults to ['Z'] - should just be the orientations, e.g. Z,1,2,N,E :type var_wintype: Bool :param var_wintype: If True, the winlen will be multiplied by the P-S time if both P and S picks are available, otherwise it will be multiplied by the hypocentral distance*0.34 - dervided using a p-s ratio of 1.68 and S-velocity of 1.5km/s to give a large window, defaults to True :type winlen: Float :param winlen: Length of window, see above parameter, if var_wintype is False Then this will be in seconds, otherwise it is the multiplier to the p-s time, defaults to 0.5 :type pre_pick: Float :param pre_pick: Time before the s-pick to start the cut window, defaults to 0.2 :type pre_filt: Bool :param pre_filt: To apply a pre-filter or not, defaults to True :type lowcut: Float :param lowcut: Lowcut in Hz for the pre-filter, defaults to 1.0 :type highcut: Float :param highcut: Highcut in Hz for the pre-filter, defaults to 20.0 :type corners: Int :param corners: Number of corners to use in the pre-filter """ # Hardwire a p-s multiplier of hypocentral distance based on p-s ratio of # 1.68 and an S-velocity 0f 1.5km/s, deliberately chosen to be quite slow ps_multiplier=0.34 from eqcorrscan.utils import Sfile_util from obspy import read from scipy.signal import iirfilter from obspy.signal.invsim import paz2AmpValueOfFreqResp import warnings # First we need to work out what stations have what picks picks=Sfile_util.readpicks(sfile) # Convert these picks into a lists stations=[] # List of stations channels=[] # List of channels picktimes=[] # List of pick times picktypes=[] # List of pick types distances=[] # List of hypocentral distances picks_out=[] for pick in picks: if pick.phase in ['P','S']: picks_out.append(pick) # Need to be able to remove this if there # isn't data for a station! stations.append(pick.station) channels.append(pick.channel) picktimes.append(pick.time) picktypes.append(pick.phase) distances.append(pick.distance) # Read in waveforms stream=read(datapath+'/'+Sfile_util.readwavename(sfile)[0]) if len(Sfile_util.readwavename(sfile)) > 1: for wavfile in Sfile_util.readwavename(sfile): stream+=read(datapath+'/'+wavfile) stream.merge() # merge the data, just in case! # For each station cut the window uniq_stas=list(set(stations)) for sta in uniq_stas: for chan in chans: print 'Working on '+sta+' '+chan tr=stream.select(station=sta, channel='*'+chan) if not tr: # Remove picks from file # picks_out=[picks_out[i] for i in xrange(len(picks))\ # if picks_out[i].station+picks_out[i].channel != \ # sta+chan] warnings.warn('There is no station and channel match in the wavefile!') break else: tr=tr[0] # Apply the pre-filter if pre_filt: try: tr.detrend('simple') except: dummy=tr.split() dummy.detrend('simple') tr=dummy.merge()[0] tr.filter('bandpass',freqmin=lowcut, freqmax=highcut,\ corners=corners) sta_picks=[i for i in xrange(len(stations)) \ if stations[i]==sta] hypo_dist=picks[sta_picks[0]].distance CAZ=picks[sta_picks[0]].CAZ if var_wintype: if 'S' in [picktypes[i] for i in sta_picks] and\ 'P' in [picktypes[i] for i in sta_picks]: # If there is an S-pick we can use this :D S_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='S'] S_pick=min(S_pick) P_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='P'] P_pick=min(P_pick) try: tr.trim(starttime=S_pick-pre_pick, \ endtime=S_pick+(S_pick-P_pick)*winlen) except: break elif 'S' in [picktypes[i] for i in sta_picks]: S_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='S'] S_pick=min(S_pick) P_modelled=S_pick-hypo_dist*ps_multiplier try: tr.trim(starttime=S_pick-pre_pick,\ endtime=S_pick+(S_pick-P_modelled)*winlen) except: break else: # In this case we only have a P pick P_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='P'] P_pick=min(P_pick) S_modelled=P_pick+hypo_dist*ps_multiplier try: tr.trim(starttime=S_modelled-pre_pick,\ endtime=S_modelled+(S_modelled-P_pick)*winlen) except: break # Work out the window length based on p-s time or distance elif 'S' in [picktypes[i] for i in sta_picks]: # If the window is fixed we still need to find the start time, # which can be based either on the S-pick (this elif), or # on the hypocentral distance and the P-pick # Take the minimum S-pick time if more than one S-pick is available S_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='S'] S_pick=min(S_pick) try: tr.trim(starttime=S_pick-pre_pick, endtime=S_pick+winlen) except: break else: # In this case, there is no S-pick and the window length is fixed # We need to calculate an expected S_pick based on the hypocentral # distance, this will be quite hand-wavey as we are not using # any kind of velocity model. P_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='P'] P_pick=min(P_pick) hypo_dist=[distances[i] for i in sta_picks\ if picktypes[i]=='P'][0] S_modelled=P_pick+hypo_dist*ps_multiplier try: tr.trim(starttime=S_modelled-pre_pick,\ endtime=S_modelled+winlen) except: break # Find the response information resp_info=_find_resp(tr.stats.station, tr.stats.channel,\ tr.stats.network, tr.stats.starttime, tr.stats.delta,\ respdir) PAZ=[] seedresp=[] if resp_info and 'gain' in resp_info: PAZ=resp_info elif resp_info: seedresp=resp_info # Simulate a Wood Anderson Seismograph if PAZ and len(tr.data) > 10: # Set ten data points to be the minimum to pass tr=_sim_WA(tr, PAZ, None, 10) elif seedresp and len(tr.data) > 10: tr=_sim_WA(tr, None, seedresp, 10) elif len(tr.data) > 10: warnings.warn('No PAZ for '+tr.stats.station+' '+\ tr.stats.channel+' at time: '+\ str(tr.stats.starttime)) continue if len(tr.data) <= 10: # Should remove the P and S picks if len(tr.data)==0 warnings.warn('No data found for: '+tr.stats.station) # print 'No data in miniseed file for '+tr.stats.station+\ # ' removing picks' # picks_out=[picks_out[i] for i in xrange(len(picks_out))\ # if i not in sta_picks] break # Get the amplitude amplitude, period, delay= _max_p2t(tr.data, tr.stats.delta) if amplitude==0.0: break print 'Amplitude picked: '+str(amplitude) # Note, amplitude should be in meters at the moment! # Remove the pre-filter response if pre_filt: # Generate poles and zeros for the filter we used earlier - this # is how the filter is designed in the convenience methods of # filtering in obspy. z, p, k=iirfilter(corners, [lowcut/(0.5*tr.stats.sampling_rate),\ highcut/(0.5*tr.stats.sampling_rate)],\ btype='band', ftype='butter', output='zpk') filt_paz={'poles': list(p), 'zeros': list(z), 'gain': k, 'sensitivity': 1.0} amplitude /= (paz2AmpValueOfFreqResp(filt_paz, 1/period) * \ filt_paz['sensitivity']) # Convert amplitude to mm if PAZ: # Divide by Gain to get to nm (returns pm? 10^-12) # amplitude *=PAZ['gain'] amplitude /= 1000 if seedresp: # Seedresp method returns mm amplitude *= 1000000 # Write out the half amplitude, approximately the peak amplitude as # used directly in magnitude calculations # Page 343 of Seisan manual: # Amplitude (Zero-Peak) in units of nm, nm/s, nm/s^2 or counts amplitude *= 0.5 # Generate a PICK type object for this pick picks_out.append(Sfile_util.PICK(station=tr.stats.station, channel=tr.stats.channel, impulsivity=' ', phase='IAML', weight='', polarity=' ', time=tr.stats.starttime+delay, coda=999, amplitude=amplitude, peri=period, azimuth=float('NaN'), velocity=float('NaN'), AIN=999, SNR='', azimuthres=999, timeres=float('NaN'), finalweight=999, distance=hypo_dist, CAZ=CAZ)) # Copy the header from the sfile to a new local S-file fin=open(sfile,'r') fout=open('mag_calc.out','w') for line in fin: if not line[79]=='7': fout.write(line) else: fout.write(line) break fin.close() fout.close() # Write picks out to new s-file for pick in picks_out: print pick Sfile_util.populateSfile('mag_calc.out',picks_out) return picks
def lag_calc(detections, detect_data, templates, shift_len=0.2, min_cc=0.4): """ Overseer function to take a list of detection objects, cut the data for them to lengths of the same length of the template + shift_len on either side. This will then write out SEISAN s-file for the detections with pick times based on the lag-times found at the maximum correlation, providing that correlation is above the min_cc. :type detections: List of DETECTION :param detections: List of DETECTION objects :type detect_data: obspy.Stream :param detect_data: All the data needed to cut from - can be a gappy Stream :type templates: List of tuple of String, obspy.Stream :param templates: List of the templates used as tuples of template name, template :type shift_len: float :param shift_len: Shift length allowed for the pick in seconds, will be plus/minus this amount - default=0.2 :type min_cc: float :param min_cc: Minimum cross-correlation value to be considered a pick, default=0.4 """ from eqcorrscan.utils import Sfile_util from obspy import Stream # First work out the delays for each template delays = [] # List of tuples for template in templates: temp_delays = [] for tr in tempate[1]: temp_delays.append((tr.stats.station, tr.stats.channel,\ tr.stats.starttime-template.sort['starttime'][0].stats.starttime)) delays.append((template[0], temp_delays)) detect_streams = [] for detection in detections: detect_stream = [] for tr in detect_data: tr_copy = tr.copy() template = [ t for t in templates if t[0] == detection.template_name ][0] template = template.select(station=tr.stats.station, channel=tr.stats.channel) if template: template_len = len(template[0]) else: continue # If there is no template-data match then skip the rest # of the trace loop. delay = [ delay for delay in delays if delay[0] == detection.template_name ][0] delay=[d for d in delay if d[0]==tr.stats.station and \ d[1]==tr.stats.channel][0] detect_stream.append(tr_copy.trim(starttime=detection.detect_time-\ shift_len+delay, endtime=detection.detect_time+delay+\ shift_len+template_len)) detect_streams.append((detection.template_name, Stream(detect_stream))) # Tuple of template name and data stream # Segregate detections by template lags = [] for template in templates: template_detections=[detect[1] for detect in detect_streams\ if detect[0]==template[0]] lags.append(day_loop(template_detections, template[1])) # Write out the lags! for event in lags: # I think I have an old version of Sfile_util here sfilename = Sfile_util.blanksfile(wavefile, 'L', 'PYTH', 'out', True) picks = [] for pick in event: picks.append(Sfile_util.PICK()) Sfile_util.populateSfile(sfilename, picks)
def lag_calc(detections, detect_data, templates, shift_len=0.2, min_cc=0.4): """ Overseer function to take a list of detection objects, cut the data for them to lengths of the same length of the template + shift_len on either side. This will then write out SEISAN s-file for the detections with pick times based on the lag-times found at the maximum correlation, providing that correlation is above the min_cc. :type detections: List of DETECTION :param detections: List of DETECTION objects :type detect_data: obspy.Stream :param detect_data: All the data needed to cut from - can be a gappy Stream :type templates: List of tuple of String, obspy.Stream :param templates: List of the templates used as tuples of template name, template :type shift_len: float :param shift_len: Shift length allowed for the pick in seconds, will be plus/minus this amount - default=0.2 :type min_cc: float :param min_cc: Minimum cross-correlation value to be considered a pick, default=0.4 """ from eqcorrscan.utils import Sfile_util from obspy import Stream # First work out the delays for each template delays=[] # List of tuples for template in templates: temp_delays=[] for tr in tempate[1]: temp_delays.append((tr.stats.station, tr.stats.channel,\ tr.stats.starttime-template.sort['starttime'][0].stats.starttime)) delays.append((template[0], temp_delays)) detect_streams=[] for detection in detections: detect_stream=[] for tr in detect_data: tr_copy=tr.copy() template=[t for t in templates if t[0]==detection.template_name][0] template=template.select(station=tr.stats.station, channel=tr.stats.channel) if template: template_len=len(template[0]) else: continue # If there is no template-data match then skip the rest # of the trace loop. delay=[delay for delay in delays if delay[0]==detection.template_name][0] delay=[d for d in delay if d[0]==tr.stats.station and \ d[1]==tr.stats.channel][0] detect_stream.append(tr_copy.trim(starttime=detection.detect_time-\ shift_len+delay, endtime=detection.detect_time+delay+\ shift_len+template_len)) detect_streams.append((detection.template_name, Stream(detect_stream))) # Tuple of template name and data stream # Segregate detections by template lags=[] for template in templates: template_detections=[detect[1] for detect in detect_streams\ if detect[0]==template[0]] lags.append(day_loop(template_detections, template[1])) # Write out the lags! for event in lags: # I think I have an old version of Sfile_util here sfilename=Sfile_util.blanksfile(wavefile, 'L', 'PYTH', 'out', True) picks=[] for pick in event: picks.append(Sfile_util.PICK()) Sfile_util.populateSfile(sfilename, picks)
def Amp_pick_sfile(sfile, datapath, respdir, chans=['Z'], var_wintype=True, \ winlen=0.9, pre_pick=0.2, pre_filt=True, lowcut=1.0,\ highcut=20.0, corners=4): """ Function to read information from a SEISAN s-file, load the data and the picks, cut the data for the channels given around the S-window, simulate a Wood Anderson seismometer, then pick the maximum peak-to-trough amplitude. Output will be put into a mag_calc.out file which will be in full S-file format and can be copied to a REA database. :type sfile: String :type datapath: String :param datapath: Path to the waveform files - usually the path to the WAV directory :type respdir: String :param respdir: Path to the response information directory :type chans: List of strings :param chans: List of the channels to pick on, defaults to ['Z'] - should just be the orientations, e.g. Z,1,2,N,E :type var_wintype: Bool :param var_wintype: If True, the winlen will be multiplied by the P-S time if both P and S picks are available, otherwise it will be multiplied by the hypocentral distance*0.34 - dervided using a p-s ratio of 1.68 and S-velocity of 1.5km/s to give a large window, defaults to True :type winlen: Float :param winlen: Length of window, see above parameter, if var_wintype is False Then this will be in seconds, otherwise it is the multiplier to the p-s time, defaults to 0.5 :type pre_pick: Float :param pre_pick: Time before the s-pick to start the cut window, defaults to 0.2 :type pre_filt: Bool :param pre_filt: To apply a pre-filter or not, defaults to True :type lowcut: Float :param lowcut: Lowcut in Hz for the pre-filter, defaults to 1.0 :type highcut: Float :param highcut: Highcut in Hz for the pre-filter, defaults to 20.0 :type corners: Int :param corners: Number of corners to use in the pre-filter """ # Hardwire a p-s multiplier of hypocentral distance based on p-s ratio of # 1.68 and an S-velocity 0f 1.5km/s, deliberately chosen to be quite slow ps_multiplier = 0.34 from eqcorrscan.utils import Sfile_util from obspy import read from scipy.signal import iirfilter from obspy.signal.invsim import paz2AmpValueOfFreqResp import warnings # First we need to work out what stations have what picks picks = Sfile_util.readpicks(sfile) # Convert these picks into a lists stations = [] # List of stations channels = [] # List of channels picktimes = [] # List of pick times picktypes = [] # List of pick types distances = [] # List of hypocentral distances picks_out = [] for pick in picks: if pick.phase in ['P', 'S']: picks_out.append(pick) # Need to be able to remove this if there # isn't data for a station! stations.append(pick.station) channels.append(pick.channel) picktimes.append(pick.time) picktypes.append(pick.phase) distances.append(pick.distance) # Read in waveforms stream = read(datapath + '/' + Sfile_util.readwavename(sfile)[0]) if len(Sfile_util.readwavename(sfile)) > 1: for wavfile in Sfile_util.readwavename(sfile): stream += read(datapath + '/' + wavfile) stream.merge() # merge the data, just in case! # For each station cut the window uniq_stas = list(set(stations)) for sta in uniq_stas: for chan in chans: print 'Working on ' + sta + ' ' + chan tr = stream.select(station=sta, channel='*' + chan) if not tr: # Remove picks from file # picks_out=[picks_out[i] for i in xrange(len(picks))\ # if picks_out[i].station+picks_out[i].channel != \ # sta+chan] warnings.warn( 'There is no station and channel match in the wavefile!') break else: tr = tr[0] # Apply the pre-filter if pre_filt: try: tr.detrend('simple') except: dummy = tr.split() dummy.detrend('simple') tr = dummy.merge()[0] tr.filter('bandpass',freqmin=lowcut, freqmax=highcut,\ corners=corners) sta_picks=[i for i in xrange(len(stations)) \ if stations[i]==sta] hypo_dist = picks[sta_picks[0]].distance CAZ = picks[sta_picks[0]].CAZ if var_wintype: if 'S' in [picktypes[i] for i in sta_picks] and\ 'P' in [picktypes[i] for i in sta_picks]: # If there is an S-pick we can use this :D S_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='S'] S_pick = min(S_pick) P_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='P'] P_pick = min(P_pick) try: tr.trim(starttime=S_pick-pre_pick, \ endtime=S_pick+(S_pick-P_pick)*winlen) except: break elif 'S' in [picktypes[i] for i in sta_picks]: S_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='S'] S_pick = min(S_pick) P_modelled = S_pick - hypo_dist * ps_multiplier try: tr.trim(starttime=S_pick-pre_pick,\ endtime=S_pick+(S_pick-P_modelled)*winlen) except: break else: # In this case we only have a P pick P_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='P'] P_pick = min(P_pick) S_modelled = P_pick + hypo_dist * ps_multiplier try: tr.trim(starttime=S_modelled-pre_pick,\ endtime=S_modelled+(S_modelled-P_pick)*winlen) except: break # Work out the window length based on p-s time or distance elif 'S' in [picktypes[i] for i in sta_picks]: # If the window is fixed we still need to find the start time, # which can be based either on the S-pick (this elif), or # on the hypocentral distance and the P-pick # Take the minimum S-pick time if more than one S-pick is available S_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='S'] S_pick = min(S_pick) try: tr.trim(starttime=S_pick - pre_pick, endtime=S_pick + winlen) except: break else: # In this case, there is no S-pick and the window length is fixed # We need to calculate an expected S_pick based on the hypocentral # distance, this will be quite hand-wavey as we are not using # any kind of velocity model. P_pick=[picktimes[i] for i in sta_picks \ if picktypes[i]=='P'] P_pick = min(P_pick) hypo_dist=[distances[i] for i in sta_picks\ if picktypes[i]=='P'][0] S_modelled = P_pick + hypo_dist * ps_multiplier try: tr.trim(starttime=S_modelled-pre_pick,\ endtime=S_modelled+winlen) except: break # Find the response information resp_info=_find_resp(tr.stats.station, tr.stats.channel,\ tr.stats.network, tr.stats.starttime, tr.stats.delta,\ respdir) PAZ = [] seedresp = [] if resp_info and 'gain' in resp_info: PAZ = resp_info elif resp_info: seedresp = resp_info # Simulate a Wood Anderson Seismograph if PAZ and len( tr.data ) > 10: # Set ten data points to be the minimum to pass tr = _sim_WA(tr, PAZ, None, 10) elif seedresp and len(tr.data) > 10: tr = _sim_WA(tr, None, seedresp, 10) elif len(tr.data) > 10: warnings.warn('No PAZ for '+tr.stats.station+' '+\ tr.stats.channel+' at time: '+\ str(tr.stats.starttime)) continue if len(tr.data) <= 10: # Should remove the P and S picks if len(tr.data)==0 warnings.warn('No data found for: ' + tr.stats.station) # print 'No data in miniseed file for '+tr.stats.station+\ # ' removing picks' # picks_out=[picks_out[i] for i in xrange(len(picks_out))\ # if i not in sta_picks] break # Get the amplitude amplitude, period, delay = _max_p2t(tr.data, tr.stats.delta) if amplitude == 0.0: break print 'Amplitude picked: ' + str(amplitude) # Note, amplitude should be in meters at the moment! # Remove the pre-filter response if pre_filt: # Generate poles and zeros for the filter we used earlier - this # is how the filter is designed in the convenience methods of # filtering in obspy. z, p, k=iirfilter(corners, [lowcut/(0.5*tr.stats.sampling_rate),\ highcut/(0.5*tr.stats.sampling_rate)],\ btype='band', ftype='butter', output='zpk') filt_paz = { 'poles': list(p), 'zeros': list(z), 'gain': k, 'sensitivity': 1.0 } amplitude /= (paz2AmpValueOfFreqResp(filt_paz, 1/period) * \ filt_paz['sensitivity']) # Convert amplitude to mm if PAZ: # Divide by Gain to get to nm (returns pm? 10^-12) # amplitude *=PAZ['gain'] amplitude /= 1000 if seedresp: # Seedresp method returns mm amplitude *= 1000000 # Write out the half amplitude, approximately the peak amplitude as # used directly in magnitude calculations # Page 343 of Seisan manual: # Amplitude (Zero-Peak) in units of nm, nm/s, nm/s^2 or counts amplitude *= 0.5 # Generate a PICK type object for this pick picks_out.append( Sfile_util.PICK(station=tr.stats.station, channel=tr.stats.channel, impulsivity=' ', phase='IAML', weight='', polarity=' ', time=tr.stats.starttime + delay, coda=999, amplitude=amplitude, peri=period, azimuth=float('NaN'), velocity=float('NaN'), AIN=999, SNR='', azimuthres=999, timeres=float('NaN'), finalweight=999, distance=hypo_dist, CAZ=CAZ)) # Copy the header from the sfile to a new local S-file fin = open(sfile, 'r') fout = open('mag_calc.out', 'w') for line in fin: if not line[79] == '7': fout.write(line) else: fout.write(line) break fin.close() fout.close() # Write picks out to new s-file for pick in picks_out: print pick Sfile_util.populateSfile('mag_calc.out', picks_out) return picks