Beispiel #1
0
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
    event = Sfile_util.readpicks(sfile)[0]
    # 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 event.picks:
        if pick.phase_hint 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.waveform_id.station_code)
            channels.append(pick.waveform_id.channel_code)
            picktimes.append(pick.time)
            picktypes.append(pick.phase_hint)
            arrival = [
                arrival for arrival in event.origins[0].arrivals
                if arrival.pick_id == pick.resource_id
            ]
            distances.append(arrival.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))
    del arrival
    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
            ]
            pick_id = event.picks[sta_picks[0]].resource_id
            arrival = [
                arrival for arrival in event.origins[0].arrivals
                if arrival.pick_id == pick_id
            ]
            hypo_dist = arrival.distance
            CAZ = arrival.azimuth
            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()
    for pick in picks_out:
        fout.write(pick)
        # Note this uses the legacy pick class
    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_out
Beispiel #2
0
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)