Пример #1
0
    def test_writing_blockette_100(self):
        """
        Tests that blockette 100 is written correctly. It is only used if
        the sampling rate is higher than 32727 Hz or smaller than 1.0 /
        32727.0 Hz.
        """
        # Three traces, only the middle one needs it.
        tr = Trace(data=np.linspace(0, 100, 101))
        st = Stream(traces=[tr.copy(), tr.copy(), tr.copy()])

        st[1].stats.sampling_rate = 60000.0

        with io.BytesIO() as buf:
            st.write(buf, format="mseed")
            buf.seek(0, 0)
            st2 = read(buf)

        self.assertTrue(np.allclose(st[0].stats.sampling_rate, st2[0].stats.sampling_rate))
        self.assertTrue(np.allclose(st[1].stats.sampling_rate, st2[1].stats.sampling_rate))
        self.assertTrue(np.allclose(st[2].stats.sampling_rate, st2[2].stats.sampling_rate))

        st[1].stats.sampling_rate = 1.0 / 60000.0

        with io.BytesIO() as buf:
            st.write(buf, format="mseed")
            buf.seek(0, 0)
            st2 = read(buf)

        self.assertTrue(np.allclose(st[0].stats.sampling_rate, st2[0].stats.sampling_rate))
        self.assertTrue(np.allclose(st[1].stats.sampling_rate, st2[1].stats.sampling_rate))
        self.assertTrue(np.allclose(st[2].stats.sampling_rate, st2[2].stats.sampling_rate))
Пример #2
0
    def test_rtrim_with_padding(self):
        """
        Tests the _rtrim() method of the Trace class with padding. It has
        already been tested in the two sided trimming tests. This is just to
        have an explicit test. Also tests issue #429.
        """
        # set up
        trace = Trace(data=np.arange(10))
        start = UTCDateTime(2000, 1, 1, 0, 0, 0, 0)
        trace.stats.starttime = start
        trace.stats.sampling_rate = 1.0
        trace.verify()

        # Pad with no fill_value will mask the additional values.
        tr = trace.copy()
        end = tr.stats.endtime
        tr._rtrim(end + 10, pad=True)
        self.assertEqual(tr.stats.endtime, trace.stats.endtime + 10)
        np.testing.assert_array_equal(tr.data[0:10], np.arange(10))
        # Check that the first couple of entries are not masked.
        self.assertFalse(tr.data[0:10].mask.any())
        # All the other entries should be masked.
        self.assertTrue(tr.data[10:].mask.all())

        # Pad with fill_value.
        tr = trace.copy()
        end = tr.stats.endtime
        tr._rtrim(end + 10, pad=True, fill_value=-33)
        self.assertEqual(tr.stats.endtime, trace.stats.endtime + 10)
        # The first ten entries should not have changed.
        np.testing.assert_array_equal(tr.data[0:10], np.arange(10))
        # The rest should be filled with the fill_value.
        np.testing.assert_array_equal(tr.data[10:], np.ones(10) * -33)
Пример #3
0
 def test_slice_noStarttimeOrEndtime(self):
     """
     Tests the slicing of trace objects with no starttime or endtime
     provided. Compares results against the equivalent trim() operation
     """
     tr_orig = Trace(data=np.arange(10, dtype='int32'))
     tr = tr_orig.copy()
     # two time points outside the trace and two inside
     t1 = tr.stats.starttime - 2
     t2 = tr.stats.starttime + 2
     t3 = tr.stats.endtime - 3
     t4 = tr.stats.endtime + 2
     # test 1: only removing data at left side
     tr_trim = tr_orig.copy()
     tr_trim.trim(starttime=t2)
     self.assertEqual(tr_trim, tr.slice(starttime=t2))
     self.assertEqual(tr_trim, tr.slice(starttime=t2, endtime=t4))
     # test 2: only removing data at right side
     tr_trim = tr_orig.copy()
     tr_trim.trim(endtime=t3)
     self.assertEqual(tr_trim, tr.slice(endtime=t3))
     self.assertEqual(tr_trim, tr.slice(starttime=t1, endtime=t3))
     # test 3: not removing data at all
     tr_trim = tr_orig.copy()
     tr_trim.trim(starttime=t1, endtime=t4)
     self.assertEqual(tr_trim, tr.slice())
     self.assertEqual(tr_trim, tr.slice(starttime=t1))
     self.assertEqual(tr_trim, tr.slice(endtime=t4))
     self.assertEqual(tr_trim, tr.slice(starttime=t1, endtime=t4))
     tr_trim.trim()
     self.assertEqual(tr_trim, tr.slice())
     self.assertEqual(tr_trim, tr.slice(starttime=t1))
     self.assertEqual(tr_trim, tr.slice(endtime=t4))
     self.assertEqual(tr_trim, tr.slice(starttime=t1, endtime=t4))
     # test 4: removing data at left and right side
     tr_trim = tr_orig.copy()
     tr_trim.trim(starttime=t2, endtime=t3)
     self.assertEqual(tr_trim, tr.slice(t2, t3))
     self.assertEqual(tr_trim, tr.slice(starttime=t2, endtime=t3))
     # test 5: no data left after operation
     tr_trim = tr_orig.copy()
     tr_trim.trim(starttime=t4)
     self.assertEqual(tr_trim, tr.slice(starttime=t4))
     self.assertEqual(tr_trim, tr.slice(starttime=t4, endtime=t4 + 1))
Пример #4
0
def resample_and_plot(tr: Trace, sampling_rate: float):
    """
    Simplified (made less general) from obspy Trace.resample.
    
    Uses a frequency domain method to resample, and a hanning window.
    
    Parameters
    ----------
    tr:
        Obspy Trace to resample. Works on a copy of the trace. 
        Your data are safe here.
    sampling_rate:
        Desired sampling rate in Hz.

    Returns
    -------
    Resampled Trace, Figure.
    """ 
    from scipy.signal import get_window
    from scipy.fftpack import rfft, irfft
    import matplotlib.pyplot as plt

    factor = tr.stats.sampling_rate / float(sampling_rate)
    
    # Copy the trace and work on this copy
    tr_out = tr.copy()

    # Copy things for plotting
    data_in = tr.data
    max_time = tr.stats.npts * tr.stats.delta
    dt = tr.stats.delta
    N = tr.stats.npts
    # resample in the frequency domain. Make sure the byteorder is native.
    x = rfft(tr_out.data.newbyteorder("="))
    # Cast the value to be inserted to the same dtype as the array to avoid
    # issues with numpy rule 'safe'.
    x = np.insert(x, 1, x.dtype.type(0))
    if tr_out.stats.npts % 2 == 0:
        x = np.append(x, [0])
    x_r = x[::2]
    x_i = x[1::2]

    # Multiply by a hanning window to stabilise the interpolation
    large_w = np.fft.ifftshift(
        get_window('hanning', tr_out.stats.npts))
    x_r *= large_w[:tr_out.stats.npts // 2 + 1]
    x_i *= large_w[:tr_out.stats.npts // 2 + 1]

    # interpolate
    num = int(tr_out.stats.npts / factor)
    df = 1.0 / (tr_out.stats.npts * tr_out.stats.delta)
    d_large_f = 1.0 / num * sampling_rate
    f = df * np.arange(0, tr_out.stats.npts // 2 + 1, dtype=np.int32)
    n_large_f = num // 2 + 1
    large_f = d_large_f * np.arange(0, n_large_f, dtype=np.int32)
    large_y = np.zeros((2 * n_large_f))
    large_y[::2] = np.interp(large_f, f, x_r)
    large_y[1::2] = np.interp(large_f, f, x_i)

    large_y = np.delete(large_y, 1)
    if num % 2 == 0:
        large_y = np.delete(large_y, -1)
    tr_out.data = irfft(large_y) * (float(num) / float(tr_out.stats.npts))
    tr_out.stats.sampling_rate = sampling_rate
    
    # Plot
    fig, axes = plt.subplots(ncols=2)
    axes[0].plot(np.arange(0, max_time, dt), data_in)
    axes[1].semilogx(
        np.linspace(0.0, 1.0 / (2. * dt), int(N / 2)),
        2./N * np.abs(x[:N//2]), label="Original")
    axes[1].semilogx(
        np.linspace(dt, dt + 1.0 / (2. * tr_out.stats.delta), num // 2),
        2./N * np.abs(large_y[:num//2]), label="Resampled")
    axes[0].plot(np.arange(0, max_time, 1 / sampling_rate), tr_out.data)
    axes[1].legend()
    axes[0].set_xlabel("Time (s)")
    axes[1].set_xlabel("Frequency (Hz)")
    return tr_out, fig
Пример #5
0
def pro5stack(eq_file,
              plot_scale_fac=0.05,
              slowR_lo=-0.1,
              slowR_hi=0.1,
              slow_delta=0.0005,
              start_buff=-50,
              end_buff=50,
              ref_lat=36.3,
              ref_lon=138.5,
              envelope=1,
              plot_dyn_range=1000,
              log_plot=1,
              norm=1,
              global_norm_plot=1,
              color_plot=1,
              fig_index=401,
              ARRAY=0):

    #%% Import functions
    import obspy
    import obspy.signal
    from obspy import UTCDateTime
    from obspy import Stream, Trace
    from obspy import read
    from obspy.geodetics import gps2dist_azimuth
    import numpy as np
    import os
    from obspy.taup import TauPyModel
    import obspy.signal as sign
    import matplotlib.pyplot as plt
    from matplotlib.colors import LogNorm
    model = TauPyModel(model='iasp91')
    from scipy.signal import hilbert
    import math
    import time

    #	import sys # don't show any warnings
    #	import warnings

    print('Running pro5a_stack')

    #%% Get saved event info, also used to name files
    start_time_wc = time.time()

    if ARRAY == 0:
        file = open(eq_file, 'r')
    elif ARRAY == 1:
        file = open('EvLocs/' + eq_file, 'r')
    lines = file.readlines()
    split_line = lines[0].split()
    #			ids.append(split_line[0])  ignore label for now
    t = UTCDateTime(split_line[1])
    date_label = split_line[1][0:10]
    ev_lat = float(split_line[2])
    ev_lon = float(split_line[3])
    ev_depth = float(split_line[4])

    #if not sys.warnoptions:
    #    warnings.simplefilter("ignore")

    #%% Get station location file
    if ARRAY == 0:  # Hinet set and center
        sta_file = '/Users/vidale/Documents/GitHub/Array_codes/Files/hinet_sta.txt'
        ref_lat = 36.3
        ref_lon = 138.5
    elif ARRAY == 1:  # LASA set and center
        sta_file = '/Users/vidale/Documents/GitHub/Array_codes/Files/LASA_sta.txt'
        ref_lat = 46.69
        ref_lon = -106.22
    else:  # NORSAR set and center if 2
        sta_file = '/Users/vidale/Documents/GitHub/Array_codes/Files/NORSAR_sta.txt'
        ref_lat = 61
        ref_lon = 11
    with open(sta_file, 'r') as file:
        lines = file.readlines()
    print(str(len(lines)) + ' stations read from ' + sta_file)
    # Load station coords into arrays
    station_index = range(len(lines))
    st_names = []
    st_lats = []
    st_lons = []
    for ii in station_index:
        line = lines[ii]
        split_line = line.split()
        st_names.append(split_line[0])
        st_lats.append(split_line[1])
        st_lons.append(split_line[2])

#%% Name file, read data
# date_label = '2018-04-02' # date for filename
    if ARRAY == 0:
        fname = 'HD' + date_label + 'sel.mseed'
    elif ARRAY == 1:
        fname = 'Pro_Files/HD' + date_label + 'sel.mseed'
    st = Stream()
    print('reading ' + fname)
    st = read(fname)
    print('Read in: ' + str(len(st)) + ' traces')
    nt = len(st[0].data)
    dt = st[0].stats.delta
    print('First trace has : ' + str(nt) + ' time pts, time sampling of ' +
          str(dt) + ' and thus duration of ' + str((nt - 1) * dt))

    #%% Build Stack arrays
    stack = Stream()
    tr = Trace()
    tr.stats.delta = dt
    tr.stats.network = 'stack'
    tr.stats.channel = 'BHZ'
    slow_n = int(1 +
                 (slowR_hi - slowR_lo) / slow_delta)  # number of slownesses
    stack_nt = int(1 + ((end_buff - start_buff) / dt))  # number of time points
    # In English, stack_slows = range(slow_n) * slow_delta - slowR_lo
    a1 = range(slow_n)
    stack_slows = [(x * slow_delta + slowR_lo) for x in a1]
    print(str(slow_n) + ' slownesses.')
    tr.stats.starttime = t + start_buff
    tr.data = np.zeros(stack_nt)
    done = 0
    for stack_one in stack_slows:
        tr1 = tr.copy()
        tr1.stats.station = str(int(done))
        stack.extend([tr1])
        done += 1
    #	stack.append([tr])
    #	stack += tr

    #  Only need to compute ref location to event distance once
    ref_distance = gps2dist_azimuth(ev_lat, ev_lon, ref_lat, ref_lon)

    #%% Select traces by distance, window and adjust start time to align picked times
    done = 0
    for tr in st:  # traces one by one, find lat-lon by searching entire inventory.  Inefficient but cheap
        for ii in station_index:
            if ARRAY == 0:  # for hi-net, have to chop off last letter, always 'h'
                this_name = st_names[ii]
                this_name_truc = this_name[0:5]
                name_truc_cap = this_name_truc.upper()
            elif ARRAY == 1:
                name_truc_cap = st_names[ii]
            if (tr.stats.station == name_truc_cap
                ):  # find station in inventory
                if norm == 1:
                    tr.normalize()
#					tr.normalize(norm= -len(st)) # mystery command or error
                stalat = float(st_lats[ii])
                stalon = float(
                    st_lons[ii])  # look up lat & lon again to find distance
                distance = gps2dist_azimuth(
                    stalat, stalon, ev_lat,
                    ev_lon)  # Get traveltimes again, hard to store
                tr.stats.distance = distance[0]  # distance in m
                del_dist = (ref_distance[0] - distance[0]) / (1000)  # in km
                # ALSO NEEDS distance station - hypocenter calculation
                #isolate components of distance in radial and transverse directions, ref_distR & ref_distT
                # FIX ref_distR = distance*cos(azi-backazi)
                # FIX ref_distT = distance*sin(azi-backazi)
                #			for(k=0;k<nslow;k++){
                #				slow = 110.*(LOWSLOW + k*DELTASLOW);
                for slow_i in range(
                        slow_n):  # for this station, loop over slownesses
                    time_lag = -del_dist * stack_slows[
                        slow_i]  # time shift due to slowness, flipped to match 2D
                    #					start_offset = tr.stats.starttime - t
                    #					time_correction = (start_buff - (start_offset + time_lag))/dt
                    time_correction = ((t - tr.stats.starttime) +
                                       (time_lag + start_buff)) / dt
                    #				print('Time lag ' + str(time_lag) + ' for slowness ' + str(stack_slows[slow_i]) + ' and distance ' + str(del_dist) + ' time sample correction is ' + str(time_correction))
                    for it in range(stack_nt):  # check points one at a time
                        it_in = int(it + time_correction)
                        if it_in >= 0 and it_in < nt - 1:  # does data lie within seismogram?
                            stack[slow_i].data[it] += tr[it_in]
                done += 1
                if done % 50 == 0:
                    print('Done stacking ' + str(done) + ' out of ' +
                          str(len(st)) + ' stations.')
#%% Plot traces
    global_max = 0
    for slow_i in range(
            slow_n):  # find global max, and if requested, take envelope
        if len(stack[slow_i].data) == 0:
            print('%d data has zero length ' % (slow_i))
        if envelope == 1 or color_plot == 1:
            stack[slow_i].data = np.abs(hilbert(stack[slow_i].data))
        local_max = max(abs(stack[slow_i].data))
        if local_max > global_max:
            global_max = local_max
    if global_max <= 0:
        print('global_max ' + str(global_max) + ' slow_n ' + str(slow_n))

    # create time axis (x-axis), use of slow_i here is arbitrary, oops
    ttt = (np.arange(len(stack[slow_i].data)) * stack[slow_i].stats.delta +
           (stack[slow_i].stats.starttime - t))  # in units of seconds

    # Plotting
    if color_plot == 1:  # 2D color plot
        stack_array = np.zeros((slow_n, stack_nt))

        #	stack_array = np.random.rand(int(slow_n),int(stack_nt))  # test with random numbers
        min_allowed = global_max / plot_dyn_range
        if log_plot == 1:
            for it in range(stack_nt):  # check points one at a time
                for slow_i in range(
                        slow_n):  # for this station, loop over slownesses
                    num_val = stack[slow_i].data[it]
                    if num_val < min_allowed:
                        num_val = min_allowed
                    stack_array[
                        slow_i,
                        it] = math.log10(num_val) - math.log10(min_allowed)
        else:
            for it in range(stack_nt):  # check points one at a time
                for slow_i in range(
                        slow_n):  # for this station, loop over slownesses
                    stack_array[slow_i,
                                it] = stack[slow_i].data[it] / global_max
        y, x = np.mgrid[slice(stack_slows[0], stack_slows[-1] + slow_delta,
                              slow_delta),
                        slice(ttt[0], ttt[-1] + dt,
                              dt)]  # make underlying x-y grid for plot
        #	y, x = np.mgrid[ stack_slows , time ]  # make underlying x-y grid for plot
        plt.close(fig_index)

        fig, ax = plt.subplots(1, figsize=(9, 2))
        fig.subplots_adjust(bottom=0.3)
        #		c = ax.pcolormesh(x, y, stack_array, cmap=plt.cm.gist_yarg)
        #		c = ax.pcolormesh(x, y, stack_array, cmap=plt.cm.gist_rainbow_r)
        c = ax.pcolormesh(x, y, stack_array, cmap=plt.cm.binary)
        ax.axis([x.min(), x.max(), y.min(), y.max()])
        fig.colorbar(c, ax=ax)
        plt.figure(fig_index, figsize=(6, 8))
        plt.close(fig_index)
    else:  # line plot
        for slow_i in range(slow_n):
            dist_offset = stack_slows[slow_i]  # in units of slowness
            if global_norm_plot != 1:
                plt.plot(
                    ttt,
                    stack[slow_i].data * plot_scale_fac /
                    (stack[slow_i].data.max() - stack[slow_i].data.min()) +
                    dist_offset,
                    color='black')
            else:
                plt.plot(ttt,
                         stack[slow_i].data * plot_scale_fac /
                         (global_max - stack[slow_i].data.min()) + dist_offset,
                         color='black')
        plt.ylim(slowR_lo, slowR_hi)
        plt.xlim(start_buff, end_buff)
    plt.xlabel('Time (s)')
    plt.ylabel('Slowness (s/km)')
    plt.title(date_label)
    plt.show()

    #%% Save processed files
    print('Stack has ' + str(len(stack)) + ' traces')
    if ARRAY == 0:
        fname = 'HD' + date_label + '_1dstack.mseed'
    elif ARRAY == 1:
        fname = 'Pro_Files/HD' + date_label + '_1dstack.mseed'
    stack.write(fname, format='MSEED')

    elapsed_time_wc = time.time() - start_time_wc
    print('This job took ' + str(elapsed_time_wc) + ' seconds')
    os.system('say "Done"')
Пример #6
0
def match_filter(template_names, template_list, st, threshold,
                 threshold_type, trig_int, plotvar, plotdir='.', cores=1,
                 tempdir=False, debug=0, plot_format='jpg'):
    r"""Over-arching code to run the correlations of given templates with a\
    day of seismic data and output the detections based on a given threshold.

    :type template_names: list
    :param template_names: List of template names in the same order as\
     template_list
    :type template_list: list :class: 'obspy.Stream'
    :param template_list: A list of templates of which each template is a\
        Stream of obspy traces containing seismic data and header information.
    :type st: :class: 'obspy.Stream'
    :param st: An obspy.Stream object containing all the data available and\
        required for the correlations with templates given.  For efficiency\
        this should contain no excess traces which are not in one or more of\
        the templates.  This will now remove excess traces internally, but\
        will copy the stream and work on the copy, leaving your input stream\
        untouched.
    :type threshold: float
    :param threshold: A threshold value set based on the threshold_type
    :type threshold_type: str
    :param threshold_type: The type of threshold to be used, can be MAD,\
        absolute or av_chan_corr.    MAD threshold is calculated as the\
        threshold*(median(abs(cccsum))) where cccsum is the cross-correlation\
        sum for a given template. absolute threhsold is a true absolute\
        threshold based on the cccsum value av_chan_corr is based on the mean\
        values of single-channel cross-correlations assuming all data are\
        present as required for the template, \
        e.g. av_chan_corr_thresh=threshold*(cccsum/len(template)) where\
        template is a single template from the input and the length is the\
        number of channels within this template.
    :type trig_int: float
    :param trig_int: Minimum gap between detections in seconds.
    :type plotvar: bool
    :param plotvar: Turn plotting on or off
    :type plotdir: str
    :param plotdir: Path to plotting folder, plots will be output here,\
        defaults to run location.
    :type tempdir: String or False
    :param tempdir: Directory to put temporary files, or False
    :type cores: int
    :param cores: Number of cores to use
    :type debug: int
    :param debug: Debug output level, the bigger the number, the more the\
        output.

    :return: :class: 'DETECTIONS' detections for each channel formatted as\
    :class: 'obspy.UTCDateTime' objects.

    .. rubric:: Note
        Plotting within the match-filter routine uses the Agg backend with\
        interactive plotting turned off.  This is because the function is\
        designed to work in bulk.  If you wish to turn interactive plotting on\
        you must import matplotlib in your script first, when you them import\
        match_filter you will get the warning that this call to matplotlib has\
        no effect, which will mean that match_filter has not changed the\
        plotting behaviour.
    """
    import matplotlib
    matplotlib.use('Agg')
    import matplotlib.pyplot as plt
    plt.ioff()
    import copy
    from eqcorrscan.utils import EQcorrscan_plotting
    from eqcorrscan.utils import findpeaks
    from obspy import Trace
    import time

    # Copy the stream here because we will f**k about with it
    stream = st.copy()
    templates = copy.deepcopy(template_list)
    # Debug option to confirm that the channel names match those in the
    # templates
    if debug >= 2:
        template_stachan = []
        data_stachan = []
        for template in templates:
            for tr in template:
                template_stachan.append(tr.stats.station + '.' +
                                        tr.stats.channel)
        for tr in stream:
            data_stachan.append(tr.stats.station + '.' + tr.stats.channel)
        template_stachan = list(set(template_stachan))
        data_stachan = list(set(data_stachan))
        if debug >= 3:
            print 'I have template info for these stations:'
            print template_stachan
            print 'I have daylong data for these stations:'
            print data_stachan
    # Perform a check that the daylong vectors are daylong
    for tr in stream:
        if not tr.stats.sampling_rate * 86400 == tr.stats.npts:
            msg = ' '.join(['Data are not daylong for', tr.stats.station,
                            tr.stats.channel])
            raise ValueError(msg)
    # Call the _template_loop function to do all the correlation work
    outtic = time.clock()
    # Edit here from previous, stable, but slow match_filter
    # Would be worth testing without an if statement, but with every station in
    # the possible template stations having data, but for those without real
    # data make the data NaN to return NaN ccc_sum
    # Note: this works
    if debug >= 2:
        print 'Ensuring all template channels have matches in daylong data'
    template_stachan = []
    for template in templates:
        for tr in template:
            template_stachan += [(tr.stats.station, tr.stats.channel)]
    template_stachan = list(set(template_stachan))
    # Copy this here to keep it safe
    for stachan in template_stachan:
        if not stream.select(station=stachan[0], channel=stachan[1]):
            # Remove template traces rather than adding NaN data
            for template in templates:
                if template.select(station=stachan[0], channel=stachan[1]):
                    for tr in template.select(station=stachan[0],
                                              channel=stachan[1]):
                        template.remove(tr)
    # Remove un-needed channels
    for tr in stream:
        if not (tr.stats.station, tr.stats.channel) in template_stachan:
            stream.remove(tr)
    # Also pad out templates to have all channels
    for template in templates:
        for stachan in template_stachan:
            if not template.select(station=stachan[0], channel=stachan[1]):
                nulltrace = Trace()
                nulltrace.stats.station = stachan[0]
                nulltrace.stats.channel = stachan[1]
                nulltrace.stats.sampling_rate = template[0].stats.sampling_rate
                nulltrace.stats.starttime = template[0].stats.starttime
                nulltrace.data = np.array([np.NaN] * len(template[0].data),
                                          dtype=np.float32)
                template += nulltrace
    if debug >= 2:
        print 'Starting the correlation run for this day'
    [cccsums, no_chans] = _channel_loop(templates, stream, cores, debug)
    if len(cccsums[0]) == 0:
        raise ValueError('Correlation has not run, zero length cccsum')
    outtoc = time.clock()
    print ' '.join(['Looping over templates and streams took:',
                    str(outtoc - outtic), 's'])
    if debug >= 2:
        print ' '.join(['The shape of the returned cccsums is:',
                        str(np.shape(cccsums))])
        print ' '.join(['This is from', str(len(templates)), 'templates'])
        print ' '.join(['Correlated with', str(len(stream)),
                        'channels of data'])
    detections = []
    for i, cccsum in enumerate(cccsums):
        template = templates[i]
        if threshold_type == 'MAD':
            rawthresh = threshold * np.median(np.abs(cccsum))
        elif threshold_type == 'absolute':
            rawthresh = threshold
        elif threshold == 'av_chan_corr':
            rawthresh = threshold * (cccsum / len(template))
        else:
            print 'You have not selected the correct threshold type, I will' +\
                  'use MAD as I like it'
            rawthresh = threshold * np.mean(np.abs(cccsum))
        # Findpeaks returns a list of tuples in the form [(cccsum, sample)]
        print ' '.join(['Threshold is set at:', str(rawthresh)])
        print ' '.join(['Max of data is:', str(max(cccsum))])
        print ' '.join(['Mean of data is:', str(np.mean(cccsum))])
        if np.abs(np.mean(cccsum)) > 0.05:
            warnings.warn('Mean is not zero!  Check this!')
        # Set up a trace object for the cccsum as this is easier to plot and
        # maintins timing
        if plotvar:
            stream_plot = copy.deepcopy(stream[0])
            # Downsample for plotting
            stream_plot.decimate(int(stream[0].stats.sampling_rate / 10))
            cccsum_plot = Trace(cccsum)
            cccsum_plot.stats.sampling_rate = stream[0].stats.sampling_rate
            # Resample here to maintain shape better
            cccsum_hist = cccsum_plot.copy()
            cccsum_hist = cccsum_hist.decimate(int(stream[0].stats.sampling_rate /
                                                   10)).data
            cccsum_plot = EQcorrscan_plotting.chunk_data(cccsum_plot, 10,
                                                         'Maxabs').data
            # Enforce same length
            stream_plot.data = stream_plot.data[0:len(cccsum_plot)]
            cccsum_plot = cccsum_plot[0:len(stream_plot.data)]
            cccsum_hist = cccsum_hist[0:len(stream_plot.data)]
            EQcorrscan_plotting.triple_plot(cccsum_plot, cccsum_hist,
                                            stream_plot, rawthresh, True,
                                            plotdir + '/cccsum_plot_' +
                                            template_names[i] + '_' +
                                            stream[0].stats.starttime.datetime.strftime('%Y-%m-%d') +
                                            '.' + plot_format)
            if debug >= 4:
                print ' '.join(['Saved the cccsum to:', template_names[i],
                                stream[0].stats.starttime.datetime.strftime('%Y%j')])
                np.save(template_names[i] +
                        stream[0].stats.starttime.datetime.strftime('%Y%j'),
                        cccsum)
        tic = time.clock()
        if debug >= 4:
            np.save('cccsum_' + str(i) + '.npy', cccsum)
        if debug >= 3 and max(cccsum) > rawthresh:
            peaks = findpeaks.find_peaks2_short(cccsum, rawthresh,
                                                trig_int * stream[0].stats.sampling_rate,
                                                debug,
                                                stream[0].stats.starttime,
                                                stream[0].stats.sampling_rate)
        elif max(cccsum) > rawthresh:
            peaks = findpeaks.find_peaks2_short(cccsum, rawthresh,
                                                trig_int * stream[0].stats.sampling_rate,
                                                debug)
        else:
            print 'No peaks found above threshold'
            peaks = False
        toc = time.clock()
        if debug >= 1:
            print ' '.join(['Finding peaks took:', str(toc - tic), 's'])
        if peaks:
            for peak in peaks:
                detecttime = stream[0].stats.starttime +\
                    peak[1] / stream[0].stats.sampling_rate
                detections.append(DETECTION(template_names[i],
                                            detecttime,
                                            no_chans[i], peak[0], rawthresh,
                                            'corr'))
    del stream, templates
    return detections
Пример #7
0
def pro5stack2d(eq_file,
                plot_scale_fac=0.05,
                slow_delta=0.0005,
                slowR_lo=-0.1,
                slowR_hi=0.1,
                slowT_lo=-0.1,
                slowT_hi=0.1,
                start_buff=-50,
                end_buff=50,
                norm=1,
                global_norm_plot=1,
                ARRAY=0,
                NS=0,
                decimate_fac=0):

    from obspy import UTCDateTime
    from obspy import Stream, Trace
    from obspy import read
    from obspy.geodetics import gps2dist_azimuth
    import numpy as np
    import os
    from scipy.signal import hilbert
    import math
    import time

    import sys  # don't show any warnings
    import warnings

    print('Running pro5b_stack2d')
    start_time_wc = time.time()

    if ARRAY == 0:
        file = open(eq_file, 'r')
    elif ARRAY == 1:
        goto = '/Users/vidale/Documents/PyCode/LASA/EvLocs'
        os.chdir(goto)
        file = open(eq_file, 'r')

    lines = file.readlines()
    split_line = lines[0].split()
    #			ids.append(split_line[0])  ignore label for now
    t = UTCDateTime(split_line[1])
    date_label = split_line[1][0:10]
    ev_lat = float(split_line[2])
    ev_lon = float(split_line[3])
    #	ev_depth    = float(      split_line[4])

    if not sys.warnoptions:
        warnings.simplefilter("ignore")

#%% Get location file
    if ARRAY == 0:  # Hinet set
        sta_file = '/Users/vidale/Documents/GitHub/Array_codes/Files/hinet_sta.txt'
        ref_lat = 36.3
        ref_lon = 138.5
    else:  # LASA set
        sta_file = '/Users/vidale/Documents/GitHub/Array_codes/Files/LASA_sta.txt'
        ref_lat = 46.69
        ref_lon = -106.22
    with open(sta_file, 'r') as file:
        lines = file.readlines()
    print(str(len(lines)) + ' stations read from ' + sta_file)
    # Load station coords into arrays
    station_index = range(len(lines))
    st_names = []
    st_lats = []
    st_lons = []
    for ii in station_index:
        line = lines[ii]
        split_line = line.split()
        st_names.append(split_line[0])
        st_lats.append(split_line[1])
        st_lons.append(split_line[2])

#%% Input parameters
# date_label = '2018-04-02' # date for filename
    fname = 'HD' + date_label + 'sel.mseed'
    if ARRAY == 1:
        goto = '/Users/vidale/Documents/PyCode/LASA/Pro_Files'
        os.chdir(goto)

    st = Stream()
    st = read(fname)
    print('Read in: ' + str(len(st)) + ' traces')
    nt = len(st[0].data)
    dt = st[0].stats.delta
    print('First trace has : ' + str(nt) + ' time pts, time sampling of ' +
          str(dt) + ' and thus duration of ' + str((nt - 1) * dt))

    #%% Make grid of slownesses
    slowR_n = int(1 +
                  (slowR_hi - slowR_lo) / slow_delta)  # number of slownesses
    slowT_n = int(1 +
                  (slowT_hi - slowT_lo) / slow_delta)  # number of slownesses
    stack_nt = int(1 + ((end_buff - start_buff) / dt))  # number of time points
    # In English, stack_slows = range(slow_n) * slow_delta - slow_lo
    a1R = range(slowR_n)
    a1T = range(slowT_n)
    stack_Rslows = [(x * slow_delta + slowR_lo) for x in a1R]
    stack_Tslows = [(x * slow_delta + slowT_lo) for x in a1T]
    print(
        str(slowR_n) + ' radial slownesses, ' + str(slowT_n) +
        ' trans slownesses, ')

    #%% Build empty Stack array
    stack = Stream()
    tr = Trace()
    tr.stats.delta = dt
    tr.stats.starttime = t + start_buff
    tr.stats.npts = stack_nt
    tr.stats.network = 'stack'
    tr.stats.channel = 'BHZ'
    tr.data = np.zeros(stack_nt)
    done = 0
    for stackR_one in stack_Rslows:
        for stackT_one in stack_Tslows:
            tr1 = tr.copy()
            tr1.stats.station = str(int(done))
            stack.extend([tr1])
            done += 1

    #  Only need to compute ref location to event distance once
    ref_dist_az = gps2dist_azimuth(ev_lat, ev_lon, ref_lat, ref_lon)
    #	ref_dist    = ref_dist_az[0]/1000  # km
    ref_back_az = ref_dist_az[2]
    #	print(f'Ref location {ref_lat:.4f} , {ref_lon:.4f}, event location {ev_lat:.4f}  {ev_lon:.4f} ref_back_az  {ref_back_az:.1f}°')

    #%% select by distance, window and adjust start time to align picked times
    done = 0
    for tr in st:  # traces one by one, find lat-lon by searching entire inventory.  Inefficient but cheap
        for ii in station_index:
            if ARRAY == 0:  # have to chop off last letter, always 'h'
                this_name = st_names[ii]
                this_name_truc = this_name[0:5]
                name_truc_cap = this_name_truc.upper()
            elif ARRAY == 1:
                name_truc_cap = st_names[ii]
            if (tr.stats.station == name_truc_cap
                ):  # find station in inventory
                #			if (tr.stats.station == st_names[ii]): # found station in inventory
                if norm == 1:
                    tr.normalize()  # trace divided abs(max of trace)
                stalat = float(st_lats[ii])
                stalon = float(
                    st_lons[ii])  # use lat & lon to find distance and back-az
                rel_dist_az = gps2dist_azimuth(stalat, stalon, ref_lat,
                                               ref_lon)
                rel_dist = rel_dist_az[0] / 1000  # km
                rel_back_az = rel_dist_az[1]  # radians

                #				print(f'Sta lat-lon {stalat:.4f}  {stalon:.4f}')
                if NS == 0:
                    del_distR = rel_dist * math.cos(
                        (rel_back_az - ref_back_az) * math.pi / 180)
                    del_distT = rel_dist * math.sin(
                        (rel_back_az - ref_back_az) * math.pi / 180)
                # North and east
                else:
                    del_distR = rel_dist * math.cos(
                        rel_back_az * math.pi / 180)
                    del_distT = rel_dist * math.sin(
                        rel_back_az * math.pi / 180)
                for slowR_i in range(
                        slowR_n
                ):  # for this station, loop over radial slownesses
                    for slowT_i in range(
                            slowT_n):  # loop over transverse slownesses
                        time_lag = del_distR * stack_Rslows[
                            slowR_i]  # time shift due to radial slowness
                        time_lag += del_distT * stack_Tslows[
                            slowT_i]  # time shift due to transverse slowness
                        time_correction = ((t - tr.stats.starttime) +
                                           (time_lag + start_buff)) / dt
                        indx = int(slowR_i * slowT_n + slowT_i)
                        for it in range(
                                stack_nt):  # check points one at a time
                            it_in = int(it + time_correction)
                            if it_in >= 0 and it_in < nt - 2:  # does data lie within seismogram?
                                # should be 1, not 2, but 2 prevents the problem "index XX is out of bounds for axis 0 with size XX"
                                stack[indx].data[it] += tr[it_in]
                done += 1
                if done % 20 == 0:
                    print('Done stacking ' + str(done) + ' out of ' +
                          str(len(st)) + ' stations.')
#%% take envelope, decimate envelope
    stack_raw = stack.copy()
    for slowR_i in range(slowR_n):  # loop over radial slownesses
        for slowT_i in range(slowT_n):  # loop over transverse slownesses
            indx = slowR_i * slowT_n + slowT_i
            stack[indx].data = np.abs(hilbert(stack[indx].data))
            if decimate_fac != 0:
                stack[indx].decimate(decimate_fac)

#%%  Save processed files
    fname = 'HD' + date_label + '_2dstack_env.mseed'
    stack.write(fname, format='MSEED')

    fname = 'HD' + date_label + '_2dstack.mseed'
    stack_raw.write(fname, format='MSEED')

    elapsed_time_wc = time.time() - start_time_wc
    print('This job took ' + str(elapsed_time_wc) + ' seconds')
    os.system('say "Done"')
Пример #8
0
def match_filter(template_names, template_list, st, threshold,
                 threshold_type, trig_int, plotvar, plotdir='.', cores=1,
                 tempdir=False, debug=0, plot_format='png',
                 output_cat=False, extract_detections=False,
                 arg_check=True):
    """
    Main matched-filter detection function.
    Over-arching code to run the correlations of given templates with a \
    day of seismic data and output the detections based on a given threshold.
    For a functional example see the tutorials.

    :type template_names: list
    :param template_names: List of template names in the same order as \
        template_list
    :type template_list: list
    :param template_list: A list of templates of which each template is a \
        Stream of obspy traces containing seismic data and header information.
    :type st: obspy.core.stream.Stream
    :param st: An obspy.Stream object containing all the data available and \
        required for the correlations with templates given.  For efficiency \
        this should contain no excess traces which are not in one or more of \
        the templates.  This will now remove excess traces internally, but \
        will copy the stream and work on the copy, leaving your input stream \
        untouched.
    :type threshold: float
    :param threshold: A threshold value set based on the threshold_type
    :type threshold_type: str
    :param threshold_type: The type of threshold to be used, can be MAD, \
        absolute or av_chan_corr.    MAD threshold is calculated as the \
        threshold*(median(abs(cccsum))) where cccsum is the cross-correlation \
        sum for a given template. absolute threhsold is a true absolute \
        threshold based on the cccsum value av_chan_corr is based on the mean \
        values of single-channel cross-correlations assuming all data are \
        present as required for the template, \
        e.g. av_chan_corr_thresh=threshold*(cccsum/len(template)) where \
        template is a single template from the input and the length is the \
        number of channels within this template.
    :type trig_int: float
    :param trig_int: Minimum gap between detections in seconds.
    :type plotvar: bool
    :param plotvar: Turn plotting on or off
    :type plotdir: str
    :param plotdir: Path to plotting folder, plots will be output here, \
        defaults to run location.
    :type tempdir: str
    :param tempdir: Directory to put temporary files, or False
    :type cores: int
    :param cores: Number of cores to use
    :type debug: int
    :param debug: Debug output level, the bigger the number, the more the \
        output.
    :type plot_format: str
    :param plot_format: Specify format of output plots if saved
    :type output_cat: bool
    :param output_cat: Specifies if matched_filter will output an \
        obspy.Catalog class containing events for each detection. Default \
        is False, in which case matched_filter will output a list of \
        detection classes, as normal.
    :type extract_detections: bool
    :param extract_detections: Specifies whether or not to return a list of \
        streams, one stream per detection.
    :type arg_check: bool
    :param arg_check: Check arguments, defaults to True, but if running in \
        bulk, and you are certain of your arguments, then set to False.

    :return: :class: 'DETECTIONS' detections for each channel formatted as \
        :class: 'obspy.UTCDateTime' objects.
    :return: :class: obspy.Catalog containing events for each detection.
    :return: list of :class: obspy.Stream objects for each detection.

    .. note:: Plotting within the match-filter routine uses the Agg backend \
        with interactive plotting turned off.  This is because the function \
        is designed to work in bulk.  If you wish to turn interactive \
        plotting on you must import matplotlib in your script first, when you \
        them import match_filter you will get the warning that this call to \
        matplotlib has no effect, which will mean that match_filter has not \
        changed the plotting behaviour.

    .. note:: The output_cat flag will create an :class: obspy.Catalog \
        containing one event for each :class: 'DETECTIONS' generated by \
        match_filter. Each event will contain a number of comments dealing \
        with correlation values and channels used for the detection. Each \
        channel used for the detection will have a corresponding :class: Pick \
        which will contain time and waveform information. HOWEVER, the user \
        should note that, at present, the pick times do not account for the \
        prepick times inherent in each template. For example, if a template \
        trace starts 0.1 seconds before the actual arrival of that phase, \
        then the pick time generated by match_filter for that phase will be \
        0.1 seconds early. We are looking towards a solution which will \
        involve saving templates alongside associated metadata.
    """
    import matplotlib
    matplotlib.use('Agg')
    import matplotlib.pyplot as plt
    plt.ioff()
    import copy
    from eqcorrscan.utils import plotting
    from eqcorrscan.utils import findpeaks
    from obspy import Trace, Catalog, UTCDateTime, Stream
    from obspy.core.event import Event, Pick, CreationInfo, ResourceIdentifier
    from obspy.core.event import Comment, WaveformStreamID
    import time

    if arg_check:
        # Check the arguments to be nice - if arguments wrong type the parallel
        # output for the error won't be useful
        if not type(template_names) == list:
            raise IOError('template_names must be of type: list')
        if not type(template_list) == list:
            raise IOError('templates must be of type: list')
        for template in template_list:
            if not type(template) == Stream:
                msg = 'template in template_list must be of type: ' +\
                      'obspy.core.stream.Stream'
                raise IOError(msg)
        if not type(st) == Stream:
            msg = 'st must be of type: obspy.core.stream.Stream'
            raise IOError(msg)
        if threshold_type not in ['MAD', 'absolute', 'av_chan_corr']:
            msg = 'threshold_type must be one of: MAD, absolute, av_chan_corr'
            raise IOError(msg)

    # Copy the stream here because we will muck about with it
    stream = st.copy()
    templates = copy.deepcopy(template_list)
    # Debug option to confirm that the channel names match those in the
    # templates
    if debug >= 2:
        template_stachan = []
        data_stachan = []
        for template in templates:
            for tr in template:
                template_stachan.append(tr.stats.station + '.' +
                                        tr.stats.channel)
        for tr in stream:
            data_stachan.append(tr.stats.station + '.' + tr.stats.channel)
        template_stachan = list(set(template_stachan))
        data_stachan = list(set(data_stachan))
        if debug >= 3:
            print('I have template info for these stations:')
            print(template_stachan)
            print('I have daylong data for these stations:')
            print(data_stachan)
    # Perform a check that the daylong vectors are daylong
    for tr in stream:
        if not tr.stats.sampling_rate * 86400 == tr.stats.npts:
            msg = ' '.join(['Data are not daylong for', tr.stats.station,
                            tr.stats.channel])
            raise ValueError(msg)
    # Perform check that all template lengths are internally consistent
    for i, temp in enumerate(template_list):
        if len(set([tr.stats.npts for tr in temp])) > 1:
            msg = 'Template %s contains traces of differing length!! THIS \
                  WILL CAUSE ISSUES' % template_names[i]
            raise ValueError(msg)
    # Call the _template_loop function to do all the correlation work
    outtic = time.clock()
    # Edit here from previous, stable, but slow match_filter
    # Would be worth testing without an if statement, but with every station in
    # the possible template stations having data, but for those without real
    # data make the data NaN to return NaN ccc_sum
    # Note: this works
    if debug >= 2:
        print('Ensuring all template channels have matches in daylong data')
    template_stachan = []
    for template in templates:
        for tr in template:
            template_stachan += [(tr.stats.station, tr.stats.channel)]
    template_stachan = list(set(template_stachan))
    # Copy this here to keep it safe
    for stachan in template_stachan:
        if not stream.select(station=stachan[0], channel=stachan[1]):
            # Remove template traces rather than adding NaN data
            for template in templates:
                if template.select(station=stachan[0], channel=stachan[1]):
                    for tr in template.select(station=stachan[0],
                                              channel=stachan[1]):
                        template.remove(tr)
    # Remove un-needed channels
    for tr in stream:
        if not (tr.stats.station, tr.stats.channel) in template_stachan:
            stream.remove(tr)
    # Also pad out templates to have all channels
    for template, template_name in zip(templates, template_names):
        if len(template) == 0:
            msg = ('No channels matching in continuous data for ' +
                   'template' + template_name)
            warnings.warn(msg)
            templates.remove(template)
            template_names.remove(template_name)
            continue
        for stachan in template_stachan:
            if not template.select(station=stachan[0], channel=stachan[1]):
                nulltrace = Trace()
                nulltrace.stats.station = stachan[0]
                nulltrace.stats.channel = stachan[1]
                nulltrace.stats.sampling_rate = template[0].stats.sampling_rate
                nulltrace.stats.starttime = template[0].stats.starttime
                nulltrace.data = np.array([np.NaN] * len(template[0].data),
                                          dtype=np.float32)
                template += nulltrace
    if debug >= 2:
        print('Starting the correlation run for this day')
    [cccsums, no_chans, chans] = _channel_loop(templates, stream, cores, debug)
    if len(cccsums[0]) == 0:
        raise ValueError('Correlation has not run, zero length cccsum')
    outtoc = time.clock()
    print(' '.join(['Looping over templates and streams took:',
                    str(outtoc - outtic), 's']))
    if debug >= 2:
        print(' '.join(['The shape of the returned cccsums is:',
                        str(np.shape(cccsums))]))
        print(' '.join(['This is from', str(len(templates)), 'templates']))
        print(' '.join(['Correlated with', str(len(stream)),
                        'channels of data']))
    detections = []
    if output_cat:
        det_cat = Catalog()
    for i, cccsum in enumerate(cccsums):
        template = templates[i]
        if threshold_type == 'MAD':
            rawthresh = threshold * np.median(np.abs(cccsum))
        elif threshold_type == 'absolute':
            rawthresh = threshold
        elif threshold_type == 'av_chan_corr':
            rawthresh = threshold * no_chans[i]
        # Findpeaks returns a list of tuples in the form [(cccsum, sample)]
        print(' '.join(['Threshold is set at:', str(rawthresh)]))
        print(' '.join(['Max of data is:', str(max(cccsum))]))
        print(' '.join(['Mean of data is:', str(np.mean(cccsum))]))
        if np.abs(np.mean(cccsum)) > 0.05:
            warnings.warn('Mean is not zero!  Check this!')
        # Set up a trace object for the cccsum as this is easier to plot and
        # maintains timing
        if plotvar:
            stream_plot = copy.deepcopy(stream[0])
            # Downsample for plotting
            stream_plot.decimate(int(stream[0].stats.sampling_rate / 10))
            cccsum_plot = Trace(cccsum)
            cccsum_plot.stats.sampling_rate = stream[0].stats.sampling_rate
            # Resample here to maintain shape better
            cccsum_hist = cccsum_plot.copy()
            cccsum_hist = cccsum_hist.decimate(int(stream[0].stats.
                                                   sampling_rate / 10)).data
            cccsum_plot = plotting.chunk_data(cccsum_plot, 10,
                                              'Maxabs').data
            # Enforce same length
            stream_plot.data = stream_plot.data[0:len(cccsum_plot)]
            cccsum_plot = cccsum_plot[0:len(stream_plot.data)]
            cccsum_hist = cccsum_hist[0:len(stream_plot.data)]
            plotting.triple_plot(cccsum_plot, cccsum_hist,
                                 stream_plot, rawthresh, True,
                                 plotdir + '/cccsum_plot_' +
                                 template_names[i] + '_' +
                                 stream[0].stats.starttime.
                                 datetime.strftime('%Y-%m-%d') +
                                 '.' + plot_format)
            if debug >= 4:
                print(' '.join(['Saved the cccsum to:', template_names[i],
                                stream[0].stats.starttime.datetime.
                                strftime('%Y%j')]))
                np.save(template_names[i] +
                        stream[0].stats.starttime.datetime.strftime('%Y%j'),
                        cccsum)
        tic = time.clock()
        if debug >= 4:
            np.save('cccsum_' + str(i) + '.npy', cccsum)
        if debug >= 3 and max(cccsum) > rawthresh:
            peaks = findpeaks.find_peaks2_short(cccsum, rawthresh,
                                                trig_int * stream[0].stats.
                                                sampling_rate, debug,
                                                stream[0].stats.starttime,
                                                stream[0].stats.sampling_rate)
        elif max(cccsum) > rawthresh:
            peaks = findpeaks.find_peaks2_short(cccsum, rawthresh,
                                                trig_int * stream[0].stats.
                                                sampling_rate, debug)
        else:
            print('No peaks found above threshold')
            peaks = False
        toc = time.clock()
        if debug >= 1:
            print(' '.join(['Finding peaks took:', str(toc - tic), 's']))
        if peaks:
            for peak in peaks:
                detecttime = stream[0].stats.starttime +\
                    peak[1] / stream[0].stats.sampling_rate
                # Detect time must be valid QuakeML uri within resource_id.
                # This will write a formatted string which is still readable by UTCDateTime
                rid = ResourceIdentifier(id=template_names[i] + '_' +
                                         str(detecttime.strftime('%Y%m%dT%H%M%S.%f')),
                                         prefix='smi:local')
                ev = Event(resource_id=rid)
                cr_i = CreationInfo(author='EQcorrscan',
                                    creation_time=UTCDateTime())
                ev.creation_info = cr_i
                # All detection info in Comments for lack of a better idea
                thresh_str = 'threshold=' + str(rawthresh)
                ccc_str = 'detect_val=' + str(peak[0])
                used_chans = 'channels used: ' +\
                             ' '.join([str(pair) for pair in chans[i]])
                ev.comments.append(Comment(text=thresh_str))
                ev.comments.append(Comment(text=ccc_str))
                ev.comments.append(Comment(text=used_chans))
                min_template_tm = min([tr.stats.starttime for tr in template])
                for tr in template:
                    if (tr.stats.station, tr.stats.channel) not in chans[i]:
                        continue
                    else:
                        pick_tm = detecttime + (tr.stats.starttime - min_template_tm)
                        wv_id = WaveformStreamID(network_code=tr.stats.network,
                                                 station_code=tr.stats.station,
                                                 channel_code=tr.stats.channel)
                        ev.picks.append(Pick(time=pick_tm, waveform_id=wv_id))
                detections.append(DETECTION(template_names[i],
                                            detecttime,
                                            no_chans[i], peak[0], rawthresh,
                                            'corr', chans[i], event=ev))
                if output_cat:
                    det_cat.append(ev)
        if extract_detections:
            detection_streams = extract_from_stream(stream, detections)
    del stream, templates
    if output_cat and not extract_detections:
        return detections, det_cat
    elif not extract_detections:
        return detections
    elif extract_detections and not output_cat:
        return detections, detection_streams
    else:
        return detections, det_cat, detection_streams
Пример #9
0
def template_grid(stations, nodes, travel_times, phase, PS_ratio=1.68, \
                samp_rate=100, flength=False, phaseout='all'):
    """
    Function to generate a group of synthetic seismograms to simulate phase
    arrivals from a grid of known sources in a three-dimensional model.  Lags
    must be known and supplied, these can be generated from the bright_lights
    function: read_tt, and resampled to fit the desired grid dimensions and
    spacing using other functions therein.  These synthetic seismograms are very
    simple models of seismograms using the seis_sim function herein.  These
    approximate body-wave P and S first arrivals as spikes convolved with damped
    sine waves.

    :type stations: List
    :param stations: List of the station names
    :type nodes: list of tuple
    :param nodes: List of node locations in (lon,lat,depth)
    :type travel_times: np.ndarray
    :param travel_times: Array of travel times where travel_times[i][:] refers \
        to the travel times for station=stations[i], and travel_times[i][j] \
        refers to stations[i] for nodes[j]
    :type phase: String
    :param phase: Can be either 'P' or 'S'
    :type PS_ratio: float
    :param PS_ratio: P/S velocity ratio, defaults to 1.68
    :type samp_rate: float
    :param samp_rate: Desired sample rate in Hz, defaults to 100.0
    :type flength: int
    :param flength: Length of template in samples, defaults to False
    :type phaseout: str
    :param phaseout: Either 'S', 'P', 'all' or 'both', determines which phases \
            to clip around.  'all' Encompasses both phases in one channel, but\
            will return nothing if the flength is not long enough, 'both' will\
            return two channels for each stations, one SYN_Z with the synthetic\
            P-phase, and one SYN_H with the synthetic S-phase.

    :returns: List of :class:obspy.Stream
    """
    import warnings
    if not phase in ['S','P']:
        raise IOError('Phase is neither P nor S')
    from obspy import Stream, Trace
    #Initialize empty list for templates
    templates=[]
    # Loop through the nodes, for every node generate a template!
    for i, node in enumerate(nodes):
        st=[] # Empty list to be filled with synthetics
        # Loop through stations
        for j, station in enumerate(stations):
            tr=Trace()
            tr.stats.sampling_rate=samp_rate
            tr.stats.station=station
            tr.stats.channel='SYN'
            tt=travel_times[j][i]
            if phase=='P':
                # If the input travel-time is the P-wave travel-time
                SP_time=(tt*PS_ratio)-tt
                if phaseout=='S':
                    tr.stats.starttime+=tt+SP_time
                else:
                    tr.stats.starttime+=tt
            elif phase=='S':
                # If the input travel-time is the S-wave travel-time
                SP_time=tt-(tt/PS_ratio)
                if phaseout=='S':
                    tr.stats.starttime+=tt
                else:
                    tr.stats.starttime+=tt-SP_time
            else:
                raise IOError('Input grid is not P or S')
            # Set start-time of trace to be travel-time for P-wave
            # Check that the template length is long enough to include the SP
            # if SP_time*samp_rate > flength-11:
            #     print 'No template for '+station
            #     print 'Travel-time is :'+str(tt)
            #     print node
            #     print 'SP time is: '+str(SP_time)
            #     #warnings.warn('Cannot make this template, SP-time '+str(SP_time)+\
            #                     # ' longer than length: '+str(flength/samp_rate))
            #     continue
            if SP_time*samp_rate < flength-11 and phaseout=='all':
                tr.data=seis_sim(SP=int(SP_time*samp_rate), amp_ratio=1.5,\
                                flength=flength, phaseout=phaseout)
                st.append(tr)
            elif phaseout=='all':
                warnings.warn('Cannot make a bulk synthetic with this fixed '+\
                              'length for station '+station)
            elif phaseout in ['P','S']:
                tr.data=seis_sim(SP=int(SP_time*samp_rate), amp_ratio=1.5,\
                                flength=flength, phaseout=phaseout)
                st.append(tr)
            elif phaseout == 'both':
                for _phaseout in ['P', 'S']:
                    _tr=tr.copy()
                    _tr.data=seis_sim(SP=int(SP_time*samp_rate), amp_ratio=1.5,\
                                flength=flength, phaseout=_phaseout)
                    if _phaseout=='P':
                        _tr.stats.channel='SYN_Z'
                        # starttime defaults to S-time
                        _tr.stats.starttime=_tr.stats.starttime-SP_time
                    elif _phaseout=='S':
                        _tr.stats.channel='SYN_H'
                    st.append(_tr)
        templates.append(Stream(st))
        # Stream(st).plot(size=(800,600))
    return templates
Пример #10
0
if mtype in ['ln_energy_ratio', 'energy_diff']:
    j = 0.5 * (msr_s - msr_o)**2
elif mtype in [
        'full_waveform', 'windowed_waveform', 'square_envelope', 'envelope'
]:
    j = 0.5 * np.sum(np.power((msr_s - msr_o), 2))

# left hand side of test 1:
# adjt source time function * du = change of misfit wrt u
djdc = np.dot(data, d_c)

# right hand side of test 1:
# Finite difference approx of misfit change for different steps
dcheck = []
d_ch = c_syn.copy()

for step in steps:
    d_ch.data = c_ini + 10.**step * d_c
    msr_sh = m_func(d_ch, **m_a_options)
    if mtype == 'energy_diff':
        msr_sh = msr_sh[0] + msr_sh[1]

    jh = 0.5 * (msr_sh - msr_o)**2
    if mtype in [
            'full_waveform', 'windowed_waveform', 'envelope', 'square_envelope'
    ]:
        jh = 0.5 * np.sum(np.power((msr_sh - msr_o), 2))

    djdch = (jh - j) / (10.**step)
    dcheck.append(abs(djdc - djdch) / abs(djdc))
Пример #11
0
                                                         channel=ich)[0]
                                        s = "%s.%s.%s" % (netwk, ss, ich)
                                        # print " s ==", s
                                        uts = UTCDateTime(
                                            ttt.stats.starttime).timestamp
                                        utr = UTCDateTime(reft).timestamp
                                        if tdifmin <= 0:
                                            timestart = (timex + abs(tdifmin) +
                                                         (uts - utr))

                                        elif tdifmin > 0:
                                            timestart = (timex - abs(tdifmin) +
                                                         (uts - utr))

                                        timend = timestart + temp_length
                                        ta = tc.copy()
                                        ta.trim(
                                            starttime=timestart,
                                            endtime=timend,
                                            pad=True,
                                            fill_value=0,
                                        )
                                        amaxac[il] = max(abs(ta.data))
                                        tid_c = "%s.%s" % (ss, ich)
                                        damaxac[tid_c] = float(amaxac[il])
                                        dct = damaxac[tid_c]
                                        dtt = damaxat[tid_c]
                                        if dct != 0 and dtt != 0:
                                            md[il] = mag_detect(
                                                mt, damaxat[tid_c],
                                                damaxac[tid_c])
Пример #12
0
class TestCosTaper(unittest.TestCase):
    def setUp(self):
        self.sr = 10  # sampling rate
        st = AttribDict({'sampling_rate': self.sr})
        self.testtr = Trace(np.ones(1000), header=st)
        tl = np.random.randint(1, high=20)
        self.tls = tl * self.sr  # taper len in samples
        self.tr_res = pu.cos_taper(self.testtr.copy(), tl, False)
        self.tr_resl = pu.cos_taper(self.testtr.copy(), tl, False, side='left')
        self.tr_resr = pu.cos_taper(self.testtr.copy(),
                                    tl,
                                    False,
                                    side='right')

    def test_in_place(self):
        self.sr = 10  # sampling rate
        st = AttribDict({'sampling_rate': self.sr})
        testtro = Trace(np.ones(1000), header=st)
        testtr = testtro.copy()
        self.assertEqual(testtr, testtro)
        pu.cos_taper(testtr, 5, False)
        self.assertNotEqual(testtr, testtro)

    def test_ends(self):
        # Check that ends reduce to 0
        self.assertAlmostEqual(self.tr_res.data[0], 0)
        self.assertAlmostEqual(self.tr_resl.data[0], 0)
        self.assertAlmostEqual(self.tr_res.data[-1], 0)
        self.assertAlmostEqual(self.tr_resr.data[-1], 0)
        # one-sided taper
        self.assertAlmostEqual(self.tr_resl.data[-1], 1)
        self.assertAlmostEqual(self.tr_resr.data[0], 1)

    def test_middle(self):
        # Assert that the rest (in the middle) stayed the same
        self.assertTrue(
            np.array_equal(self.testtr[self.tls:-self.tls],
                           self.tr_res[self.tls:-self.tls]))
        self.assertTrue(
            np.array_equal(self.testtr[self.tls:-self.tls],
                           self.tr_resr[self.tls:-self.tls]))
        self.assertTrue(
            np.array_equal(self.testtr[self.tls:-self.tls],
                           self.tr_resl[self.tls:-self.tls]))

    def test_up_down(self):
        # Everything else should be between 1 and 0
        # up
        self.assertTrue(np.all(self.tr_res[1:-1] > 0))
        self.assertTrue(np.all(self.tr_resr[1:-1] > 0))
        self.assertTrue(np.all(self.tr_resl[1:-1] > 0))
        self.assertTrue(np.all(self.tr_res[1:self.tls] < 1))
        self.assertTrue(np.all(self.tr_resl[1:self.tls] < 1))
        # down
        self.assertTrue(np.all(self.tr_res[-self.tls:-1] < 1))
        self.assertTrue(np.all(self.tr_resr[-self.tls:-1] < 1))

    def test_empty_trace(self):
        testtr = Trace(np.array([]), header=self.testtr.stats)
        with self.assertRaises(ValueError):
            pu.cos_taper(testtr, 10, False)

    def test_invalid_taper_len(self):
        with self.assertRaises(ValueError):
            pu.cos_taper(self.testtr.copy(), np.random.randint(-100, 0), False)
        with self.assertRaises(ValueError):
            pu.cos_taper(self.testtr.copy(), 501 * self.sr, False)

    def test_masked_value(self):
        tr0 = read()[0]
        tr1 = tr0.copy()
        tr1.stats.starttime += 240
        st = Stream([tr0, tr1])
        tr = st.merge()[0]
        tl = np.random.randint(1, high=5)
        ttr = pu.cos_taper(tr, tl, True)
        # Check that ends reduce to 0
        self.assertAlmostEqual(ttr.data[0], 0)
        self.assertAlmostEqual(ttr.data[-1], 0)
        self.assertAlmostEqual(ttr.data[tr0.count() - 1], 0)
        self.assertAlmostEqual(ttr.data[-tr1.count()], 0)
        # Also the mask should be retained
        self.assertEqual(len(ttr.data[ttr.data.mask]),
                         ttr.count() - tr0.count() - tr1.count())
Пример #13
0
def extract_s(taupy_model,
              picker,
              event,
              station_longitude,
              station_latitude,
              stn,
              ste,
              ba,
              win_start=-50,
              win_end=50,
              resample_hz=20,
              bp_freqmins=[0.05, 2],
              bp_freqmaxs=[0.5, 5],
              margin=20,
              max_amplitude=1e8):

    po = event.preferred_origin
    if (not po): return None

    atimes = []
    try:
        atimes = taupy_model.get_travel_times_geo(po.depthkm,
                                                  po.lat,
                                                  po.lon,
                                                  station_latitude,
                                                  station_longitude,
                                                  phase_list=('S', ))
    except:
        return None
    # end try

    if (len(atimes) == 0): return None
    tat = atimes[0].time  # theoretical arrival time

    tr = None
    try:
        stn = stn.slice(po.utctime + tat + win_start,
                        po.utctime + tat + win_end)
        stn.resample(resample_hz)

        if (ste):
            ste = ste.slice(po.utctime + tat + win_start,
                            po.utctime + tat + win_end)
            ste.resample(resample_hz)
        # end if

        if (ste):
            if (type(stn[0].data) == np.ndarray
                    and type(ste[0].data) == np.ndarray):
                rc, tc = rotate_ne_rt(stn[0].data, ste[0].data, ba)
                tr = Trace(data=tc, header=stn[0].stats)
                #tr = Trace(data=np.sqrt(np.power(rc,2) + np.power(tc,2)), header=stn[0].stats)
            # end if
        else:
            if (type(stn[0].data) == np.ndarray):
                tr = stn[0]
            # end if
        # end if
    except Exception as e:
        return None
    # end try

    if (tr):
        if (np.max(tr.data) > max_amplitude): return None

        pickslist = []
        snrlist = []
        residuallist = []

        tr.detrend('linear')
        for i in range(len(bp_freqmins)):
            trc = tr.copy()
            trc.filter('bandpass',
                       freqmin=bp_freqmins[i],
                       freqmax=bp_freqmaxs[i],
                       corners=4,
                       zerophase=True)

            try:
                scnl, picks, polarity, snr, uncert = picker.picks(trc)

                for ipick, pick in enumerate(picks):
                    actualArrival = pick - po.utctime
                    residual = actualArrival - tat

                    if (np.fabs(residual) < margin):
                        pickslist.append(pick)
                        snrlist.append(snr[ipick])
                        residuallist.append(residual)

                        #summary = fbpicker.FBSummary(picker, trc)
                        #summary = aicdpicker.AICDSummary(picker, trc)
                        #outputPath = '/home/rakib/work/pst/picking/sarr'
                        #outputPath = '/g/data1a/ha3/rakib/seismic/pst/tests/plots/new'
                        #ofn = '%s/%s.%s_%f_%d.s.png' % (outputPath, scnl, str(po.utctime), snr[0], i)
                        #summary.plot_picks(show=False, savefn=ofn)
                    # end if
                # end for
            except:
                continue
            # end try
        # end for

        if (len(pickslist)):
            optimal_pick_idx = np.argmax(np.array(snrlist))

            return pickslist[optimal_pick_idx], residuallist[optimal_pick_idx], \
                   snrlist[optimal_pick_idx], optimal_pick_idx
        # end if
    # end if

    return None
Пример #14
0
def match_filter(template_names, template_list, st, threshold,
                 threshold_type, trig_int, plotvar, plotdir='.', cores=1,
                 tempdir=False, debug=0, plot_format='png',
                 output_cat=False, extract_detections=False,
                 arg_check=True):
    """
    Main matched-filter detection function.
    Over-arching code to run the correlations of given templates with a \
    day of seismic data and output the detections based on a given threshold.
    For a functional example see the tutorials.

    :type template_names: list
    :param template_names: List of template names in the same order as \
        template_list
    :type template_list: list
    :param template_list: A list of templates of which each template is a \
        Stream of obspy traces containing seismic data and header information.
    :type st: obspy.core.stream.Stream
    :param st: An obspy.Stream object containing all the data available and \
        required for the correlations with templates given.  For efficiency \
        this should contain no excess traces which are not in one or more of \
        the templates.  This will now remove excess traces internally, but \
        will copy the stream and work on the copy, leaving your input stream \
        untouched.
    :type threshold: float
    :param threshold: A threshold value set based on the threshold_type
    :type threshold_type: str
    :param threshold_type: The type of threshold to be used, can be MAD, \
        absolute or av_chan_corr.    MAD threshold is calculated as the \
        threshold*(median(abs(cccsum))) where cccsum is the cross-correlation \
        sum for a given template. absolute threhsold is a true absolute \
        threshold based on the cccsum value av_chan_corr is based on the mean \
        values of single-channel cross-correlations assuming all data are \
        present as required for the template, \
        e.g. av_chan_corr_thresh=threshold*(cccsum/len(template)) where \
        template is a single template from the input and the length is the \
        number of channels within this template.
    :type trig_int: float
    :param trig_int: Minimum gap between detections in seconds.
    :type plotvar: bool
    :param plotvar: Turn plotting on or off
    :type plotdir: str
    :param plotdir: Path to plotting folder, plots will be output here, \
        defaults to run location.
    :type tempdir: str
    :param tempdir: Directory to put temporary files, or False
    :type cores: int
    :param cores: Number of cores to use
    :type debug: int
    :param debug: Debug output level, the bigger the number, the more the \
        output.
    :type plot_format: str
    :param plot_format: Specify format of output plots if saved
    :type output_cat: bool
    :param output_cat: Specifies if matched_filter will output an \
        obspy.Catalog class containing events for each detection. Default \
        is False, in which case matched_filter will output a list of \
        detection classes, as normal.
    :type extract_detections: bool
    :param extract_detections: Specifies whether or not to return a list of \
        streams, one stream per detection.
    :type arg_check: bool
    :param arg_check: Check arguments, defaults to True, but if running in \
        bulk, and you are certain of your arguments, then set to False.

    :return: :class: 'DETECTIONS' detections for each channel formatted as \
        :class: 'obspy.UTCDateTime' objects.
    :return: :class: obspy.Catalog containing events for each detection.
    :return: list of :class: obspy.Stream objects for each detection.

    .. note:: Plotting within the match-filter routine uses the Agg backend \
        with interactive plotting turned off.  This is because the function \
        is designed to work in bulk.  If you wish to turn interactive \
        plotting on you must import matplotlib in your script first, when you \
        them import match_filter you will get the warning that this call to \
        matplotlib has no effect, which will mean that match_filter has not \
        changed the plotting behaviour.

    .. note:: The output_cat flag will create an :class: obspy.Catalog \
        containing one event for each :class: 'DETECTIONS' generated by \
        match_filter. Each event will contain a number of comments dealing \
        with correlation values and channels used for the detection. Each \
        channel used for the detection will have a corresponding :class: Pick \
        which will contain time and waveform information. HOWEVER, the user \
        should note that, at present, the pick times do not account for the \
        prepick times inherent in each template. For example, if a template \
        trace starts 0.1 seconds before the actual arrival of that phase, \
        then the pick time generated by match_filter for that phase will be \
        0.1 seconds early. We are looking towards a solution which will \
        involve saving templates alongside associated metadata.
    """
    import matplotlib
    matplotlib.use('Agg')
    import matplotlib.pyplot as plt
    plt.ioff()
    import copy
    from eqcorrscan.utils import plotting
    from eqcorrscan.utils import findpeaks
    from obspy import Trace, Catalog, UTCDateTime, Stream
    from obspy.core.event import Event, Pick, CreationInfo, ResourceIdentifier
    from obspy.core.event import Comment, WaveformStreamID
    import time

    if arg_check:
        # Check the arguments to be nice - if arguments wrong type the parallel
        # output for the error won't be useful
        if not type(template_names) == list:
            raise IOError('template_names must be of type: list')
        if not type(template_list) == list:
            raise IOError('templates must be of type: list')
        for template in template_list:
            if not type(template) == Stream:
                msg = 'template in template_list must be of type: ' +\
                      'obspy.core.stream.Stream'
                raise IOError(msg)
        if not type(st) == Stream:
            msg = 'st must be of type: obspy.core.stream.Stream'
            raise IOError(msg)
        if str(threshold_type) not in [str('MAD'), str('absolute'),
                                       str('av_chan_corr')]:
            msg = 'threshold_type must be one of: MAD, absolute, av_chan_corr'
            raise IOError(msg)

    # Copy the stream here because we will muck about with it
    stream = st.copy()
    templates = copy.deepcopy(template_list)
    # Debug option to confirm that the channel names match those in the
    # templates
    if debug >= 2:
        template_stachan = []
        data_stachan = []
        for template in templates:
            for tr in template:
                template_stachan.append(tr.stats.station + '.' +
                                        tr.stats.channel)
        for tr in stream:
            data_stachan.append(tr.stats.station + '.' + tr.stats.channel)
        template_stachan = list(set(template_stachan))
        data_stachan = list(set(data_stachan))
        if debug >= 3:
            print('I have template info for these stations:')
            print(template_stachan)
            print('I have daylong data for these stations:')
            print(data_stachan)
    # Perform a check that the daylong vectors are all the same length
    min_start_time = min([tr.stats.starttime for tr in stream])
    max_end_time = max([tr.stats.endtime for tr in stream])
    longest_trace_length = stream[0].stats.sampling_rate * (max_end_time -
                                                            min_start_time)
    for tr in stream:
        if not tr.stats.npts == longest_trace_length:
            msg = 'Data are not equal length, padding short traces'
            warnings.warn(msg)
            start_pad = np.zeros(int(tr.stats.sampling_rate *
                                     (tr.stats.starttime - min_start_time)))
            end_pad = np.zeros(int(tr.stats.sampling_rate *
                                   (max_end_time - tr.stats.endtime)))
            tr.data = np.concatenate([start_pad, tr.data, end_pad])
    # Perform check that all template lengths are internally consistent
    for i, temp in enumerate(template_list):
        if len(set([tr.stats.npts for tr in temp])) > 1:
            msg = 'Template %s contains traces of differing length!! THIS \
                  WILL CAUSE ISSUES' % template_names[i]
            raise ValueError(msg)
    # Call the _template_loop function to do all the correlation work
    outtic = time.clock()
    # Edit here from previous, stable, but slow match_filter
    # Would be worth testing without an if statement, but with every station in
    # the possible template stations having data, but for those without real
    # data make the data NaN to return NaN ccc_sum
    # Note: this works
    if debug >= 2:
        print('Ensuring all template channels have matches in long data')
    template_stachan = []
    for template in templates:
        for tr in template:
            template_stachan += [(tr.stats.station, tr.stats.channel)]
    template_stachan = list(set(template_stachan))
    # Copy this here to keep it safe
    for stachan in template_stachan:
        if not stream.select(station=stachan[0], channel=stachan[1]):
            # Remove template traces rather than adding NaN data
            for template in templates:
                if template.select(station=stachan[0], channel=stachan[1]):
                    for tr in template.select(station=stachan[0],
                                              channel=stachan[1]):
                        template.remove(tr)
    # Remove un-needed channels
    for tr in stream:
        if not (tr.stats.station, tr.stats.channel) in template_stachan:
            stream.remove(tr)
    # Also pad out templates to have all channels
    for template, template_name in zip(templates, template_names):
        if len(template) == 0:
            msg = ('No channels matching in continuous data for ' +
                   'template' + template_name)
            warnings.warn(msg)
            templates.remove(template)
            template_names.remove(template_name)
            continue
        for stachan in template_stachan:
            if not template.select(station=stachan[0], channel=stachan[1]):
                nulltrace = Trace()
                nulltrace.stats.station = stachan[0]
                nulltrace.stats.channel = stachan[1]
                nulltrace.stats.sampling_rate = template[0].stats.sampling_rate
                nulltrace.stats.starttime = template[0].stats.starttime
                nulltrace.data = np.array([np.NaN] * len(template[0].data),
                                          dtype=np.float32)
                template += nulltrace
    if debug >= 2:
        print('Starting the correlation run for this day')
    [cccsums, no_chans, chans] = _channel_loop(templates=templates,
                                               stream=stream,
                                               cores=cores,
                                               debug=debug)
    if len(cccsums[0]) == 0:
        raise ValueError('Correlation has not run, zero length cccsum')
    outtoc = time.clock()
    print(' '.join(['Looping over templates and streams took:',
                    str(outtoc - outtic), 's']))
    if debug >= 2:
        print(' '.join(['The shape of the returned cccsums is:',
                        str(np.shape(cccsums))]))
        print(' '.join(['This is from', str(len(templates)), 'templates']))
        print(' '.join(['Correlated with', str(len(stream)),
                        'channels of data']))
    detections = []
    if output_cat:
        det_cat = Catalog()
    for i, cccsum in enumerate(cccsums):
        template = templates[i]
        if str(threshold_type) == str('MAD'):
            rawthresh = threshold * np.median(np.abs(cccsum))
        elif str(threshold_type) == str('absolute'):
            rawthresh = threshold
        elif str(threshold_type) == str('av_chan_corr'):
            rawthresh = threshold * no_chans[i]
        # Findpeaks returns a list of tuples in the form [(cccsum, sample)]
        print(' '.join(['Threshold is set at:', str(rawthresh)]))
        print(' '.join(['Max of data is:', str(max(cccsum))]))
        print(' '.join(['Mean of data is:', str(np.mean(cccsum))]))
        if np.abs(np.mean(cccsum)) > 0.05:
            warnings.warn('Mean is not zero!  Check this!')
        # Set up a trace object for the cccsum as this is easier to plot and
        # maintains timing
        if plotvar:
            stream_plot = copy.deepcopy(stream[0])
            # Downsample for plotting
            stream_plot.decimate(int(stream[0].stats.sampling_rate / 10))
            cccsum_plot = Trace(cccsum)
            cccsum_plot.stats.sampling_rate = stream[0].stats.sampling_rate
            # Resample here to maintain shape better
            cccsum_hist = cccsum_plot.copy()
            cccsum_hist = cccsum_hist.decimate(int(stream[0].stats.
                                                   sampling_rate / 10)).data
            cccsum_plot = plotting.chunk_data(cccsum_plot, 10,
                                              'Maxabs').data
            # Enforce same length
            stream_plot.data = stream_plot.data[0:len(cccsum_plot)]
            cccsum_plot = cccsum_plot[0:len(stream_plot.data)]
            cccsum_hist = cccsum_hist[0:len(stream_plot.data)]
            plotting.triple_plot(cccsum_plot, cccsum_hist,
                                 stream_plot, rawthresh, True,
                                 plotdir + '/cccsum_plot_' +
                                 template_names[i] + '_' +
                                 stream[0].stats.starttime.
                                 datetime.strftime('%Y-%m-%d') +
                                 '.' + plot_format)
            if debug >= 4:
                print(' '.join(['Saved the cccsum to:', template_names[i],
                                stream[0].stats.starttime.datetime.
                                strftime('%Y%j')]))
                np.save(template_names[i] +
                        stream[0].stats.starttime.datetime.strftime('%Y%j'),
                        cccsum)
        tic = time.clock()
        if debug >= 4:
            np.save('cccsum_' + str(i) + '.npy', cccsum)
        if debug >= 3 and max(cccsum) > rawthresh:
            peaks = findpeaks.find_peaks2_short(cccsum, rawthresh,
                                                trig_int * stream[0].stats.
                                                sampling_rate, debug,
                                                stream[0].stats.starttime,
                                                stream[0].stats.sampling_rate)
        elif max(cccsum) > rawthresh:
            peaks = findpeaks.find_peaks2_short(cccsum, rawthresh,
                                                trig_int * stream[0].stats.
                                                sampling_rate, debug)
        else:
            print('No peaks found above threshold')
            peaks = False
        toc = time.clock()
        if debug >= 1:
            print(' '.join(['Finding peaks took:', str(toc - tic), 's']))
        if peaks:
            for peak in peaks:
                detecttime = stream[0].stats.starttime +\
                    peak[1] / stream[0].stats.sampling_rate
                # Detect time must be valid QuakeML uri within resource_id.
                # This will write a formatted string which is still
                # readable by UTCDateTime
                rid = ResourceIdentifier(id=template_names[i] + '_' +
                                         str(detecttime.
                                             strftime('%Y%m%dT%H%M%S.%f')),
                                         prefix='smi:local')
                ev = Event(resource_id=rid)
                cr_i = CreationInfo(author='EQcorrscan',
                                    creation_time=UTCDateTime())
                ev.creation_info = cr_i
                # All detection info in Comments for lack of a better idea
                thresh_str = 'threshold=' + str(rawthresh)
                ccc_str = 'detect_val=' + str(peak[0])
                used_chans = 'channels used: ' +\
                             ' '.join([str(pair) for pair in chans[i]])
                ev.comments.append(Comment(text=thresh_str))
                ev.comments.append(Comment(text=ccc_str))
                ev.comments.append(Comment(text=used_chans))
                min_template_tm = min([tr.stats.starttime for tr in template])
                for tr in template:
                    if (tr.stats.station, tr.stats.channel) not in chans[i]:
                        continue
                    else:
                        pick_tm = detecttime + (tr.stats.starttime -
                                                min_template_tm)
                        wv_id = WaveformStreamID(network_code=tr.stats.network,
                                                 station_code=tr.stats.station,
                                                 channel_code=tr.stats.channel)
                        ev.picks.append(Pick(time=pick_tm, waveform_id=wv_id))
                detections.append(DETECTION(template_names[i],
                                            detecttime,
                                            no_chans[i], peak[0], rawthresh,
                                            'corr', chans[i], event=ev))
                if output_cat:
                    det_cat.append(ev)
        if extract_detections:
            detection_streams = extract_from_stream(stream, detections)
    del stream, templates
    if output_cat and not extract_detections:
        return detections, det_cat
    elif not extract_detections:
        return detections
    elif extract_detections and not output_cat:
        return detections, detection_streams
    else:
        return detections, det_cat, detection_streams
Пример #15
0
def extract_s(taupy_model,
              pickerlist,
              event,
              station_longitude,
              station_latitude,
              stn,
              ste,
              ba,
              win_start=-50,
              win_end=50,
              resample_hz=20,
              bp_freqmins=[0.01, 0.01, 0.5],
              bp_freqmaxs=[1, 2., 5.],
              margin=None,
              max_amplitude=1e8,
              plot_output_folder=None):

    po = event.preferred_origin
    if (not po): return None

    atimes = []
    try:
        atimes = taupy_model.get_travel_times_geo(po.depthkm,
                                                  po.lat,
                                                  po.lon,
                                                  station_latitude,
                                                  station_longitude,
                                                  phase_list=('S', ))
    except:
        return None
    # end try

    if (len(atimes) == 0): return None
    tat = atimes[0].time  # theoretical arrival time

    buffer_start = -10
    buffer_end = 10
    snrtr = None
    try:
        stn = stn.slice(po.utctime + tat + win_start + buffer_start,
                        po.utctime + tat + win_end + buffer_end)
        stn = stn.copy()
        stn.resample(resample_hz)
        stn.detrend('linear')

        if (ste):
            ste = ste.slice(po.utctime + tat + win_start + buffer_start,
                            po.utctime + tat + win_end + buffer_end)
            ste = ste.copy()
            ste.resample(resample_hz)
            ste.detrend('linear')
        # end if

        if (ste):
            if (type(stn[0].data) == np.ndarray
                    and type(ste[0].data) == np.ndarray):
                rc, tc = rotate_ne_rt(stn[0].data, ste[0].data, ba)
                snrtr = Trace(data=tc, header=stn[0].stats)
                snrtr.detrend('linear')
            # end if
        else:
            if (type(stn[0].data) == np.ndarray):
                snrtr = stn[0]
            # end if
        # end if
    except Exception as e:
        return None
    # end try

    if (type(snrtr.data) == np.ndarray):
        if (np.max(snrtr.data) > max_amplitude): return None

        pickslist = []
        snrlist = []
        residuallist = []
        bandindex = -1
        pickerindex = -1
        taper_percentage = float(buffer_end) / float(win_end)

        foundpicks = False
        for i in range(len(bp_freqmins)):
            trc = snrtr.copy()
            trc.taper(max_percentage=taper_percentage, type='hann')
            trc.filter('bandpass',
                       freqmin=bp_freqmins[i],
                       freqmax=bp_freqmaxs[i],
                       corners=4,
                       zerophase=True)
            trc = trc.slice(po.utctime + tat + win_start,
                            po.utctime + tat + win_end)

            for ipicker, picker in enumerate(pickerlist):
                try:
                    scnl, picks, polarity, snr, uncert = picker.picks(trc)

                    for ipick, pick in enumerate(picks):
                        actualArrival = pick - po.utctime
                        residual = actualArrival - tat

                        if ((margin and np.fabs(residual) < margin)
                                or (margin == None)):
                            pickslist.append(pick)

                            plotinfo = None
                            if (plot_output_folder):
                                plotinfo = {
                                    'eventid': event.public_id,
                                    'origintime': po.utctime,
                                    'mag':
                                    event.preferred_magnitude.magnitude_value,
                                    'net': trc.stats.network,
                                    'sta': trc.stats.station,
                                    'phase': 's',
                                    'ppsnr': snr[ipick],
                                    'pickid': ipick,
                                    'outputfolder': plot_output_folder
                                }
                            # end if

                            wab = snrtr.slice(pick - 3, pick + 3)
                            wab_filtered = trc.slice(pick - 3, pick + 3)
                            scales = np.logspace(0.5, 4, 30)
                            cwtsnr, dom_freq, slope_ratio = compute_quality_measures(
                                wab, wab_filtered, scales, plotinfo)
                            snrlist.append(
                                [snr[ipick], cwtsnr, dom_freq, slope_ratio])

                            residuallist.append(residual)
                            bandindex = i
                            pickerindex = ipicker

                            foundpicks = True
                        # end if
                    # end for
                except:
                    continue
                # end try
                if (foundpicks): break
            # end for
            if (foundpicks): break
        # end for

        if (len(pickslist)):
            return pickslist, residuallist, \
                   np.array(snrlist), bandindex, pickerindex
        # end if
    # end if

    return None
Пример #16
0
def match_filter(
    template_names, templates, stream, threshold, threshold_type, trig_int, plotvar, cores=1, tempdir=False, debug=0
):
    """
    Over-arching code to run the correlations of given templates with a day of
    seismic data and output the detections based on a given threshold.

    :type templates: list :class: 'obspy.Stream'
    :param templates: A list of templates of which each template is a Stream of\
        obspy traces containing seismic data and header information.
    :type stream: :class: 'obspy.Stream'
    :param stream: An obspy.Stream object containing all the data available and\
        required for the correlations with templates given.  For efficiency this\
        should contain no excess traces which are not in one or more of the\
        templates.
    :type threshold: float
    :param threshold: A threshold value set based on the threshold_type
    :type threshold_type: str
    :param threshold_type: The type of threshold to be used, can be MAD,\
        absolute or av_chan_corr.    MAD threshold is calculated as the\
        threshold*(median(abs(cccsum))) where cccsum is the cross-correlation\
        sum for a given template. absolute threhsold is a true absolute\
        threshold based on the cccsum value av_chan_corr is based on the mean\
        values of single-channel cross-correlations assuming all data are\
        present as required for the template, \
        e.g. av_chan_corr_thresh=threshold*(cccsum/len(template)) where\
        template is a single template from the input and the length is the\
        number of channels within this template.
    :type trig_int: float
    :param trig_int: Minimum gap between detections in seconds.
    :type tempdir: String or False
    :param tempdir: Directory to put temporary files, or False
    :type cores: int
    :param cores: Number of cores to use
    :type debug: int
    :param debug: Debug output level, the bigger the number, the more the output

    :return: :class: 'DETECTIONS' detections for each channel formatted as\
    :class: 'obspy.UTCDateTime' objects.

    """
    from eqcorrscan.utils import findpeaks, EQcorrscan_plotting
    import time, copy
    from obspy import Trace

    match_internal = False  # Set to True if memory is an issue, if True, will only
    # use about the same amount of memory as the seismic dat
    # take up.  If False, it will use 20-100GB per instance
    # Debug option to confirm that the channel names match those in the templates
    if debug >= 2:
        template_stachan = []
        data_stachan = []
        for template in templates:
            for tr in template:
                template_stachan.append(tr.stats.station + "." + tr.stats.channel)
        for tr in stream:
            data_stachan.append(tr.stats.station + "." + tr.stats.channel)
        template_stachan = list(set(template_stachan))
        data_stachan = list(set(data_stachan))
        if debug >= 3:
            print "I have template info for these stations:"
            print template_stachan
            print "I have daylong data for these stations:"
            print data_stachan
    # Perform a check that the daylong vectors are daylong
    for tr in stream:
        if not tr.stats.sampling_rate * 86400 == tr.stats.npts:
            raise ValueError("Data are not daylong for " + tr.stats.station + "." + tr.stats.channel)
    # Call the _template_loop function to do all the correlation work
    outtic = time.clock()
    # Edit here from previous, stable, but slow match_filter
    # Would be worth testing without an if statement, but with every station in
    # the possible template stations having data, but for those without real
    # data make the data NaN to return NaN ccc_sum
    if debug >= 2:
        print "Ensuring all template channels have matches in daylong data"
    template_stachan = []
    for template in templates:
        for tr in template:
            template_stachan += [(tr.stats.station, tr.stats.channel)]
    template_stachan = list(set(template_stachan))
    # Copy this here to keep it safe
    for stachan in template_stachan:
        if not stream.select(station=stachan[0], channel=stachan[1]):
            # Add a trace of NaN's
            nulltrace = Trace()
            nulltrace.stats.station = stachan[0]
            nulltrace.stats.channel = stachan[1]
            nulltrace.stats.sampling_rate = stream[0].stats.sampling_rate
            nulltrace.stats.starttime = stream[0].stats.starttime
            nulltrace.data = np.array([np.NaN] * len(stream[0].data), dtype=np.float32)
            stream += nulltrace
    # Also pad out templates to have all channels
    for template in templates:
        for stachan in template_stachan:
            if not template.select(station=stachan[0], channel=stachan[1]):
                nulltrace = Trace()
                nulltrace.stats.station = stachan[0]
                nulltrace.stats.channel = stachan[1]
                nulltrace.stats.sampling_rate = template[0].stats.sampling_rate
                nulltrace.stats.starttime = template[0].stats.starttime
                nulltrace.data = np.array([np.NaN] * len(template[0].data), dtype=np.float32)
                template += nulltrace

    if debug >= 2:
        print "Starting the correlation run for this day"
    if match_internal:
        [cccsums, no_chans] = run_channel_loop(templates, stream, tempdir)
    else:
        [cccsums, no_chans] = _channel_loop(templates, stream, cores, debug)
    if len(cccsums[0]) == 0:
        raise ValueError("Correlation has not run, zero length cccsum")
    outtoc = time.clock()
    print "Looping over templates and streams took: " + str(outtoc - outtic) + " s"
    if debug >= 2:
        print "The shape of the returned cccsums is: " + str(np.shape(cccsums))
        print "This is from " + str(len(templates)) + " templates"
        print "Correlated with " + str(len(stream)) + " channels of data"
    i = 0
    detections = []
    for cccsum in cccsums:
        template = templates[i]
        if threshold_type == "MAD":
            rawthresh = threshold * np.median(np.abs(cccsum))
        elif threshold_type == "absolute":
            rawthresh = threshold
        elif threshold == "av_chan_corr":
            rawthresh = threshold * (cccsum / len(template))
        else:
            print "You have not selected the correct threshold type, I will use MAD as I like it"
            rawthresh = threshold * np.mean(np.abs(cccsum))
        # Findpeaks returns a list of tuples in the form [(cccsum, sample)]
        print "Threshold is set at: " + str(rawthresh)
        print "Max of data is: " + str(max(cccsum))
        print "Mean of data is: " + str(np.mean(cccsum))
        if np.abs(np.mean(cccsum)) > 0.05:
            warnings.warn("Mean is not zero!  Check this!")
        # Set up a trace object for the cccsum as this is easier to plot and
        # maintins timing
        if plotvar:
            stream_plot = copy.deepcopy(stream[0])
            # Downsample for plotting
            stream_plot.decimate(int(stream[0].stats.sampling_rate / 20))
            cccsum_plot = Trace(cccsum)
            cccsum_plot.stats.sampling_rate = stream[0].stats.sampling_rate
            # Resample here to maintain shape better
            cccsum_hist = cccsum_plot.copy()
            cccsum_hist = cccsum_hist.decimate(int(stream[0].stats.sampling_rate / 20)).data
            cccsum_plot = EQcorrscan_plotting.chunk_data(cccsum_plot, 20, "Maxabs").data
            # Enforce same length
            stream_plot.data = stream_plot.data[0 : len(cccsum_plot)]
            cccsum_plot = cccsum_plot[0 : len(stream_plot.data)]
            cccsum_hist = cccsum_hist[0 : len(stream_plot.data)]
            EQcorrscan_plotting.triple_plot(
                cccsum_plot,
                cccsum_hist,
                stream_plot,
                rawthresh,
                True,
                "plot/cccsum_plot_"
                + template_names[i]
                + "_"
                + str(stream[0].stats.starttime.year)
                + "-"
                + str(stream[0].stats.starttime.month)
                + "-"
                + str(stream[0].stats.starttime.day)
                + ".jpg",
            )
            np.save(template_names[i] + stream[0].stats.starttime.datetime.strftime("%Y%j"), cccsum)
        tic = time.clock()
        if debug >= 4:
            np.save("cccsum_" + str(i) + ".npy", cccsum)
        if debug >= 3 and max(cccsum) > rawthresh:
            peaks = findpeaks.find_peaks2_short(
                cccsum,
                rawthresh,
                trig_int * stream[0].stats.sampling_rate,
                debug,
                stream[0].stats.starttime,
                stream[0].stats.sampling_rate,
            )
        elif max(cccsum) > rawthresh:
            peaks = findpeaks.find_peaks2_short(cccsum, rawthresh, trig_int * stream[0].stats.sampling_rate, debug)
        else:
            print "No peaks found above threshold"
            peaks = False
        toc = time.clock()
        if debug >= 1:
            print "Finding peaks took: " + str(toc - tic) + " s"
        if peaks:
            for peak in peaks:
                detecttime = stream[0].stats.starttime + peak[1] / stream[0].stats.sampling_rate
                detections.append(DETECTION(template_names[i], detecttime, no_chans[i], peak[0], rawthresh, "corr"))
        i += 1

    return detections
Пример #17
0
def template_grid(stations,
                  nodes,
                  travel_times,
                  phase,
                  PS_ratio=1.68,
                  samp_rate=100,
                  flength=False,
                  phaseout='all'):
    """
    Generate a group of synthetic seismograms for a grid of sources.

    Used to simulate phase arrivals from a grid of known sources in a
    three-dimensional model.  Lags must be known and supplied, these can be
    generated from the bright_lights function: read_tt, and resampled to fit
    the desired grid dimensions and spacing using other functions therein.
    These synthetic seismograms are very simple models of seismograms using
    the seis_sim function herein. These approximate body-wave P and S first
    arrivals as spikes convolved with damped sine waves.

    :type stations: list
    :param stations: List of the station names
    :type nodes: list
    :param nodes: List of node locations in (lon,lat,depth)
    :type travel_times: numpy.ndarray
    :param travel_times: Array of travel times where travel_times[i][:] \
        refers to the travel times for station=stations[i], and \
        travel_times[i][j] refers to stations[i] for nodes[j]
    :type phase: str
    :param phase: Can be either 'P' or 'S'
    :type PS_ratio: float
    :param PS_ratio: P/S velocity ratio, defaults to 1.68
    :type samp_rate: float
    :param samp_rate: Desired sample rate in Hz, defaults to 100.0
    :type flength: int
    :param flength: Length of template in samples, defaults to False
    :type phaseout: str
    :param phaseout: Either 'S', 'P', 'all' or 'both', determines which \
        phases to clip around.  'all' Encompasses both phases in one channel, \
        but will return nothing if the flength is not long enough, 'both' \
        will return two channels for each stations, one SYN_Z with the \
        synthetic P-phase, and one SYN_H with the synthetic S-phase.

    :returns: List of :class:`obspy.core.stream.Stream`
    """
    if phase not in ['S', 'P']:
        raise IOError('Phase is neither P nor S')
    # Initialize empty list for templates
    templates = []
    # Loop through the nodes, for every node generate a template!
    for i, node in enumerate(nodes):
        st = []  # Empty list to be filled with synthetics
        # Loop through stations
        for j, station in enumerate(stations):
            tr = Trace()
            tr.stats.sampling_rate = samp_rate
            tr.stats.station = station
            tr.stats.channel = 'SYN'
            tt = travel_times[j][i]
            if phase == 'P':
                # If the input travel-time is the P-wave travel-time
                SP_time = (tt * PS_ratio) - tt
                if phaseout == 'S':
                    tr.stats.starttime += tt + SP_time
                else:
                    tr.stats.starttime += tt
            elif phase == 'S':
                # If the input travel-time is the S-wave travel-time
                SP_time = tt - (tt / PS_ratio)
                if phaseout == 'S':
                    tr.stats.starttime += tt
                else:
                    tr.stats.starttime += tt - SP_time
            else:
                raise IOError('Input grid is not P or S')
            # Set start-time of trace to be travel-time for P-wave
            # Check that the template length is long enough to include the SP
            if flength and SP_time * samp_rate < flength - 11 \
               and phaseout == 'all':
                tr.data = seis_sim(sp=int(SP_time * samp_rate),
                                   amp_ratio=1.5,
                                   flength=flength,
                                   phaseout=phaseout)
                st.append(tr)
            elif flength and phaseout == 'all':
                warnings.warn('Cannot make a bulk synthetic with this fixed ' +
                              'length for station ' + station)
            elif phaseout == 'all':
                tr.data = seis_sim(sp=int(SP_time * samp_rate),
                                   amp_ratio=1.5,
                                   flength=flength,
                                   phaseout=phaseout)
                st.append(tr)
            elif phaseout in ['P', 'S']:
                tr.data = seis_sim(sp=int(SP_time * samp_rate),
                                   amp_ratio=1.5,
                                   flength=flength,
                                   phaseout=phaseout)
                st.append(tr)
            elif phaseout == 'both':
                for _phaseout in ['P', 'S']:
                    _tr = tr.copy()
                    _tr.data = seis_sim(sp=int(SP_time * samp_rate),
                                        amp_ratio=1.5,
                                        flength=flength,
                                        phaseout=_phaseout)
                    if _phaseout == 'P':
                        _tr.stats.channel = 'SYN_Z'
                        # starttime defaults to S-time
                        _tr.stats.starttime = _tr.stats.starttime - SP_time
                    elif _phaseout == 'S':
                        _tr.stats.channel = 'SYN_H'
                    st.append(_tr)
        templates.append(Stream(st))
        # Stream(st).plot(size=(800,600))
    return templates
Пример #18
0
def match_filter(template_names,
                 template_list,
                 st,
                 threshold,
                 threshold_type,
                 trig_int,
                 plotvar,
                 plotdir='.',
                 cores=1,
                 tempdir=False,
                 debug=0,
                 plot_format='jpg'):
    r"""Over-arching code to run the correlations of given templates with a\
    day of seismic data and output the detections based on a given threshold.

    :type template_names: list
    :param template_names: List of template names in the same order as\
     template_list
    :type template_list: list :class: 'obspy.Stream'
    :param template_list: A list of templates of which each template is a\
        Stream of obspy traces containing seismic data and header information.
    :type st: :class: 'obspy.Stream'
    :param st: An obspy.Stream object containing all the data available and\
        required for the correlations with templates given.  For efficiency\
        this should contain no excess traces which are not in one or more of\
        the templates.  This will now remove excess traces internally, but\
        will copy the stream and work on the copy, leaving your input stream\
        untouched.
    :type threshold: float
    :param threshold: A threshold value set based on the threshold_type
    :type threshold_type: str
    :param threshold_type: The type of threshold to be used, can be MAD,\
        absolute or av_chan_corr.    MAD threshold is calculated as the\
        threshold*(median(abs(cccsum))) where cccsum is the cross-correlation\
        sum for a given template. absolute threhsold is a true absolute\
        threshold based on the cccsum value av_chan_corr is based on the mean\
        values of single-channel cross-correlations assuming all data are\
        present as required for the template, \
        e.g. av_chan_corr_thresh=threshold*(cccsum/len(template)) where\
        template is a single template from the input and the length is the\
        number of channels within this template.
    :type trig_int: float
    :param trig_int: Minimum gap between detections in seconds.
    :type plotvar: bool
    :param plotvar: Turn plotting on or off
    :type plotdir: str
    :param plotdir: Path to plotting folder, plots will be output here,\
        defaults to run location.
    :type tempdir: String or False
    :param tempdir: Directory to put temporary files, or False
    :type cores: int
    :param cores: Number of cores to use
    :type debug: int
    :param debug: Debug output level, the bigger the number, the more the\
        output.

    :return: :class: 'DETECTIONS' detections for each channel formatted as\
    :class: 'obspy.UTCDateTime' objects.

    .. rubric:: Note
        Plotting within the match-filter routine uses the Agg backend with\
        interactive plotting turned off.  This is because the function is\
        designed to work in bulk.  If you wish to turn interactive plotting on\
        you must import matplotlib in your script first, when you them import\
        match_filter you will get the warning that this call to matplotlib has\
        no effect, which will mean that match_filter has not changed the\
        plotting behaviour.
    """
    import matplotlib
    matplotlib.use('Agg')
    import matplotlib.pyplot as plt
    plt.ioff()
    import copy
    from eqcorrscan.utils import EQcorrscan_plotting
    from eqcorrscan.utils import findpeaks
    from obspy import Trace
    import time

    # Copy the stream here because we will f**k about with it
    stream = st.copy()
    templates = copy.deepcopy(template_list)
    # Debug option to confirm that the channel names match those in the
    # templates
    if debug >= 2:
        template_stachan = []
        data_stachan = []
        for template in templates:
            for tr in template:
                template_stachan.append(tr.stats.station + '.' +
                                        tr.stats.channel)
        for tr in stream:
            data_stachan.append(tr.stats.station + '.' + tr.stats.channel)
        template_stachan = list(set(template_stachan))
        data_stachan = list(set(data_stachan))
        if debug >= 3:
            print 'I have template info for these stations:'
            print template_stachan
            print 'I have daylong data for these stations:'
            print data_stachan
    # Perform a check that the daylong vectors are daylong
    for tr in stream:
        if not tr.stats.sampling_rate * 86400 == tr.stats.npts:
            msg = ' '.join([
                'Data are not daylong for', tr.stats.station, tr.stats.channel
            ])
            raise ValueError(msg)
    # Call the _template_loop function to do all the correlation work
    outtic = time.clock()
    # Edit here from previous, stable, but slow match_filter
    # Would be worth testing without an if statement, but with every station in
    # the possible template stations having data, but for those without real
    # data make the data NaN to return NaN ccc_sum
    # Note: this works
    if debug >= 2:
        print 'Ensuring all template channels have matches in daylong data'
    template_stachan = []
    for template in templates:
        for tr in template:
            template_stachan += [(tr.stats.station, tr.stats.channel)]
    template_stachan = list(set(template_stachan))
    # Copy this here to keep it safe
    for stachan in template_stachan:
        if not stream.select(station=stachan[0], channel=stachan[1]):
            # Remove template traces rather than adding NaN data
            for template in templates:
                if template.select(station=stachan[0], channel=stachan[1]):
                    for tr in template.select(station=stachan[0],
                                              channel=stachan[1]):
                        template.remove(tr)
    # Remove un-needed channels
    for tr in stream:
        if not (tr.stats.station, tr.stats.channel) in template_stachan:
            stream.remove(tr)
    # Also pad out templates to have all channels
    for template in templates:
        for stachan in template_stachan:
            if not template.select(station=stachan[0], channel=stachan[1]):
                nulltrace = Trace()
                nulltrace.stats.station = stachan[0]
                nulltrace.stats.channel = stachan[1]
                nulltrace.stats.sampling_rate = template[0].stats.sampling_rate
                nulltrace.stats.starttime = template[0].stats.starttime
                nulltrace.data = np.array([np.NaN] * len(template[0].data),
                                          dtype=np.float32)
                template += nulltrace
    if debug >= 2:
        print 'Starting the correlation run for this day'
    [cccsums, no_chans] = _channel_loop(templates, stream, cores, debug)
    if len(cccsums[0]) == 0:
        raise ValueError('Correlation has not run, zero length cccsum')
    outtoc = time.clock()
    print ' '.join([
        'Looping over templates and streams took:',
        str(outtoc - outtic), 's'
    ])
    if debug >= 2:
        print ' '.join(
            ['The shape of the returned cccsums is:',
             str(np.shape(cccsums))])
        print ' '.join(['This is from', str(len(templates)), 'templates'])
        print ' '.join(
            ['Correlated with',
             str(len(stream)), 'channels of data'])
    detections = []
    for i, cccsum in enumerate(cccsums):
        template = templates[i]
        if threshold_type == 'MAD':
            rawthresh = threshold * np.median(np.abs(cccsum))
        elif threshold_type == 'absolute':
            rawthresh = threshold
        elif threshold == 'av_chan_corr':
            rawthresh = threshold * (cccsum / len(template))
        else:
            print 'You have not selected the correct threshold type, I will' +\
                  'use MAD as I like it'
            rawthresh = threshold * np.mean(np.abs(cccsum))
        # Findpeaks returns a list of tuples in the form [(cccsum, sample)]
        print ' '.join(['Threshold is set at:', str(rawthresh)])
        print ' '.join(['Max of data is:', str(max(cccsum))])
        print ' '.join(['Mean of data is:', str(np.mean(cccsum))])
        if np.abs(np.mean(cccsum)) > 0.05:
            warnings.warn('Mean is not zero!  Check this!')
        # Set up a trace object for the cccsum as this is easier to plot and
        # maintins timing
        if plotvar:
            stream_plot = copy.deepcopy(stream[0])
            # Downsample for plotting
            stream_plot.decimate(int(stream[0].stats.sampling_rate / 10))
            cccsum_plot = Trace(cccsum)
            cccsum_plot.stats.sampling_rate = stream[0].stats.sampling_rate
            # Resample here to maintain shape better
            cccsum_hist = cccsum_plot.copy()
            cccsum_hist = cccsum_hist.decimate(
                int(stream[0].stats.sampling_rate / 10)).data
            cccsum_plot = EQcorrscan_plotting.chunk_data(
                cccsum_plot, 10, 'Maxabs').data
            # Enforce same length
            stream_plot.data = stream_plot.data[0:len(cccsum_plot)]
            cccsum_plot = cccsum_plot[0:len(stream_plot.data)]
            cccsum_hist = cccsum_hist[0:len(stream_plot.data)]
            EQcorrscan_plotting.triple_plot(
                cccsum_plot, cccsum_hist, stream_plot, rawthresh, True,
                plotdir + '/cccsum_plot_' + template_names[i] + '_' +
                stream[0].stats.starttime.datetime.strftime('%Y-%m-%d') + '.' +
                plot_format)
            if debug >= 4:
                print ' '.join([
                    'Saved the cccsum to:', template_names[i],
                    stream[0].stats.starttime.datetime.strftime('%Y%j')
                ])
                np.save(
                    template_names[i] +
                    stream[0].stats.starttime.datetime.strftime('%Y%j'),
                    cccsum)
        tic = time.clock()
        if debug >= 4:
            np.save('cccsum_' + str(i) + '.npy', cccsum)
        if debug >= 3 and max(cccsum) > rawthresh:
            peaks = findpeaks.find_peaks2_short(
                cccsum, rawthresh, trig_int * stream[0].stats.sampling_rate,
                debug, stream[0].stats.starttime,
                stream[0].stats.sampling_rate)
        elif max(cccsum) > rawthresh:
            peaks = findpeaks.find_peaks2_short(
                cccsum, rawthresh, trig_int * stream[0].stats.sampling_rate,
                debug)
        else:
            print 'No peaks found above threshold'
            peaks = False
        toc = time.clock()
        if debug >= 1:
            print ' '.join(['Finding peaks took:', str(toc - tic), 's'])
        if peaks:
            for peak in peaks:
                detecttime = stream[0].stats.starttime +\
                    peak[1] / stream[0].stats.sampling_rate
                detections.append(
                    DETECTION(template_names[i], detecttime, no_chans[i],
                              peak[0], rawthresh, 'corr'))
    del stream, templates
    return detections
Пример #19
0
# testing the test:
# j,data = l2_simple(c_syn,c_obs)

# left hand side of test 1: adjt source time function * du = change of misfit wrt u
djdc = np.dot(data,d_c) 

#if mtype in ['envelope','square_envelope']:
#	djdc = np.dot(data[0],d_c)
	#djdc += np.dot(data[1],d_c)

# right hand side of test 1: Finite difference approx of misfit change for different steps


dcheck = []
d_ch = c_syn.copy()


for step in steps:
	d_ch.data = c_ini + 10. ** step * d_c
	msr_sh = m_func(d_ch,**m_a_options)
	if mtype == 'energy_diff':	
		msr_sh = msr_sh[0] + msr_sh[1]

	jh = 0.5 * (msr_sh - msr_o)**2
	if mtype in ['windowed_waveform','envelope','square_envelope']:
		jh = 0.5 * np.sum(np.power((msr_sh-msr_o),2))
	# testing the test:
	# jh, dn = l2_simple(d_ch,c_obs)
	djdch = (jh - j) / (10.**step) 
	dcheck.append(abs(djdc - djdch) / abs(djdc))
Пример #20
0
def slant_stack(eq_num, plot_scale_fac = 0.05, slowR_lo = -0.1, slowR_hi = 0.1, stack_option = 1,
            slow_delta = 0.0005, start_buff = -50, end_buff = 50,
            ref_lat = 36.3, ref_lon = 138.5, ref_loc = 0, envelope = 1, plot_dyn_range = 1000,
            log_plot = 1, norm = 1, global_norm_plot = 1, color_plot = 1, fig_index = 401, ARRAY = 0):

#%% Import functions
    import obspy
    import obspy.signal
    from obspy import UTCDateTime
    from obspy import Stream, Trace
    from obspy import read
    from obspy.geodetics import gps2dist_azimuth
    import numpy as np
    import os
    from obspy.taup import TauPyModel
    import obspy.signal as sign
    import matplotlib.pyplot as plt
    from matplotlib.colors import LogNorm
    model = TauPyModel(model='iasp91')
    from scipy.signal import hilbert
    import math
    import time
    from termcolor import colored

    env_stack = 0  # flag to stack envelopes instead of oscillating seismograms

#    import sys # don't show any warnings
#    import warnings

    print(colored('Running pro5a_stack', 'cyan'))

#%% Get saved event info, also used to name files
    start_time_wc = time.time()

    fname = '/Users/vidale/Documents/Research/IC/EvLocs/event' + str(eq_num) + '.txt'
    file = open(fname, 'r')
    lines=file.readlines()

    split_line = lines[0].split()
#            ids.append(split_line[0])  ignore label for now
    t           = UTCDateTime(split_line[1])
    date_label  = split_line[1][0:10]
    ev_lat      = float(      split_line[2])
    ev_lon      = float(      split_line[3])
    ev_depth    = float(      split_line[4])

    #if not sys.warnoptions:
    #    warnings.simplefilter("ignore")

#%% Get station location file
    if ARRAY == 0: # Hinet set and center
        sta_file = '/Users/vidale/Documents/GitHub/Array_codes/Files/sta_hinet.txt'
        if ref_loc == 0:
            ref_lat = 36.3
            ref_lon = 138.5
    elif ARRAY == 1: # LASA set and center
        sta_file = '/Users/vidale/Documents/GitHub/Array_codes/Files/sta_LASA.txt'
        if ref_loc == 0:
            ref_lat = 46.69
            ref_lon = -106.22
    elif ARRAY == 2: # China set and center
        sta_file = '/Users/vidale/Documents/GitHub/Array_codes/Files/sta_ch.txt'
        if ref_loc == 0:
            ref_lat = 38      # °N
            ref_lon = 104.5   # °E
    else:         # NORSAR set and center
        sta_file = '/Users/vidale/Documents/GitHub/Array_codes/Files/sta_NORSAR.txt'
        if ref_loc == 0:
            ref_lat = 61
            ref_lon = 11
    with open(sta_file, 'r') as file:
        lines = file.readlines()
    print('    ' + str(len(lines)) + ' stations of metadata read from ' + sta_file)
    # Load station coords into arrays
    station_index = range(len(lines))
    st_names = []
    st_lats  = []
    st_lons  = []
    for ii in station_index:
        line = lines[ii]
        split_line = line.split()
        st_names.append(split_line[0])
        st_lats.append( split_line[1])
        st_lons.append( split_line[2])
    if ARRAY == 0:  # shorten and make upper case Hi-net station names to match station list
        for ii in station_index:
            this_name = st_names[ii]
            this_name_truc = this_name[0:5]
            st_names[ii]  = this_name_truc.upper()

#%% Name file, read data
    # date_label = '2018-04-02' # date for filename
    fname = 'HD' + date_label + 'sel.mseed'
    goto = '/Users/vidale/Documents/Research/IC/Pro_Files'
    os.chdir(goto)

    # fname = '/Users/vidale/Documents/PyCode/Pro_Files/HD' + date_label + 'sel.mseed'

    st = Stream()
    print('        reading ' + fname)
    print('        Stack option is ' + str(stack_option))
    st = read(fname)
    print('    ' + str(len(st)) + ' traces read in')
    nt = len(st[0].data)
    dt = st[0].stats.delta
    print(f'        First trace has {nt} time pts, time sampling of {dt:.2f} and thus duration of {(nt-1)*dt:.0f} and max amp of {max(abs(st[0].data)):.1f}')
    print(f'st[0].stats.starttime-t {(st[0].stats.starttime-t):.2f} start_buff {start_buff:.2f}')

#%% Build Stack arrays
    stack = Stream()
    tr = Trace()
    tr.stats.delta = dt
    tr.stats.network = 'stack'
    tr.stats.channel = 'BHZ'
    slow_n = int(1 + (slowR_hi - slowR_lo)/slow_delta)  # number of slownesses
    stack_nt = int(1 + ((end_buff - start_buff)/dt))  # number of time points
    # In English, stack_slows = range(slow_n) * slow_delta - slowR_lo
    a1 = range(slow_n)
    stack_slows = [(x * slow_delta + slowR_lo) for x in a1]
    print('        ' + str(slow_n) + ' slownesses.')
    tr.stats.starttime = t + start_buff
    # print(f'tr.stats.starttime-t {(tr.stats.starttime-t):.2f} start_buff {start_buff:.2f}')
    tr.data = np.zeros(stack_nt)
    done = 0
    for stack_one in stack_slows:
        tr1 = tr.copy()
        tr1.stats.station = str(int(done))
        stack.extend([tr1])
        done += 1
    #    stack.append([tr])
    #    stack += tr

    #  Only need to compute ref location to event distance once
    ref_distance = gps2dist_azimuth(ev_lat,ev_lon,ref_lat,ref_lon)

#%% Select traces by distance, window and adjust start time to align picked times
    done = 0
    if env_stack == 1: #convert oscillating seismograms to envelopes
        for tr in st:
            tr.data = np.abs(hilbert(tr.data))

    for tr in st: # traces one by one
        if tr.stats.station in st_names:  # find station in station list
            ii = st_names.index(tr.stats.station)
            if norm == 1:
                tr.normalize()
            stalat = float(st_lats[ii])
            stalon = float(st_lons[ii]) # look up lat & lon again to find distance
            distance = gps2dist_azimuth(stalat,stalon,ev_lat,ev_lon) # Get traveltimes again, hard to store
            tr.stats.distance=distance[0] # distance in m
            del_dist = (ref_distance[0] - distance[0])/(1000) # in km
            rel_start_buff = tr.stats.starttime - (t + start_buff)
            print(f'{tr.stats.station} del_dist {del_dist:.2f} ref_dist {ref_distance[0]/1000.:.2f} distance {distance[0]/1000.:.2f} rel_start_buff {rel_start_buff:.2f} tr.stats.starttime-t {(tr.stats.starttime-t):.2f} start_buff {start_buff:.2f}')

            for slow_i in range(slow_n):  # for this station, loop over slownesses
                time_lag = -del_dist * stack_slows[slow_i]  # time shift due to slowness, flipped to match 2D
                time_correction = (rel_start_buff + time_lag)/dt
                # print(f'{slow_i} time_lag {time_lag:.1f} time correction {time_correction:.1f}')

                if stack_option == 0:
                    for it in range(stack_nt):  # check points one at a time
                        it_in = int(it + time_correction)
                        if it_in >= 0 and it_in < nt - 1: # does data lie within seismogram?
                            stack[slow_i].data[it] += tr[it_in]

                if stack_option == 1:
                    arr = tr.data
                    nshift = int(time_correction)
                    if time_correction < 0:
                        nshift = nshift-1
                    if nshift <= 0:
                        nbeg1 = -nshift
                        nend1 = stack_nt
                        nbeg2 = 0
                        nend2 = stack_nt + nshift;
                    elif nshift > 0:
                        nbeg1 = 0
                        nend1 = stack_nt - nshift
                        nbeg2 = nshift
                        nend2 = stack_nt
                    if nend1 >= 0 and nbeg1 <= stack_nt:
                        stack[slow_i].data[nbeg1:nend1] += arr[nbeg2:nend2]

            done += 1
            if done % 50 == 0:
                print('        Done stacking ' + str(done) + ' out of ' + str(len(st)) + ' stations.')
        else:
            print(tr.stats.station + ' not found in station list')

#%% Plot traces
    global_max = 0
    for slow_i in range(slow_n): # find global max, and if requested, take envelope
        if len(stack[slow_i].data) == 0:
                print('%d data has zero length ' % (slow_i))
        if envelope == 1 or color_plot == 1:
            stack[slow_i].data = np.abs(hilbert(stack[slow_i].data))
        local_max = max(abs(stack[slow_i].data))
        if local_max > global_max:
            global_max = local_max
    if global_max <= 0:
        print('        global_max ' + str(global_max) + ' slow_n ' + str(slow_n))

    # create time axis (x-axis), use of slow_i here is arbitrary, oops
    ttt = (np.arange(len(stack[slow_i].data)) * stack[slow_i].stats.delta +
         (stack[slow_i].stats.starttime - t)) # in units of seconds

    # Plotting
    if color_plot == 1: # 2D color plot
        stack_array = np.zeros((slow_n,stack_nt))

    #    stack_array = np.random.rand(int(slow_n),int(stack_nt))  # test with random numbers
        min_allowed = global_max/plot_dyn_range
        if log_plot == 1:
            for it in range(stack_nt):  # check points one at a time
                for slow_i in range(slow_n):  # for this station, loop over slownesses
                    num_val = stack[slow_i].data[it]
                    if num_val < min_allowed:
                        num_val = min_allowed
                    stack_array[slow_i, it] = math.log10(num_val) - math.log10(min_allowed)
        else:
            for it in range(stack_nt):  # check points one at a time
                for slow_i in range(slow_n):  # for this station, loop over slownesses
                    stack_array[slow_i, it] = stack[slow_i].data[it]/global_max
        y, x = np.mgrid[slice(stack_slows[0], stack_slows[-1] + slow_delta, slow_delta),
                     slice(ttt[0], ttt[-1] + dt, dt)]  # make underlying x-y grid for plot
    #    y, x = np.mgrid[ stack_slows , time ]  # make underlying x-y grid for plot
        plt.close(fig_index)

        fig, ax = plt.subplots(1, figsize=(9,9))
        fig.subplots_adjust(bottom=0.3)
        c = ax.pcolormesh(x, y, stack_array, cmap=plt.cm.gist_rainbow_r)
        # c = ax.pcolormesh(x, y, stack_array, cmap=plt.cm.gist_yarg)
        # c = ax.pcolormesh(x, y, stack_array, cmap=plt.cm.binary)
        ax.axis([x.min(), x.max(), y.min(), y.max()])
        if log_plot == 1:
            fig.colorbar(c, ax=ax, label='log amplitude')
        else:
            fig.colorbar(c, ax=ax, label='linear amplitude')
        plt.figure(fig_index,figsize=(6,8))
        plt.close(fig_index)
    else: # line plot
        for slow_i in range(slow_n):
            dist_offset = stack_slows[slow_i] # in units of slowness
            if global_norm_plot != 1:
                plt.plot(ttt, stack[slow_i].data*plot_scale_fac / (stack[slow_i].data.max()
            - stack[slow_i].data.min()) + dist_offset, color = 'black')
            else:
                plt.plot(ttt, stack[slow_i].data*plot_scale_fac / (global_max
            - stack[slow_i].data.min()) + dist_offset, color = 'black')
        plt.ylim(slowR_lo,slowR_hi)
        plt.xlim(start_buff,end_buff)
    plt.xlabel('Time (s)')
    plt.ylabel('Slowness (s/km)')
    plt.title('1Dstack   ' + str(eq_num) + '  ' + date_label)
    # os.chdir('/Users/vidale/Documents/PyCode/Plots')
    # plt.savefig(date_label + '_' + str(start_buff) + '_' + str(end_buff) + '_1D.png')
    plt.show()

#%% Save processed files
    print('        Stack has ' + str(len(stack)) + ' slownesses')
#
#    if ARRAY == 0:
#        goto = '/Users/vidale/Documents/PyCode/Hinet'
#    if ARRAY == 1:
#        goto = '/Users/vidale/Documents/PyCode/LASA/Pro_Files'
#    os.chdir(goto)
#    fname = 'HD' + date_label + '_1dstack.mseed'
#    stack.write(fname,format = 'MSEED')

    elapsed_time_wc = time.time() - start_time_wc
    print(f'    This job took   {elapsed_time_wc:.1f}   seconds')
    os.system('say "Done"')