Пример #1
0
    def test_linstack(self):
        """Test the utils.stacking.linstack function."""
        # Generate synth data
        import numpy as np
        from obspy import Stream, Trace

        synth = Stream(Trace())
        synth[0].data = np.zeros(200)
        synth[0].data[100] = 1.0
        sine_x = np.arange(0, 10.0, 0.5)
        damped_sine = np.exp(-sine_x) * np.sin(2 * np.pi * sine_x)
        synth[0].data = np.convolve(synth[0].data, damped_sine)
        # Normalize:
        synth[0].data = synth[0].data / synth[0].data.max()
        maximum_synth = synth[0].data.max()
        RMS_max = np.sqrt(np.mean(np.square(synth[0].data)))
        streams = [synth.copy() for i in range(10)]

        stack = linstack(streams, normalize=True)
        # Check normalized amplitude is correct
        self.assertEqual(np.float32(stack[0].data.max()),
                         np.float32(10 * maximum_synth / RMS_max))
        stack = linstack(streams, normalize=False)
        # Check amplitude is preserved
        self.assertEqual(stack[0].data.max(), 10 * maximum_synth)
        # Check length is preserved
        self.assertEqual(len(synth[0].data), len(stack[0].data))
def main(data_dir, output_dir):

    # Get borehole geode info
    stats = pd.read_csv("walkaround_borehole_orientation_stats.csv")

    # Loop over stations/geodes
    for sta in ['BH001', 'BH002', 'BH004', 'BH005', 'BH006', 'BH008', 'BH011', 'BH012', 'BH013', 'BH014', 'BH015', 'BH016', 'BH017', 'BH018']:  # geodes.Geode:

        print("Processing station %s" % sta)
        # Rotation matrix for correction
        inc_corr = - stats.loc[stats['geode'] == sta, 'inclination'].values[0]
        baz_corr = - stats.loc[stats['geode'] == sta, 'mean'].values[0]

        # Read data
        flist1 = glob(os.path.join(data_dir, "8O.%s..DP1*" % sta))
        flist2 = glob(os.path.join(data_dir, "8O.%s..DP2*" % sta))
        flist3 = glob(os.path.join(data_dir, "8O.%s..DP3*" % sta))
        if not flist1 or not flist2 or not flist3:
            continue
            
        # Look for suffix
        file = os.path.split(flist1[0])[1]
        if file.find("unit") != -1:
            idx = file.find("unit")
            suffix = "_" + file[idx:].split(".")[0]
        else:
            suffix = ""
        
        st = Stream()
        st += read(os.path.join(data_dir, "8O.%s..DP1*" % sta), format="SAC")[0]
        st += read(os.path.join(data_dir, "8O.%s..DP2*" % sta), format="SAC")[0]
        st += read(os.path.join(data_dir, "8O.%s..DP3*" % sta), format="SAC")[0]
        
        if len(st) == 0:
            continue
            
        st.resample(500.0)
        starttime = st[0].stats.starttime
        endtime = st[0].stats.endtime

        # Save rotated coordinates
        st3 = st.copy()
        compN, compE, compZ = rotate_to_zne(st3[0].data, st3[1].data, st3[2].data, inc_corr, baz_corr)
        trN = SACTrace.from_obspy_trace(st3[0])
        trN.data = compN
        trN.kcmpnm = "DPN"
        trN.write(os.path.join(output_dir,
                               "8O.%s..DPN.%s_%s%s.sac" % (sta, starttime.strftime("%Y%m%d%H%M%S"), endtime.strftime("%Y%m%d%H%M%S"), suffix)))
        trE = SACTrace.from_obspy_trace(st3[1])
        trE.data = compE
        trE.kcmpnm = "DPE"
        trE.write(os.path.join(output_dir,
                               "8O.%s..DPE.%s_%s%s.sac" % (sta, starttime.strftime("%Y%m%d%H%M%S"), endtime.strftime("%Y%m%d%H%M%S"), suffix)))
        trZ = SACTrace.from_obspy_trace(st3[2])
        trZ.data = compZ
        trZ.kcmpnm = "DPZ"
        trZ.write(os.path.join(output_dir,
                               "8O.%s..DPZ.%s_%s%s.sac" % (sta, starttime.strftime("%Y%m%d%H%M%S"), endtime.strftime("%Y%m%d%H%M%S"), suffix)))
Пример #3
0
def get_test_data():
    """
    Generate a set of waveforms from GeoNet for use in subspace testing

    :return: List of cut templates with no filters applied
    :rtype: list
    """
    from http.client import IncompleteRead
    from obspy import UTCDateTime
    from eqcorrscan.utils.catalog_utils import filter_picks
    from eqcorrscan.utils.clustering import catalog_cluster
    from obspy.clients.fdsn import Client

    client = Client("GEONET")
    cat = client.get_events(minlatitude=-40.98,
                            maxlatitude=-40.85,
                            minlongitude=175.4,
                            maxlongitude=175.5,
                            starttime=UTCDateTime(2016, 5, 11),
                            endtime=UTCDateTime(2016, 5, 13))
    cat = filter_picks(catalog=cat, top_n_picks=5)
    stachans = list(
        set([(pick.waveform_id.station_code, pick.waveform_id.channel_code)
             for event in cat for pick in event.picks]))
    clusters = catalog_cluster(catalog=cat,
                               thresh=2,
                               show=False,
                               metric='distance')
    cluster = sorted(clusters, key=lambda c: len(c))[-1]
    client = Client('GEONET')
    design_set = []
    st = Stream()
    for event in cluster:
        # This print is just in to force some output during long running test
        print("Downloading for event {0}".format(event.resource_id))
        bulk_info = []
        t1 = event.origins[0].time + 5
        t2 = t1 + 15.1
        for station, channel in stachans:
            bulk_info.append(('NZ', station, '10', channel[0:2] + '?', t1, t2))
        st += client.get_waveforms_bulk(bulk=bulk_info)
    for event in cluster:
        t1 = event.origins[0].time + 5
        t2 = t1 + 15
        design_set.append(st.copy().trim(t1, t2))
    t1 = UTCDateTime(2016, 5, 11, 19)
    t2 = UTCDateTime(2016, 5, 11, 20)
    bulk_info = [('NZ', stachan[0], '10', stachan[1][0:2] + '?', t1, t2)
                 for stachan in stachans]
    st = Stream()
    for _bulk in bulk_info:
        try:
            st += client.get_waveforms(*_bulk)
        except IncompleteRead:
            print(f"Could not download {_bulk}")
    st.merge().detrend('simple').trim(starttime=t1, endtime=t2)
    return design_set, st
Пример #4
0
    def setup_stream(self, stream: Stream, freqmin=3., freqmax=20., **kwargs):
        """
        Setup the stream to be analyzed by the CNN. It must contain 3 channels.
            This method will perform a trim, detrend and bandpass filter to the Stream. If
            sample rate is not 100 Hz it will also interpolate it to 100 Hz.

        :param stream: An obspy.Stream with 3 channels inside.
        :param freqmin: Minimum frequency for the bandpass filter. Default=3Hz, filter
            should be set True for this to have an effect.
        :param freqmax: Maximum frequency for the bandpass filter. Default=20Hz, filter
            should be set True for this to have an effect.

        :keyword:
        :keyword resample: Interpolate Stream to self.resample_freq. Default=True. This will
            interpolate to a default frequency of 100Hz. Set to False if you want to avoid it.
            However, it's highly recommend to resample to 100Hz since the CNN is better
            trained for this sample rate.
        :keyword filter: It will apply a bandpass filter using freqmin and freqmax. Default=True.
        :keyword detrend: Detrend stream using linear. Default=True.
        :keyword trim: Trim stream usigin latest_start and earliest_stop from tracers. Default=True.
        :keyword copy: If true it will copy the stream otherwise the
            original stream will be modify. Default = True. Set False if you not gonna use the
            stream after.

        :return:
        """

        copy_stream = kwargs.get("copy", True)
        trim_stream = kwargs.get("trim", True)
        detrend_stream = kwargs.get("detrend", True)
        filter_stream = kwargs.get("filter", True)
        resample_stream = kwargs.get("resample", True)

        self.__stream = stream.copy(
        ) if copy_stream else stream  # use a copy of stream by default.
        self.validate_stream()
        self.__stream.sort(['channel'])

        if trim_stream:
            latest_start = np.amax(
                [tr.stats.starttime for tr in self.__stream])
            earliest_stop = np.amin([tr.stats.endtime for tr in self.__stream])
            self.__stream.trim(latest_start, earliest_stop)

        if detrend_stream:
            self.__stream.detrend(type='linear')

        # Filter data plays a big role on predict.
        if filter_stream:
            self.__stream.filter(type='bandpass',
                                 freqmin=freqmin,
                                 freqmax=freqmax)

        if resample_stream:
            # Works better if interpolate.
            # self.__stream.resample(100)  # TODO test re sample. it gets more events.
            self.__stream.interpolate(self.resample_freq)
Пример #5
0
def trim_event_stream(
    stream: Stream,
    merge: Optional[int] = 1,
    copy: bool = True,
    trim_tolerance=None,
    required_len: Optional[float] = 0.95,
):
    """
    Trim the waveforms to a common start time and end time.

    Uses latest start and earliest end for each trace, unless an abnormally
    short trace is found.

    Parameters
    ----------
    stream : obspy.Stream
        The waveforms to trim
    merge : int, optional
        If not None, merge the waveforms with this method before trimming. See
        obspy waveforms docs for merge options
    copy : bool
        Copy the waveforms before altering it.
    trim_tolerance:
        The number of seconds the trimmed starttime or endtime can vary from
        the starttime/endtime in the current traces.
    required_len
        The fraction of the longest len a trace must have to be considered
        good. If any trace is shorter remove it.

    Returns
    -------
    stream
        The merged waveforms
    """
    if copy:
        stream = stream.copy()
    if merge is not None:  # merge
        stream.merge(method=merge)
        stream = stream.split()
    # get a dataframe of start/end/duration and trace
    data = [(tr.stats.starttime.timestamp, tr.stats.endtime.timestamp, tr)
            for tr in stream]
    df = pd.DataFrame(data, columns=["start", "end", "trace"])
    df["duration"] = df.end - df.start
    # get start and end times
    stream = _trim_stream(df, stream, required_len, trim_tolerance)
    # ensure each channel has exactly one trace or merge to create masked
    if not len(stream) == len({tr.id for tr in stream}):
        stream.merge(method=merge)
    return stream
Пример #6
0
def _stack(path, **kwargs):

    files = glob.glob(path + "/*")
    # stations = glob.glob(path + "/*")
    # print(stations)

    # for sta in stations:
    #     files = glob.glob(sta + "/*")

    st = Stream()
    for file in files:
        try:
            tr = read(file)[0]
        except:
            continue
        st.append(tr)

    # get channels
    channels = []
    for tr in st:
        chn = tr.stats.channel
        if chn not in channels:
            channels.append(chn)

    method = kwargs["stack"]["method"]
    power = kwargs["stack"]["power"]
    outpath = kwargs["io"]["outpath"] + "/1a_stack"
    try:
        os.makedirs(outpath)
    except:
        pass

    stream = st.copy()
    for chn in channels:

        st = stream.select(channel=chn)

        if method == "linear":
            tr = linear_stack(st, normalize=True)
        elif method == "PWS":
            tr = pws_stack(st, power, normalize=True)
        elif method == "bootstrap_linear" or method == "bootstrap_PWS":
            # note tr here is a stream containing two traces, mean and std
            tr = _bootstrap(st, normalize=True, **kwargs)

        filen = outpath + "/" + tr.id + "_%s.pkl" % method
        tr.write(filename=filen, format="PICKLE")

    return 0
Пример #7
0
 def test_align(self):
     """Check that alignment does as expected."""
     test_stream = Stream(read()[0])
     # Shift it
     length = 15
     st1 = test_stream.copy().trim(test_stream[0].stats.starttime + 3,
                                   test_stream[0].stats.starttime +
                                   3 + length)
     st2 = test_stream.trim(test_stream[0].stats.starttime,
                            test_stream[0].stats.starttime + length)
     aligned = subspace.align_design(design_set=[st1.copy(), st2.copy()],
                                     shift_len=5, reject=0.3,
                                     multiplex=False, plot=False)
     self.assertEqual(aligned[0][0].stats.starttime,
                      aligned[1][0].stats.starttime)
Пример #8
0
 def generate_G_matrix(
     self,
     st_GF: obspy.Stream,
     az: float,
     comp: str,
     slice: bool = False,
     tt: float = None,
     t_pre: float = None,
     t_post: float = None,
 ):
     st_in = st_GF.copy()
     if slice:
         st_in.trim(
             starttime=self.or_time + tt - t_pre,
             endtime=self.or_time + tt + t_post,
         )
     G = _GreensFunctions.from_GF_get_G(st_in, az, comp)
     return G
Пример #9
0
def get_waveforms(
    stream: Stream,
    network: str = "*",
    station: str = "*",
    location: str = "*",
    channel: str = "*",
    starttime: Optional[UTC] = None,
    endtime: Optional[UTC] = None,
):
    """
    A subset of the Client.get_waveforms method.

    Simply makes successive calls to Stream.select and Stream.trim under the
    hood. Matching is available on all str parameters.

    Parameters
    ----------
    network
        The network code
    station
        The station code
    location
        Location code
    channel
        Channel code
    starttime
        Starttime for query
    endtime
        Endtime for query

    Returns
    -------
    Stream
    """
    stream = stream.copy()
    st = stream.select(network=network,
                       station=station,
                       location=location,
                       channel=channel)
    st = st.trim(starttime=UTC(starttime or SMALL_UTC),
                 endtime=UTC(endtime or BIG_UTC))
    return st
Пример #10
0
    def test_phase_weighted_stack(self):
        """Test the utils.stacking.PWS_stack."""
        # Generate synth data
        import numpy as np
        from obspy import Stream, Trace

        synth = Stream(Trace())
        synth[0].data = np.zeros(200)
        synth[0].data[100] = 1.0
        sine_x = np.arange(0, 10.0, 0.5)
        damped_sine = np.exp(-sine_x) * np.sin(2 * np.pi * sine_x)
        synth[0].data = np.convolve(synth[0].data, damped_sine)
        # Normalize:
        synth[0].data = synth[0].data / synth[0].data.max()
        # maximum_synth = synth[0].data.max()
        # RMS_max = np.sqrt(np.mean(np.square(synth[0].data)))
        streams = [synth.copy() for i in range(10)]

        stack = PWS_stack(streams, weight=2, normalize=True)
        # Check length is preserved
        self.assertEqual(len(synth[0].data), len(stack[0].data))
Пример #11
0
    def pre_picking_processing(self):
        # read Afile
        self.st, self.FirstStn, self.fileHeader = self.unpackAfile(self.A_File)

        # remove station not in nsta24 database
        self.checkNonDBsta(self.st, self.DB.db_nsta)

        # rotate OBS stations
        if not self.EEW_Mode:
            OBS_rotate(self.st, Config_File=self.config)

        # remove station in DONT_USE_LIST
        self.remove_sta_not_use(self.st,
                                checklist=self.config['Misc']['CheckList_sta'])

        # remove station in addon block list
        self.remove_addon_block_list(
            self.st, blocklist=self.config['Misc']['Addon_BlockList'])

        # remove stations is outside velocty model
        self.Remove_Stn_Outside_Grid(self.st, nsta_db=self.DB.db_nsta)

        # de-mean without zeros
        self.demean_wo_zero()

        # copy trace for intensity use
        st_intensity_tmp = Stream()
        st_intensity_tmp += self.st.select(channel='Ch1')
        st_intensity_tmp += self.st.select(channel='Ch2')
        st_intensity_tmp += self.st.select(channel='Ch3')
        self.st_intensity = st_intensity_tmp.copy()

        # copy streams for calculate Magnitude
        self.stmag = self.st.copy()

        # remove no data Streams
        self.checkZeroGap_new(self.st)

        return self.st, self.st_intensity, self.stmag, self.FirstStn, self.fileHeader
Пример #12
0
 def _process_streams(self,
                      stream,
                      pre_processed,
                      process_cores=1,
                      parallel=False,
                      ignore_bad_data=False,
                      select_used_chans=True):
     """
     Process a stream based on the template parameters.
     """
     if select_used_chans:
         template_stream = Stream()
         for tr in self.template.st:
             template_stream += stream.select(network=tr.stats.network,
                                              station=tr.stats.station,
                                              location=tr.stats.location,
                                              channel=tr.stats.channel)
     else:
         template_stream = stream
     if not pre_processed:
         processed_streams = _group_process(template_group=[self.template],
                                            cores=process_cores,
                                            parallel=parallel,
                                            stream=template_stream.copy(),
                                            daylong=False,
                                            ignore_length=False,
                                            overlap=0.0,
                                            ignore_bad_data=ignore_bad_data)
         processed_stream = Stream()
         for p in processed_streams:
             processed_stream += p
         processed_stream.merge(method=1)
         Logger.debug(processed_stream)
     else:
         processed_stream = stream.merge()
     return processed_stream
Пример #13
0
 def test_coincidenceTriggerWithSimilarityChecking(self):
     """
     Test network coincidence trigger with cross correlation similarity
     checking of given event templates.
     """
     st = Stream()
     files = ["BW.UH1._.SHZ.D.2010.147.cut.slist.gz",
              "BW.UH2._.SHZ.D.2010.147.cut.slist.gz",
              "BW.UH3._.SHZ.D.2010.147.cut.slist.gz",
              "BW.UH3._.SHN.D.2010.147.cut.slist.gz",
              "BW.UH3._.SHE.D.2010.147.cut.slist.gz",
              "BW.UH4._.EHZ.D.2010.147.cut.slist.gz"]
     for filename in files:
         filename = os.path.join(self.path, filename)
         st += read(filename)
     # some prefiltering used for UH network
     st.filter('bandpass', freqmin=10, freqmax=20)
     # set up template event streams
     times = ["2010-05-27T16:24:33.095000", "2010-05-27T16:27:30.370000"]
     templ = {}
     for t in times:
         t = UTCDateTime(t)
         st_ = st.select(station="UH3").slice(t, t + 2.5).copy()
         templ.setdefault("UH3", []).append(st_)
     times = ["2010-05-27T16:27:30.574999"]
     for t in times:
         t = UTCDateTime(t)
         st_ = st.select(station="UH1").slice(t, t + 2.5).copy()
         templ.setdefault("UH1", []).append(st_)
     trace_ids = {"BW.UH1..SHZ": 1,
                  "BW.UH2..SHZ": 1,
                  "BW.UH3..SHZ": 1,
                  "BW.UH4..EHZ": 1}
     similarity_thresholds = {"UH1": 0.8, "UH3": 0.7}
     with warnings.catch_warnings(record=True) as w:
         # avoid getting influenced by the warning filters getting set up
         # differently in obspy-runtests.
         # (e.g. depending on options "-v" and "-q")
         warnings.resetwarnings()
         trig = coincidence_trigger(
             "classicstalta", 5, 1, st.copy(), 4, sta=0.5, lta=10,
             trace_ids=trace_ids, event_templates=templ,
             similarity_threshold=similarity_thresholds)
         # two warnings get raised
         self.assertEqual(len(w), 2)
     # check floats in resulting dictionary separately
     self.assertAlmostEqual(trig[0].pop('duration'), 3.9600000381469727)
     self.assertAlmostEqual(trig[1].pop('duration'), 1.9900000095367432)
     self.assertAlmostEqual(trig[2].pop('duration'), 1.9200000762939453)
     self.assertAlmostEqual(trig[3].pop('duration'), 3.9200000762939453)
     self.assertAlmostEqual(trig[0]['similarity'].pop('UH1'), 0.94149447384)
     self.assertAlmostEqual(trig[0]['similarity'].pop('UH3'), 1)
     self.assertAlmostEqual(trig[1]['similarity'].pop('UH1'), 0.65228204570)
     self.assertAlmostEqual(trig[1]['similarity'].pop('UH3'), 0.72679293429)
     self.assertAlmostEqual(trig[2]['similarity'].pop('UH1'), 0.89404458774)
     self.assertAlmostEqual(trig[2]['similarity'].pop('UH3'), 0.74581409371)
     self.assertAlmostEqual(trig[3]['similarity'].pop('UH1'), 1)
     self.assertAlmostEqual(trig[3]['similarity'].pop('UH3'), 1)
     remaining_results = \
         [{'coincidence_sum': 4.0,
           'similarity': {},
           'stations': ['UH3', 'UH2', 'UH1', 'UH4'],
           'time': UTCDateTime(2010, 5, 27, 16, 24, 33, 210000),
           'trace_ids': ['BW.UH3..SHZ', 'BW.UH2..SHZ', 'BW.UH1..SHZ',
                         'BW.UH4..EHZ']},
          {'coincidence_sum': 3.0,
           'similarity': {},
           'stations': ['UH3', 'UH1', 'UH2'],
           'time': UTCDateTime(2010, 5, 27, 16, 25, 26, 710000),
           'trace_ids': ['BW.UH3..SHZ', 'BW.UH1..SHZ', 'BW.UH2..SHZ']},
          {'coincidence_sum': 3.0,
           'similarity': {},
           'stations': ['UH2', 'UH1', 'UH3'],
           'time': UTCDateTime(2010, 5, 27, 16, 27, 2, 260000),
           'trace_ids': ['BW.UH2..SHZ', 'BW.UH1..SHZ', 'BW.UH3..SHZ']},
          {'coincidence_sum': 4.0,
           'similarity': {},
           'stations': ['UH3', 'UH2', 'UH1', 'UH4'],
           'time': UTCDateTime(2010, 5, 27, 16, 27, 30, 510000),
           'trace_ids': ['BW.UH3..SHZ', 'BW.UH2..SHZ', 'BW.UH1..SHZ',
                         'BW.UH4..EHZ']}]
     self.assertEqual(trig, remaining_results)
Пример #14
0
    def read_waveform_data(self, starttime, endtime, pre_pad=0., post_pad=0.):
        """
        Read in waveform data from the archive between two times.

        Supports all formats currently supported by ObsPy, including: "MSEED",
        "SAC", "SEGY", "GSE2" .

        Optionally, read data with some pre- and post-pad, and for all stations
        in the archive - this will be stored in `data.raw_waveforms`, while
        `data.waveforms` will contain only data for selected stations between
        `starttime` and `endtime`.

        Parameters
        ----------
        starttime : `obspy.UTCDateTime` object
            Timestamp from which to read waveform data.
        endtime : `obspy.UTCDateTime` object
            Timestamp up to which to read waveform data.
        pre_pad : float, optional
            Additional pre pad of data to read. Defaults to 0.
        post_pad : float, optional
            Additional post pad of data to read. Defaults to 0.

        Returns
        -------
        data : :class:`~quakemigrate.io.data.WaveformData` object
            Object containing the waveform data read from the archive that
            satisfies the query.

        """

        # Ensure pre-pad and post-pad are not negative.
        pre_pad = max(0., pre_pad)
        post_pad = max(0., post_pad)

        data = WaveformData(starttime=starttime,
                            endtime=endtime,
                            stations=self.stations,
                            read_all_stations=self.read_all_stations,
                            resample=self.resample,
                            upfactor=self.upfactor,
                            response_inv=self.response_inv,
                            water_level=self.water_level,
                            pre_filt=self.pre_filt,
                            remove_full_response=self.remove_full_response,
                            pre_pad=pre_pad,
                            post_pad=post_pad)

        files = self._load_from_path(starttime - pre_pad, endtime + post_pad)

        st = Stream()
        try:
            first = next(files)
            files = chain([first], files)
            for file in files:
                file = str(file)
                try:
                    read_start = starttime - pre_pad
                    read_end = endtime + post_pad
                    st += read(file,
                               starttime=read_start,
                               endtime=read_end,
                               nearest_sample=True)
                except TypeError:
                    logging.info(f"File not compatible with ObsPy - {file}")
                    continue

            # Merge all traces with contiguous data, or overlapping data which
            # exactly matches (== st._cleanup(); i.e. no clobber)
            st.merge(method=-1)

            # Make copy of raw waveforms to output if requested
            data.raw_waveforms = st.copy()

            # Ensure data is timestamped "on-sample" (i.e. an integer number
            # of samples after midnight). Otherwise the data will be implicitly
            # shifted when it is used to calculate the onset function /
            # migrated.
            st = util.shift_to_sample(st, interpolate=self.interpolate)

            if self.read_all_stations:
                # Re-populate st with only stations in station file
                st_selected = Stream()
                for station in self.stations:
                    st_selected += st.select(station=station)
                st = st_selected.copy()
                del st_selected

            if pre_pad != 0. or post_pad != 0.:
                # Trim data between start and end time
                for tr in st:
                    tr.trim(starttime=starttime,
                            endtime=endtime,
                            nearest_sample=True)
                    if not bool(tr):
                        st.remove(tr)

            # Test if the stream is completely empty
            # (see __nonzero__ for `obspy.Stream` object)
            if not bool(st):
                raise util.DataGapException

            # Add cleaned stream to `waveforms`
            data.waveforms = st

        except StopIteration:
            raise util.ArchiveEmptyException

        return data
Пример #15
0
lines=file.readlines()
split_line = lines[0].split()
t2           = UTCDateTime(split_line[1])
date_label2  = split_line[1][0:10]

fname1  = 'HD' + date_label1 + '_' + date_label2 + '_tshift.mseed'
tshift  = Stream()
tshift  = read(fname1)
fname1  = 'HD' + date_label1 + '_' + date_label2 + '_amp_ratio.mseed'
amp_ratio  = Stream()
amp_ratio  = read(fname1)
fname2  = 'HD' + date_label1 + '_' + date_label2 + '_amp_ave.mseed'
amp_ave = Stream()
amp_ave = read(fname2)

tshift_full = tshift.copy()
tshift.decimate(decimate_fac)
amp_ratio.decimate(decimate_fac)
amp_ave.decimate(decimate_fac)

#print(f'len(tshift): ' {len(tshift):4d} 'len(tshift[0].data): ' {len(tshift[0].data:4d})
print(f'len(tshift): {len(tshift):4d}')
print(f'len(amp_ave): {len(amp_ave):4d}')
print(f'len(amp_ratio): {len(amp_ratio):4d}')

nt = len(tshift[0].data)
dt = tshift[0].stats.delta
print(f'nt: {nt:6d}')

#%% Make grid of slownesses
slowR_n = int(1 + (slowR_hi - slowR_lo)/slow_delta)  # number of slownesses
Пример #16
0
    for master in detections:
        keep = True
        for slave in detections:
            if not master == slave and abs(master.detect_time - slave.detect_time) <= 1.0:
                # If the events are within 1s of each other then test which
                # was the 'best' match, strongest detection
                if not master.detect_val > slave.detect_val:
                    keep = False
                    break
        if keep:
            unique_detections.append(master)

print("We made a total of " + str(len(unique_detections)) + " detections")

for detection in unique_detections:
    print(
        "Detection at :"
        + str(detection.detect_time)
        + " for template "
        + detection.template_name
        + " with a cross-correlation sum of: "
        + str(detection.detect_val)
    )
    # We can plot these too
    stplot = st.copy()
    template = templates[template_names.index(detection.template_name)]
    lags = sorted([tr.stats.starttime for tr in template])
    maxlag = lags[-1] - lags[0]
    stplot.trim(starttime=detection.detect_time - 10, endtime=detection.detect_time + maxlag + 10)
    plotting.detection_multiplot(stplot, template, [detection.detect_time.datetime])
Пример #17
0
class WaveformPlotting(object):
    """
    Class that provides several solutions for plotting large and small waveform
    data sets.

    .. warning::

        This class should NOT be used directly, instead use the
        :meth:`~obspy.core.stream.Stream.plot` method of the
        ObsPy :class:`~obspy.core.stream.Stream` or
        :class:`~obspy.core.trace.Trace` objects.

    It uses matplotlib to plot the waveforms.
    """

    def __init__(self, **kwargs):
        """
        Checks some variables and maps the kwargs to class variables.
        """
        self.kwargs = kwargs
        self.stream = kwargs.get('stream')
        # Check if it is a Stream or a Trace object.
        if isinstance(self.stream, Trace):
            self.stream = Stream([self.stream])
        elif not isinstance(self.stream, Stream):
            msg = 'Plotting is only supported for Stream or Trace objects.'
            raise TypeError(msg)
        # Stream object should contain at least one Trace
        if len(self.stream) < 1:
            msg = "Empty stream object"
            raise IndexError(msg)
        self.stream = self.stream.copy()
        # Type of the plot.
        self.type = kwargs.get('type', 'normal')
        # Start- and endtimes of the plots.
        self.starttime = kwargs.get('starttime', None)
        self.endtime = kwargs.get('endtime', None)
        self.fig_obj = kwargs.get('fig', None)
        # If no times are given take the min/max values from the stream object.
        if not self.starttime:
            self.starttime = min([trace.stats.starttime for trace in
                                  self.stream])
        if not self.endtime:
            self.endtime = max([trace.stats.endtime for trace in self.stream])
        # Map stream object and slice just in case.
        self.stream.trim(self.starttime, self.endtime)
        # normalize times
        if self.type == 'relative':
            dt = self.starttime
            # fix plotting boundaries
            self.endtime = UTCDateTime(self.endtime - self.starttime)
            self.starttime = UTCDateTime(0)
            # fix stream times
            for tr in self.stream:
                tr.stats.starttime = UTCDateTime(tr.stats.starttime - dt)
        # Whether to use straight plotting or the fast minmax method.
        self.plotting_method = kwargs.get('method', 'fast')
        # Below that value the data points will be plotted normally. Above it
        # the data will be plotted using a different approach (details see
        # below). Can be overwritten by the above self.plotting_method kwarg.
        self.max_npts = 400000
        # If automerge is enabled. Merge traces with the same id for the plot.
        self.automerge = kwargs.get('automerge', True)
        # If equal_scale is enabled all plots are equally scaled.
        self.equal_scale = kwargs.get('equal_scale', True)
        # Set default values.
        # The default value for the size is determined dynamically because
        # there might be more than one channel to plot.
        self.size = kwargs.get('size', None)
        # Values that will be used to calculate the size of the plot.
        self.default_width = 800
        self.default_height_per_channel = 250
        if not self.size:
            self.width = 800
            # Check the kind of plot.
            if self.type == 'dayplot':
                self.height = 600
            else:
                # One plot for each trace.
                if self.automerge:
                    count = self.__getMergablesIds()
                    count = len(count)
                else:
                    count = len(self.stream)
                self.height = count * 250
        else:
            self.width, self.height = self.size
        # Interval length in minutes for dayplot.
        self.interval = 60 * kwargs.get('interval', 15)
        # Scaling.
        self.vertical_scaling_range = kwargs.get('vertical_scaling_range',
                                                 None)
        # Dots per inch of the plot. Might be useful for printing plots.
        self.dpi = kwargs.get('dpi', 100)
        # Color of the graph.
        if self.type == 'dayplot':
            self.color = kwargs.get('color', ('#B2000F', '#004C12', '#847200',
                                              '#0E01FF'))
            if isinstance(self.color, basestring):
                self.color = (self.color,)
            self.number_of_ticks = kwargs.get('number_of_ticks', None)
        else:
            self.color = kwargs.get('color', 'k')
            self.number_of_ticks = kwargs.get('number_of_ticks', 4)
        # Background, face  and grid color.
        self.background_color = kwargs.get('bgcolor', 'w')
        self.face_color = kwargs.get('face_color', 'w')
        self.grid_color = kwargs.get('grid_color', 'black')
        self.grid_linewidth = kwargs.get('grid_linewidth', 0.5)
        self.grid_linestyle = kwargs.get('grid_linestyle', ':')
        # Transparency. Overwrites background and facecolor settings.
        self.transparent = kwargs.get('transparent', False)
        if self.transparent:
            self.background_color = None
        # Ticks.
        self.tick_format = kwargs.get('tick_format', '%H:%M:%S')
        self.tick_rotation = kwargs.get('tick_rotation', 0)
        # Whether or not to save a file.
        self.outfile = kwargs.get('outfile')
        self.handle = kwargs.get('handle')
        # File format of the resulting file. Usually defaults to PNG but might
        # be dependent on your matplotlib backend.
        self.format = kwargs.get('format')
        self.show = kwargs.get('show', True)
        self.block = kwargs.get('block', True)
        # plot parameters options
        self.x_labels_size = kwargs.get('x_labels_size', 8)
        self.y_labels_size = kwargs.get('y_labels_size', 8)
        self.title_size = kwargs.get('title_size', 10)
        self.linewidth = kwargs.get('linewidth', 0.4)
        self.linestyle = kwargs.get('linestyle', '-')
        self.subplots_adjust_left = kwargs.get('subplots_adjust_left', 0.12)
        self.subplots_adjust_right = kwargs.get('subplots_adjust_right', 0.88)
        self.subplots_adjust_top = kwargs.get('subplots_adjust_top', 0.95)
        self.subplots_adjust_bottom = kwargs.get('subplots_adjust_bottom', 0.1)
        self.right_vertical_labels = kwargs.get('right_vertical_labels', False)
        self.one_tick_per_line = kwargs.get('one_tick_per_line', False)
        self.show_y_UTC_label = kwargs.get('show_y_UTC_label', True)
        self.title = kwargs.get('title', self.stream[0].id)

    def __del__(self):
        """
        Destructor closes the figure instance if it has been created by the
        class.
        """
        if self.kwargs.get("fig", None) is None:
            plt.close()

    def __getMergeId(self, tr):
        tr_id = tr.id
        # don't merge normal traces with previews
        try:
            if tr.stats.preview:
                tr_id += 'preview'
        except KeyError:
            pass
        # don't merge traces with different processing steps
        try:
            if tr.stats.processing:
                tr_id += str(tr.stats.processing)
        except KeyError:
            pass
        return tr_id

    def __getMergablesIds(self):
        ids = []
        for tr in self.stream:
            tr_id = self.__getMergeId(tr)
            if not tr_id in ids:
                ids.append(tr_id)
        return ids

    def plotWaveform(self, *args, **kwargs):
        """
        Creates a graph of any given ObsPy Stream object. It either saves the
        image directly to the file system or returns an binary image string.

        For all color values you can use legit HTML names, HTML hex strings
        (e.g. '#eeefff') or you can pass an R , G , B tuple, where each of
        R , G , B are in the range [0, 1]. You can also use single letters for
        basic built-in colors ('b' = blue, 'g' = green, 'r' = red, 'c' = cyan,
        'm' = magenta, 'y' = yellow, 'k' = black, 'w' = white) and gray shades
        can be given as a string encoding a float in the 0-1 range.
        """
        # Setup the figure if not passed explicitly.
        if not self.fig_obj:
            self.__setupFigure()
        else:
            self.fig = self.fig_obj
        # Determine kind of plot and do the actual plotting.
        if self.type == 'dayplot':
            self.plotDay(*args, **kwargs)
        else:
            self.plot(*args, **kwargs)
        # Adjust the subplot so there is always a fixed margin on every side
        if self.type != 'dayplot':
            fract_y = 60.0 / self.height
            fract_y2 = 40.0 / self.height
            fract_x = 80.0 / self.width
            self.fig.subplots_adjust(top=1.0 - fract_y, bottom=fract_y2,
                                     left=fract_x, right=1.0 - fract_x / 2)
        self.fig.canvas.draw()
        # The following just serves as a unified way of saving and displaying
        # the plots.
        if not self.transparent:
            extra_args = {'dpi': self.dpi,
                          'facecolor': self.face_color,
                          'edgecolor': self.face_color}
        else:
            extra_args = {'dpi': self.dpi,
                          'transparent': self.transparent}
        if self.outfile:
            # If format is set use it.
            if self.format:
                self.fig.savefig(self.outfile, format=self.format,
                                 **extra_args)
            # Otherwise use format from self.outfile or default to PNG.
            else:
                self.fig.savefig(self.outfile, **extra_args)
        else:
            # Return an binary imagestring if not self.outfile but self.format.
            if self.format:
                imgdata = StringIO.StringIO()
                self.fig.savefig(imgdata, format=self.format,
                                 **extra_args)
                imgdata.seek(0)
                return imgdata.read()
            elif self.handle:
                return self.fig
            else:
                if not self.fig_obj and self.show:
                    try:
                        plt.show(block=self.block)
                    except:
                        plt.show()

    def plot(self, *args, **kwargs):
        """
        Plot the Traces showing one graph per Trace.

        Plots the whole time series for self.max_npts points and less. For more
        points it plots minmax values.
        """
        stream_new = []
        # Just remove empty traces.
        if not self.automerge:
            for tr in self.stream:
                stream_new.append([])
                if len(tr.data):
                    stream_new[-1].append(tr)
        else:
            # Generate sorted list of traces (no copy)
            # Sort order, id, starttime, endtime
            ids = self.__getMergablesIds()
            for id in ids:
                stream_new.append([])
                for tr in self.stream:
                    tr_id = self.__getMergeId(tr)
                    if tr_id == id:
                        # does not copy the elements of the data array
                        tr_ref = copy(tr)
                        # Trim does nothing if times are outside
                        if self.starttime >= tr_ref.stats.endtime or \
                                self.endtime <= tr_ref.stats.starttime:
                            continue
                        if tr_ref.data.size:
                            stream_new[-1].append(tr_ref)
                # delete if empty list
                if not len(stream_new[-1]):
                    stream_new.pop()
                    continue
                stream_new[-1].sort(key=lambda x: x.stats.endtime)
                stream_new[-1].sort(key=lambda x: x.stats.starttime)
        # If everything is lost in the process raise an Exception.
        if not len(stream_new):
            raise Exception("Nothing to plot")
        # Create helper variable to track ids and min/max/mean values.
        self.stats = []
        # Loop over each Trace and call the appropriate plotting method.
        self.axis = []
        for _i, tr in enumerate(stream_new):
            # Each trace needs to have the same sampling rate.
            sampling_rates = set([_tr.stats.sampling_rate for _tr in tr])
            if len(sampling_rates) > 1:
                msg = "All traces with the same id need to have the same " + \
                      "sampling rate."
                raise Exception(msg)
            sampling_rate = sampling_rates.pop()
            if self.background_color:
                ax = self.fig.add_subplot(len(stream_new), 1, _i + 1,
                                          axisbg=self.background_color)
            else:
                ax = self.fig.add_subplot(len(stream_new), 1, _i + 1)
            self.axis.append(ax)
            # XXX: Also enable the minmax plotting for previews.
            if self.plotting_method != 'full' and \
                ((self.endtime - self.starttime) * sampling_rate >
                 self.max_npts):
                self.__plotMinMax(stream_new[_i], ax, *args, **kwargs)
            else:
                self.__plotStraight(stream_new[_i], ax, *args, **kwargs)
        # Set ticks.
        self.__plotSetXTicks()
        self.__plotSetYTicks()

    @deprecated_keywords({'swap_time_axis': None})
    def plotDay(self, *args, **kwargs):
        """
        Extend the seismogram.
        """
        # Merge and trim to pad.
        self.stream.merge()
        if len(self.stream) != 1:
            msg = "All traces need to be of the same id for a dayplot"
            raise ValueError(msg)
        self.stream.trim(self.starttime, self.endtime, pad=True)
        # Get minmax array.
        self.__dayplotGetMinMaxValues(self, *args, **kwargs)
        # Normalize array
        self.__dayplotNormalizeValues(self, *args, **kwargs)
        # Get timezone information. If none is  given, use local time.
        self.time_offset = kwargs.get('time_offset',
            round((UTCDateTime(datetime.now()) - UTCDateTime()) / 3600.0, 2))
        self.timezone = kwargs.get('timezone', 'local time')
        # Try to guess how many steps are needed to advance one full time unit.
        self.repeat = None
        intervals = self.extreme_values.shape[0]
        if self.interval < 60 and 60 % self.interval == 0:
            self.repeat = 60 / self.interval
        elif self.interval < 1800 and 3600 % self.interval == 0:
            self.repeat = 3600 / self.interval
        # Otherwise use a maximum value of 10.
        else:
            if intervals >= 10:
                self.repeat = 10
            else:
                self.repeat = intervals
        # Create axis to plot on.
        if self.background_color:
            ax = self.fig.add_subplot(1, 1, 1, axisbg=self.background_color)
        else:
            ax = self.fig.add_subplot(1, 1, 1)
        # Adjust the subplots
        self.fig.subplots_adjust(left=self.subplots_adjust_left,
                                 right=self.subplots_adjust_right,
                                 top=self.subplots_adjust_top,
                                 bottom=self.subplots_adjust_bottom)
        # Create x_value_array.
        aranged_array = np.arange(self.width)
        x_values = np.empty(2 * self.width)
        x_values[0::2] = aranged_array
        x_values[1::2] = aranged_array
        intervals = self.extreme_values.shape[0]
        # Loop over each step.
        for _i in xrange(intervals):
            # Create offset array.
            y_values = np.ma.empty(self.width * 2)
            y_values.fill(intervals - (_i + 1))
            # Add min and max values.
            y_values[0::2] += self.extreme_values[_i, :, 0]
            y_values[1::2] += self.extreme_values[_i, :, 1]
            # Plot the values.
            ax.plot(x_values, y_values,
                    color=self.color[_i % len(self.color)],
                    linewidth=self.linewidth, linestyle=self.linestyle)
        # Plot the scale, if required.
        scale_unit = kwargs.get("data_unit", None)
        if scale_unit is not None:
            self._plotDayplotScale(unit=scale_unit)
        # Set ranges.
        ax.set_xlim(0, self.width - 1)
        ax.set_ylim(-0.3, intervals + 0.3)
        self.axis = [ax]
        # Set ticks.
        self.__dayplotSetYTicks(*args, **kwargs)
        self.__dayplotSetXTicks(*args, **kwargs)
        # Choose to show grid but only on the x axis.
        self.fig.axes[0].grid(color=self.grid_color,
                              linestyle=self.grid_linestyle,
                              linewidth=self.grid_linewidth)
        self.fig.axes[0].yaxis.grid(False)
        # Set the title of the plot.
        self.fig.suptitle(self.title, fontsize=self.title_size)
        # Now try to plot some events.
        events = kwargs.get("events", [])
        # Potentially download some events with the help of obspy.neries.
        if "min_magnitude" in events:
            try:
                from obspy.neries import Client
                c = Client()
                events = c.getEvents(min_datetime=self.starttime,
                                     max_datetime=self.endtime,
                                     format="catalog",
                                     min_magnitude=events["min_magnitude"])
            except Exception, e:
                msg = "Could not download the events because of '%s: %s'." % \
                    (e.__class__.__name__, e.message)
                warnings.warn(msg)
        if events:
            for event in events:
                self._plotEvent(event)
Пример #18
0
 def test_coincidence_trigger_with_similarity_checking(self):
     """
     Test network coincidence trigger with cross correlation similarity
     checking of given event templates.
     """
     st = Stream()
     files = ["BW.UH1._.SHZ.D.2010.147.cut.slist.gz",
              "BW.UH2._.SHZ.D.2010.147.cut.slist.gz",
              "BW.UH3._.SHZ.D.2010.147.cut.slist.gz",
              "BW.UH3._.SHN.D.2010.147.cut.slist.gz",
              "BW.UH3._.SHE.D.2010.147.cut.slist.gz",
              "BW.UH4._.EHZ.D.2010.147.cut.slist.gz"]
     for filename in files:
         filename = os.path.join(self.path, filename)
         st += read(filename)
     # some prefiltering used for UH network
     st.filter('bandpass', freqmin=10, freqmax=20)
     # set up template event streams
     times = ["2010-05-27T16:24:33.095000", "2010-05-27T16:27:30.370000"]
     templ = {}
     for t in times:
         t = UTCDateTime(t)
         st_ = st.select(station="UH3").slice(t, t + 2.5).copy()
         templ.setdefault("UH3", []).append(st_)
     times = ["2010-05-27T16:27:30.574999"]
     for t in times:
         t = UTCDateTime(t)
         st_ = st.select(station="UH1").slice(t, t + 2.5).copy()
         templ.setdefault("UH1", []).append(st_)
     # add another template with different SEED ID, it should be ignored
     # (this can happen when using many templates over a long time period
     # and instrument changes over time)
     st_ = st_.copy()
     for tr in st_:
         tr.stats.channel = 'X' + tr.stats.channel[1:]
     templ['UH1'].insert(0, st_)
     trace_ids = {"BW.UH1..SHZ": 1,
                  "BW.UH2..SHZ": 1,
                  "BW.UH3..SHZ": 1,
                  "BW.UH4..EHZ": 1}
     similarity_thresholds = {"UH1": 0.8, "UH3": 0.7}
     with warnings.catch_warnings(record=True) as w:
         # avoid getting influenced by the warning filters getting set up
         # differently in obspy-runtests.
         # (e.g. depending on options "-v" and "-q")
         warnings.resetwarnings()
         trig = coincidence_trigger(
             "classicstalta", 5, 1, st.copy(), 4, sta=0.5, lta=10,
             trace_ids=trace_ids, event_templates=templ,
             similarity_threshold=similarity_thresholds)
     # four warnings get raised
     self.assertEqual(len(w), 4)
     self.assertEqual(
         str(w[0].message),
         "At least one trace's ID was not found in the trace ID list and "
         "was disregarded (BW.UH3..SHN)")
     self.assertEqual(
         str(w[1].message),
         "At least one trace's ID was not found in the trace ID list and "
         "was disregarded (BW.UH3..SHE)")
     self.assertEqual(
         str(w[2].message),
         'Skipping trace BW.UH1..XHZ in template correlation (not present '
         'in stream to check).')
     self.assertEqual(
         str(w[3].message),
         "Skipping template(s) for station 'UH1': No common SEED IDs when "
         "comparing template (BW.UH1..XHZ) and data streams (BW.UH1..SHZ, "
         "BW.UH2..SHZ, BW.UH3..SHE, BW.UH3..SHN, BW.UH3..SHZ, "
         "BW.UH4..EHZ).")
     # check floats in resulting dictionary separately
     self.assertAlmostEqual(trig[0].pop('duration'), 3.96, places=6)
     self.assertAlmostEqual(trig[1].pop('duration'), 1.99, places=6)
     self.assertAlmostEqual(trig[2].pop('duration'), 1.92, places=6)
     self.assertAlmostEqual(trig[3].pop('duration'), 3.92, places=6)
     self.assertAlmostEqual(trig[0]['similarity'].pop('UH1'),
                            0.94149447384, places=6)
     self.assertAlmostEqual(trig[0]['similarity'].pop('UH3'), 1,
                            places=6)
     self.assertAlmostEqual(trig[1]['similarity'].pop('UH1'),
                            0.65228204570, places=6)
     self.assertAlmostEqual(trig[1]['similarity'].pop('UH3'),
                            0.72679293429, places=6)
     self.assertAlmostEqual(trig[2]['similarity'].pop('UH1'),
                            0.89404458774, places=6)
     self.assertAlmostEqual(trig[2]['similarity'].pop('UH3'),
                            0.74581409371, places=6)
     self.assertAlmostEqual(trig[3]['similarity'].pop('UH1'), 1,
                            places=6)
     self.assertAlmostEqual(trig[3]['similarity'].pop('UH3'), 1,
                            places=6)
     remaining_results = \
         [{'coincidence_sum': 4.0,
           'similarity': {},
           'stations': ['UH3', 'UH2', 'UH1', 'UH4'],
           'time': UTCDateTime(2010, 5, 27, 16, 24, 33, 210000),
           'trace_ids': ['BW.UH3..SHZ', 'BW.UH2..SHZ', 'BW.UH1..SHZ',
                         'BW.UH4..EHZ']},
          {'coincidence_sum': 3.0,
           'similarity': {},
           'stations': ['UH3', 'UH1', 'UH2'],
           'time': UTCDateTime(2010, 5, 27, 16, 25, 26, 710000),
           'trace_ids': ['BW.UH3..SHZ', 'BW.UH1..SHZ', 'BW.UH2..SHZ']},
          {'coincidence_sum': 3.0,
           'similarity': {},
           'stations': ['UH2', 'UH1', 'UH3'],
           'time': UTCDateTime(2010, 5, 27, 16, 27, 2, 260000),
           'trace_ids': ['BW.UH2..SHZ', 'BW.UH1..SHZ', 'BW.UH3..SHZ']},
          {'coincidence_sum': 4.0,
           'similarity': {},
           'stations': ['UH3', 'UH2', 'UH1', 'UH4'],
           'time': UTCDateTime(2010, 5, 27, 16, 27, 30, 510000),
           'trace_ids': ['BW.UH3..SHZ', 'BW.UH2..SHZ', 'BW.UH1..SHZ',
                         'BW.UH4..EHZ']}]
     self.assertEqual(trig, remaining_results)
Пример #19
0
 def test_coincidenceTriggerWithSimilarityChecking(self):
     """
     Test network coincidence trigger with cross correlation similarity
     checking of given event templates.
     """
     st = Stream()
     files = [
         "BW.UH1._.SHZ.D.2010.147.cut.slist.gz",
         "BW.UH2._.SHZ.D.2010.147.cut.slist.gz",
         "BW.UH3._.SHZ.D.2010.147.cut.slist.gz",
         "BW.UH3._.SHN.D.2010.147.cut.slist.gz",
         "BW.UH3._.SHE.D.2010.147.cut.slist.gz",
         "BW.UH4._.EHZ.D.2010.147.cut.slist.gz"
     ]
     for filename in files:
         filename = os.path.join(self.path, filename)
         st += read(filename)
     # some prefiltering used for UH network
     st.filter('bandpass', freqmin=10, freqmax=20)
     # set up template event streams
     times = ["2010-05-27T16:24:33.095000", "2010-05-27T16:27:30.370000"]
     templ = {}
     for t in times:
         t = UTCDateTime(t)
         st_ = st.select(station="UH3").slice(t, t + 2.5).copy()
         templ.setdefault("UH3", []).append(st_)
     times = ["2010-05-27T16:27:30.574999"]
     for t in times:
         t = UTCDateTime(t)
         st_ = st.select(station="UH1").slice(t, t + 2.5).copy()
         templ.setdefault("UH1", []).append(st_)
     trace_ids = {
         "BW.UH1..SHZ": 1,
         "BW.UH2..SHZ": 1,
         "BW.UH3..SHZ": 1,
         "BW.UH4..EHZ": 1
     }
     similarity_thresholds = {"UH1": 0.8, "UH3": 0.7}
     trig = coincidenceTrigger("classicstalta",
                               5,
                               1,
                               st.copy(),
                               4,
                               sta=0.5,
                               lta=10,
                               trace_ids=trace_ids,
                               event_templates=templ,
                               similarity_threshold=similarity_thresholds)
     # check floats in resulting dictionary separately
     self.assertAlmostEqual(trig[0].pop('duration'), 3.9600000381469727)
     self.assertAlmostEqual(trig[1].pop('duration'), 1.9900000095367432)
     self.assertAlmostEqual(trig[2].pop('duration'), 1.9200000762939453)
     self.assertAlmostEqual(trig[3].pop('duration'), 3.9200000762939453)
     self.assertAlmostEqual(trig[0]['similarity'].pop('UH1'), 0.94149447384)
     self.assertAlmostEqual(trig[0]['similarity'].pop('UH3'), 1)
     self.assertAlmostEqual(trig[1]['similarity'].pop('UH1'), 0.65228204570)
     self.assertAlmostEqual(trig[1]['similarity'].pop('UH3'), 0.72679293429)
     self.assertAlmostEqual(trig[2]['similarity'].pop('UH1'), 0.89404458774)
     self.assertAlmostEqual(trig[2]['similarity'].pop('UH3'), 0.74581409371)
     self.assertAlmostEqual(trig[3]['similarity'].pop('UH1'), 1)
     self.assertAlmostEqual(trig[3]['similarity'].pop('UH3'), 1)
     remaining_results = \
         [{'coincidence_sum': 4.0,
           'similarity': {},
           'stations': ['UH3', 'UH2', 'UH1', 'UH4'],
           'time': UTCDateTime(2010, 5, 27, 16, 24, 33, 210000),
           'trace_ids': ['BW.UH3..SHZ', 'BW.UH2..SHZ', 'BW.UH1..SHZ',
                         'BW.UH4..EHZ']},
          {'coincidence_sum': 3.0,
           'similarity': {},
           'stations': ['UH3', 'UH1', 'UH2'],
           'time': UTCDateTime(2010, 5, 27, 16, 25, 26, 710000),
           'trace_ids': ['BW.UH3..SHZ', 'BW.UH1..SHZ', 'BW.UH2..SHZ']},
          {'coincidence_sum': 3.0,
           'similarity': {},
           'stations': ['UH2', 'UH1', 'UH3'],
           'time': UTCDateTime(2010, 5, 27, 16, 27, 2, 260000),
           'trace_ids': ['BW.UH2..SHZ', 'BW.UH1..SHZ', 'BW.UH3..SHZ']},
          {'coincidence_sum': 4.0,
           'similarity': {},
           'stations': ['UH3', 'UH2', 'UH1', 'UH4'],
           'time': UTCDateTime(2010, 5, 27, 16, 27, 30, 510000),
           'trace_ids': ['BW.UH3..SHZ', 'BW.UH2..SHZ', 'BW.UH1..SHZ',
                         'BW.UH4..EHZ']}]
     self.assertTrue(trig == remaining_results)
Пример #20
0
def run_tutorial(plot=False,
                 multiplex=True,
                 return_streams=False,
                 cores=4,
                 verbose=False):
    """
    Run the tutorial.

    :return: detections
    """
    client = Client("GEONET", debug=verbose)
    cat = client.get_events(minlatitude=-40.98,
                            maxlatitude=-40.85,
                            minlongitude=175.4,
                            maxlongitude=175.5,
                            starttime=UTCDateTime(2016, 5, 1),
                            endtime=UTCDateTime(2016, 5, 20))
    print(f"Downloaded a catalog of {len(cat)} events")
    # This gives us a catalog of events - it takes a while to download all
    # the information, so give it a bit!
    # We will generate a five station, multi-channel detector.
    cat = filter_picks(catalog=cat, top_n_picks=5)
    stachans = list(
        set([(pick.waveform_id.station_code, pick.waveform_id.channel_code)
             for event in cat for pick in event.picks]))
    # In this tutorial we will only work on one cluster, defined spatially.
    # You can work on multiple clusters, or try to whole set.
    clusters = catalog_cluster(catalog=cat,
                               metric="distance",
                               thresh=2,
                               show=False)
    # We will work on the largest cluster
    cluster = sorted(clusters, key=lambda c: len(c))[-1]
    # This cluster contains 32 events, we will now download and trim the
    # waveforms.  Note that each chanel must start at the same time and be the
    # same length for multiplexing.  If not multiplexing EQcorrscan will
    # maintain the individual differences in time between channels and delay
    # the detection statistics by that amount before stacking and detection.
    client = Client('GEONET')
    design_set = []
    st = Stream()
    for event in cluster:
        print(f"Downloading for event {event.resource_id.id}")
        bulk_info = []
        t1 = event.origins[0].time
        t2 = t1 + 25.1  # Have to download extra data, otherwise GeoNet will
        # trim wherever suits.
        t1 -= 0.1
        for station, channel in stachans:
            try:
                st += client.get_waveforms('NZ', station, '*',
                                           channel[0:2] + '?', t1, t2)
            except IncompleteRead:
                print(f"Could not download for {station} {channel}")
    print(f"Downloaded {len(st)} channels")
    for event in cluster:
        t1 = event.origins[0].time
        t2 = t1 + 25
        design_set.append(st.copy().trim(t1, t2))
    # Construction of the detector will process the traces, then align them,
    # before multiplexing.
    print("Making detector")
    detector = subspace.Detector()
    detector.construct(streams=design_set,
                       lowcut=2.0,
                       highcut=9.0,
                       filt_order=4,
                       sampling_rate=20,
                       multiplex=multiplex,
                       name='Wairarapa1',
                       align=True,
                       reject=0.2,
                       shift_len=6,
                       plot=plot).partition(9)
    print("Constructed Detector")
    if plot:
        detector.plot()
    # We also want the continuous stream to detect in.
    t1 = UTCDateTime(2016, 5, 11, 19)
    t2 = UTCDateTime(2016, 5, 11, 20)
    # We are going to look in a single hour just to minimize cost, but you can
    # run for much longer.
    bulk_info = [('NZ', stachan[0], '*', stachan[1][0] + '?' + stachan[1][-1],
                  t1, t2) for stachan in detector.stachans]
    print("Downloading continuous data")
    st = client.get_waveforms_bulk(bulk_info)
    st.merge().detrend('simple').trim(starttime=t1, endtime=t2)
    # We set a very low threshold because the detector is not that great, we
    # haven't aligned it particularly well - however, at this threshold we make
    # two real detections.
    print("Computing detections")
    detections, det_streams = detector.detect(st=st,
                                              threshold=0.4,
                                              trig_int=2,
                                              extract_detections=True,
                                              cores=cores)
    if return_streams:
        return detections, det_streams
    else:
        return detections
Пример #21
0
def p_compare2(event_no, traces1, traces2, start_buff, end_buff, taper,
               taper_frac, plot_scale_fac, filt, freq_min, freq_max, min_dist,
               max_dist, norm_each, dist_norm, basin_width, basin, CI_only,
               component1, component2, fig_inc):
    # component 1 is E, 2 is N, 3 is Z
    # H is cvmhy, H_simp is cvmhn
    # S is cvms426-223, S_simp is cvms400-100
    # norm_each == 1 scales the peak of each trace to the same size
    #	otherwise, the amplitude is normalized to 10 km, with the scale set with plot_scale_fac

    from obspy import UTCDateTime
    from obspy import Stream
    from obspy import read
    from obspy.geodetics import gps2dist_azimuth
    import numpy as np
    import os
    import matplotlib.pyplot as plt
    import time

    import sys  # don't show any warnings
    import warnings

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

    start_time_wc = time.time()

    # Box limits
    Lon_min = -118.8
    Lat_min = 33.5
    Lon_max = -117.5
    Lat_max = 34.6

    if event_no == '15481673':  # no 29
        event_name = 'LaHa2014'
    elif event_no == '10410337':  # no 14
        event_name = 'Ingl2009'
    elif event_no == '14383980':  # no 29
        event_name = 'Chin2008'
    elif event_no == '14312160':  # no 29
        event_name = 'Chat2007'
    elif event_no == '9703873':  # no 29
        event_name = 'BaHa2001'
    else:
        print('Number does not correspond to a valid event')
        sys.exit()

    verbose = False  # print every distance of a station

    if component2 == 'same':
        component2 = component1

    #%% find event details for origin time, lat, lon
    ev_file = '/Users/vidale/Documents/Research/BasinsLA/My_initial_effort/Compare/event_list.txt'
    file_ev = open(ev_file, 'r')
    for line in file_ev:  # pull numbers off all the lines
        split_line = line.split()
        event = split_line[0]
        if event == event_no:
            ev_lat = float(split_line[3])
            ev_lon = float(split_line[2])
            t1 = UTCDateTime(split_line[5])
            date_label = split_line[5][0:10]
            year1 = split_line[5][0:4]
    print(event_name + ': ev_no ' + event_no + ' ' + str(t1) + ' Lat-Lon ' +
          str(ev_lat) + ' ' + str(ev_lon))

    #%% find event details for origin time, lat, lon
    badt_file = '/Users/vidale/Documents/Research/BasinsLA/My_initial_effort/Compare/bad_trace_no.txt'
    file_badt = open(badt_file, 'r')
    badt_lines = file_badt.readlines()
    badt_event = []
    badt_station = []
    badt_compo = []

    for line in badt_lines:  # pull numbers off all the lines
        split_line = line.split()
        badt_event.append(split_line[0])
        badt_station.append(split_line[1])
        badt_compo.append(split_line[2])
    print(str(len(badt_event)) + ' bad traces in list')

    #%% Open station location file
    sta_file = '/Users/vidale/Documents/Research/BasinsLA/My_initial_effort/Compare/stations.txt'
    file_st = open(sta_file, 'r')
    line = file_st.readline()  # read first line to skip header information
    lines = file_st.readlines()
    # print(str(len(lines)) + ' stations read from ' + sta_file)

    # Load station coords into arrays, many more stations than used
    st_num = []
    st_netw = []
    st_name = []
    st_dist = []
    st_az = []
    st_baz = []
    st_lat = []
    st_lon = []
    for line in lines:
        split_line = line.split()
        st_num.append(split_line[0])
        st_netw.append(split_line[2])
        st_name.append(split_line[3])
        st_lat.append(split_line[4])
        st_lon.append(split_line[5])
        distance = gps2dist_azimuth(ev_lat, ev_lon, float(
            split_line[4]), float(split_line[5]))  # Get traveltime and azimuth
        #		print('Event ' + str(ev_lat) + ' ' + str(ev_lon) + ' station ' + split_line[4] + ' ' + split_line[5] + ' distance ' + str(distance[0]))
        st_dist.append(distance[0] / 1000.)  # distance
        st_az.append(distance[1])  # azimuth
        st_baz.append(distance[2])  # back-azimuth
    print('number of stations in list is ' + str(len(st_num)))

    #%% Load data and synthetic waveforms
    sgrams1 = Stream()
    if traces1 == 'B_Data':
        fname_datZ = '/Users/vidale/Documents/Research/BasinsLA/B_Data/' + event_name + '/allData_mseed/' + component1 + '.mseed'
    elif traces1 == 'CVM_S4':
        fname_datZ = '/Users/vidale/Documents/Research/BasinsLA/B_Syn/' + event_name + '/CVM_S4/' + component1 + '.mseed'
    elif traces1 == 'CVM_H':
        fname_datZ = '/Users/vidale/Documents/Research/BasinsLA/B_Syn/' + event_name + '/CVM_H/' + component1 + '.mseed'
    sgrams1 = read(fname_datZ)

    len1_in = len(sgrams1)
    print(traces1 + ' has ' + str(len1_in) + ' traces')
    if len(sgrams1) == 0:
        print('No stations found in traces1, quit now!')
        sys.exit()
    print('1st gather, 1st data trace has : ' + str(len(sgrams1[0].data)) +
          ' time pts ')
    print('1st gather, 1st trace starts at ' +
          str(sgrams1[0].stats.starttime) + ', event at ' + str(t1))

    if traces2 != 'no':
        sgrams2 = Stream()
        if traces2 == 'B_Data':
            fname_datZ = '/Users/vidale/Documents/Research/BasinsLA/B_Data/' + event_name + '/allData_mseed/' + component2 + '.mseed'
        elif traces2 == 'CVM_S4':
            fname_datZ = '/Users/vidale/Documents/Research/BasinsLA/B_Syn/' + event_name + '/CVM_S4/' + component2 + '.mseed'
        elif traces2 == 'CVM_H':
            fname_datZ = '/Users/vidale/Documents/Research/BasinsLA/B_Syn/' + event_name + '/CVM_H/' + component2 + '.mseed'
        sgrams2 = read(fname_datZ)

    # if traces2 == 'Ricardo_syn':
    #	 lenH = len(sgrams2)	 # correct Hercules units from from meters to cm
    #	 for ii in range(lenH):  # assume trace count of all synthetic files is same
    #		 sgrams2[ii].data	  = 100 * sgrams2[ii].data

    if traces2 != 'no':
        len2_in = len(sgrams2)
        print(traces2 + ' has ' + str(len2_in) + ' traces')
        if len(sgrams2) == 0:
            print('No stations found in traces1, quit now!')
            sys.exit()
        print('2nd gather, 1st trace has : ' + str(len(sgrams2[0].data)) +
              ' time pts ')
        print('2nd gather, 1st trace starts at ' +
              str(sgrams2[0].stats.starttime) + ', event at ' + str(t1))

    #%%  Keep only data that is not on list of bad traces
    # either individual components or A for all components
    sgrams1_good = Stream()
    for tr in sgrams1:  # examine traces one by one
        do_write = 1
        for ii in range(len(badt_event)):
            if event_no == badt_event[ii] and tr.stats.station == badt_station[
                    ii]:  # find station in inventory
                if badt_compo[ii] == 'Z' or badt_compo[ii] == 'A':
                    do_write = 0
        if do_write == 1:
            sgrams1_good += tr
    print('After rejecting listed bad traces, ' + str(len(sgrams1_good)) +
          ' out of ' + str(len1_in) + ' traces remain in 1st gather.')

    if traces2 != 'no' or '':
        sgrams2_good = Stream()
        for tr in sgrams2:  # examine traces one by one
            do_write = 1
            for ii in range(len(badt_event)):
                if event_no == badt_event[
                        ii] and tr.stats.station == badt_station[
                            ii]:  # find station in inventory
                    if badt_compo[ii] == 'Z' or badt_compo[ii] == 'A':
                        do_write = 0
            if do_write == 1:
                sgrams2_good += tr
        print('After rejecting listed bad traces, ' + str(len(sgrams2_good)) +
              ' out of ' + str(len2_in) + ' traces remain in 2nd gather.')

    if len(sgrams1_good) == 0:
        print('No common stations found, quit now!')
        sys.exit()
#%%  Keep only common traces
    if traces2 != 'no':
        sgrams1_comm = Stream()
        sgrams2_comm = Stream()
        for tr1 in sgrams1_good:  # examine traces one by one
            found_it = 0
            if tr1.stats.station in st_name:  # find station in station list
                found_it = 1
                ii = st_name.index(tr1.stats.station)
                for tr2 in sgrams2_good:  # examine traces one by one
                    if tr2.stats.station == tr1.stats.station:  # find station in station list
                        in_CI = (tr1.stats.network == 'CI'
                                 or tr2.stats.network == 'CI')
                        if in_CI == True or CI_only == False:
                            sgrams1_comm += tr1
                            sgrams2_comm += tr2
            if found_it == 0:
                print('Not on station list! station ' + tr1.stats.station +
                      ' network ' + tr1.stats.network)
        print('1st, 2nd sets of seismograms have ' + str(len(sgrams1_comm)) +
              ' and ' + str(len(sgrams2_comm)) + ' common stations')
    else:
        sgrams1_comm = Stream()
        sgrams1_comm = sgrams1_good.copy()

#%% Select data and syn, only need one, don't need 2nd gather for map
# select data by distance (and azimuth?), and cull synthetics to match data

    sgrams1_select = Stream()
    for tr in sgrams1_comm:  # examine traces one by one
        if (tr.stats.network == 'CI' or CI_only == False or traces1 == 'CVM_H'
                or traces1 == 'CVM_S4'):
            found_it = 0
            for ii in range(
                    len(st_name)):  # find matching entry in station roster
                if tr.stats.station == st_name[
                        ii]:  # find station in inventory
                    found_it = 1
                    flat = float(st_lat[ii])
                    flon = float(st_lon[ii])
                    if st_dist[ii] < max_dist and (flat < Lat_max
                                                   and flat > Lat_min
                                                   and flon < Lon_max
                                                   and flon > Lon_min):
                        # exclude stations too far away or outside the map box
                        # basin is roughly within 15 km of these 3 stations
                        distance = gps2dist_azimuth(33.99053, -118.36171,
                                                    float(flat),
                                                    float(flon))  # station BHP
                        dist1 = distance[0] / 1000  # convert m to km
                        distance = gps2dist_azimuth(33.88110, -118.17568,
                                                    float(flat),
                                                    float(flon))  # station LTP
                        dist2 = distance[0] / 1000
                        distance = gps2dist_azimuth(33.80776, -117.98116,
                                                    float(flat),
                                                    float(flon))  # station BRE
                        dist3 = distance[0] / 1000
                        #					print(tr.stats.station + ' ' + str(dist1) + ' ' + str(dist2) + ' ' + str(dist3))
                        #					keep stations only within X km of basin axis
                        if basin == False or (dist1 < basin_width) or (
                                dist2 < basin_width) or (dist3 < basin_width):
                            #						print('selected: ' + tr.stats.station)
                            tr.stats.distance = st_dist[ii]
                            tr.stats.stla = st_lat[ii]
                            tr.stats.stlo = st_lon[ii]
                            sgrams1_select += tr
            # if found_it == 0:
            #	 print('Not on station list! station ' + tr.stats.station + ' network ' + tr.stats.network)
    print('Sgrams 1: Within ' + str(max_dist) +
          ' km distance and optional basin culling, ' +
          str(len(sgrams1_select)) + ' traces remain for plotting.')

    if traces2 != 'no':
        sgrams2_select = Stream()
        for tr in sgrams2_comm:  # examine traces one by one
            if (tr.stats.network == 'CI' or CI_only == False
                    or traces2 == 'CVM_H' or traces2 == 'CVM_S4'):
                found_it = 0
                for ii in range(
                        len(st_name)):  # find matching entry in station roster
                    if tr.stats.station == st_name[
                            ii]:  # find station in inventory
                        found_it = 1
                        flat = float(st_lat[ii])
                        flon = float(st_lon[ii])
                        if st_dist[ii] < max_dist and (flat < Lat_max
                                                       and flat > Lat_min
                                                       and flon < Lon_max
                                                       and flon > Lon_min):
                            # exclude stations too far away or outside the map box
                            # basin is roughly within 15 km of these 3 stations
                            distance = gps2dist_azimuth(
                                33.99053, -118.36171, float(flat),
                                float(flon))  # station BHP
                            dist1 = distance[0] / 1000  # convert m to km
                            distance = gps2dist_azimuth(
                                33.88110, -118.17568, float(flat),
                                float(flon))  # station LTP
                            dist2 = distance[0] / 1000
                            distance = gps2dist_azimuth(
                                33.80776, -117.98116, float(flat),
                                float(flon))  # station BRE
                            dist3 = distance[0] / 1000
                            #					print(tr.stats.station + ' ' + str(dist1) + ' ' + str(dist2) + ' ' + str(dist3))
                            if basin == False or (dist1 < basin_width) or (
                                    dist2 < basin_width
                            ) or (
                                    dist3 < basin_width
                            ):  # keep stations only within X km of basin axis
                                #						print('selected: ' + tr.stats.station)
                                tr.stats.distance = st_dist[ii]
                                tr.stats.stla = st_lat[ii]
                                tr.stats.stlo = st_lon[ii]
                                sgrams2_select += tr
                # if found_it == 0:
                #	 print('Not on station list! station ' + tr.stats.station + ' network ' + tr.stats.network)
        print('Sgrams 2: Within ' + str(max_dist) +
              ' km distance and optional basin culling, ' +
              str(len(sgrams2_select)) + ' traces remain for plotting.')
    if len(sgrams1_select) == 0:
        print('No common stations found, quit now!')
        sys.exit()

    #%%  detrend, taper, filter
    if taper:
        sgrams1_select.taper(taper_frac)
        if traces2 != 'no':
            sgrams2_select.taper(taper_frac)
    if filt:
        sgrams1_select.filter('bandpass',
                              freqmin=freq_min,
                              freqmax=freq_max,
                              corners=4,
                              zerophase=True)
        if traces2 != 'no':
            sgrams2_select.filter('bandpass',
                                  freqmin=freq_min,
                                  freqmax=freq_max,
                                  corners=4,
                                  zerophase=True)
    if taper:
        sgrams1_select.taper(taper_frac)
        if traces2 != 'no':
            sgrams2_select.taper(taper_frac)

    #%% plot traces, different windows in case multiple components are to be compared
    if component1 == 'Z':
        fig_index = 13 + fig_inc
    elif component1 == 'N':
        fig_index = 14 + fig_inc
    elif component1 == 'E':
        fig_index = 15 + fig_inc
    elif component1 == 'R':
        fig_index = 16 + fig_inc
    elif component1 == 'T':
        fig_index = 17 + fig_inc

    plt.close(fig_index)
    plt.figure(fig_index, figsize=(10, 8))
    plt.xlim(start_buff, end_buff)
    plt.ylim(min_dist, max_dist)

    # find max of absolute amplitude
    maxD1 = 0
    for tr in sgrams1_select:
        tr_max = max(abs(tr.data))
        if tr_max > maxD1:
            maxD1 = tr_max

    if traces2 != 'no':
        maxD2 = 0
        for tr in sgrams2_select:
            tr_max = max(abs(tr.data))
            if tr_max > maxD2:
                maxD2 = tr_max
        maxD = max(maxD1, maxD2)
#	print('Max data and H, S, H_simp, and S_simp synthetics are ' + str(maxD) + '  ' + str(maxSH) + '  ' + str(maxSS)+ '  ' + str(maxSH_simp)+ '  ' + str(maxSS_simp))

# find max normalized to 10 km distance (i.e., amp divided by distance, assumes 1/R amp fall-off)
    maxD_N = 0
    for tr in sgrams1_select:
        if verbose == True:
            print(f'Distance is {tr.stats.distance:6.4f}' + '  ' +
                  tr.stats.station)
        tr_max = max(abs(tr.data)) * tr.stats.distance
        if tr_max > maxD_N:
            maxD_N = tr_max

    print(f'Peak amp of trace1 is {maxD1:6.4f}')
    if traces2 != 'no':
        print(f'Peak amp of trace2 is {maxD2:6.4f}')
        print(f'Peak amp of both traces is {maxD:6.4f}')
    print(f'Normed amp of data is {maxD_N:6.4f}')
    plot_fac = plot_scale_fac * (max_dist - min_dist) / maxD_N

    for tr in sgrams1_select:
        dist_offset = tr.stats.distance  # km
        if dist_offset > min_dist and dist_offset < max_dist:
            pnum = max(abs(tr.data))
            printee2 = f'amp {pnum:8.3f}  {tr.stats.network}  {tr.stats.station}'
            plt.text(s=printee2,
                     x=end_buff * 0.8,
                     y=dist_offset + (max_dist - min_dist) * 0.02,
                     color='black'
                     )  #label traces and note amplitude (cm, cm/s, cm/s^^2)
            if traces1 == 'CVM_H' or traces1 == 'CVM_S4':
                tr.stats.starttime = t1
            ttt = np.arange(len(tr.data)) * tr.stats.delta + (
                tr.stats.starttime - t1)
            color_p = 'red'
            if traces2 == 'no':
                color_p = 'black'
            if norm_each:
                plt.plot(ttt, (tr.data - np.median(tr.data)) * plot_scale_fac /
                         (tr.data.max() - tr.data.min()) + dist_offset,
                         color=color_p)
            elif dist_norm == True:
                plt.plot(ttt, (tr.data * plot_fac * tr.stats.distance) +
                         dist_offset,
                         color=color_p)
            else:
                plt.plot(ttt, (tr.data * plot_fac) + dist_offset,
                         color=color_p)

    if traces2 != 'no':
        for tr in sgrams2_select:
            dist_offset = tr.stats.distance
            if dist_offset > min_dist and dist_offset < max_dist:
                if traces2 == 'CVM_H' or traces2 == 'CVM_S4':
                    tr.stats.starttime = t1
                ttt = np.arange(len(tr.data)) * tr.stats.delta + (
                    tr.stats.starttime - t1)
                if norm_each:
                    plt.plot(ttt,
                             (tr.data - np.median(tr.data)) * plot_scale_fac /
                             (tr.data.max() - tr.data.min()) + dist_offset,
                             color='green')
                elif dist_norm == True:
                    plt.plot(ttt, (tr.data * plot_fac * tr.stats.distance) +
                             dist_offset,
                             color='green')
                else:
                    plt.plot(ttt, (tr.data * plot_fac) + dist_offset,
                             color='green')

    plt.xlabel('Time (s)')
    plt.ylabel('Epicentral distance from event (km)')
    #	plt.title(fname1[8:18] + ' vs ' + fname2[8:18])
    if traces2 != 'no':
        plt.title(date_label + ' red ' + traces1 + ' ' + component1 +
                  ' green ' + traces2 + ' ' + component2)
    else:
        plt.title(date_label + ' ' + traces1 + ' ' + component1)
    plt.show()

    elapsed_time_wc = time.time() - start_time_wc
    print('This job took ' + str(elapsed_time_wc) + ' seconds')
    os.system('say "Done"')
Пример #22
0
def make_template(df, sampling_rate, filter=[0.2, 8], tcs_length=[1, 9]):
    '''
        download template by event ID
        original function by: Amanda Thomas
        modified by: Tim Lin
         -merge data with interpolate data point 2020.09
         -add multiple attemps when download has unexpected issue 2020.09
         
    '''
    from obspy.clients.fdsn import Client
    from libcomcat.search import get_event_by_id
    from libcomcat.dataframes import get_phase_dataframe
    from obspy import Stream
    import time
    client = Client("IRIS")
    # make templates
    regional = df['Regional']
    eventid = regional + str(df['ID'])
    detail = get_event_by_id(eventid, includesuperseded=True)
    OT = UTCDateTime(detail.time)  #event origin time
    phases = get_phase_dataframe(detail, catalog=regional)
    phases = phases[phases['Status'] == 'manual']
    phases = phases[~phases.
                    duplicated(keep='first', subset=['Channel', 'Phase'])]
    st = Stream()
    tr = Stream()
    sav_net_sta_comp = []
    sav_phase = []
    sav_arr = []
    sav_travel = []
    All_info = {}
    for ii in range(len(phases)):
        elems = phases.iloc[ii]['Channel'].split('.')
        net = elems[0]
        sta = elems[1]
        comp = elems[2]
        location = elems[3]
        if location == '--':
            location = ''  #sometime the location use -- instead of empty str
        Phase = phases.iloc[ii][
            'Phase']  #P or S wave, save this info for further relocation
        #phase = phases.iloc[ii]['Phase']
        arr = UTCDateTime(phases.iloc[ii]['Arrival Time'])
        #print(int(np.round(arr.microsecond/(1/sampling_rate*10**6))*1/sampling_rate*10**6)==1000000)
        if int(
                np.round(arr.microsecond / (1 / sampling_rate * 10**6)) * 1 /
                sampling_rate * 10**6) == 1000000:
            arr.microsecond = 0
            #arr.second=arr.second+1
            arr = arr + 1
        #print('arrival Time after arrangement',arr)
        else:
            arr.microsecond = int(
                np.round(arr.microsecond / (1 / sampling_rate * 10**6)) * 1 /
                sampling_rate * 10**6)
        t1 = arr - tcs_length[0]
        t2 = arr + tcs_length[1]
        #try:
        i_attempt = 0  #reset number of attempt
        tr_exist = False
        while i_attempt < 5:
            try:
                #tr = client.get_waveforms(net, sta, "*", comp, t1-2, t2+2) #Be careful, this may query more than 1 trace channel i.e. '00','01'...
                tr = client.get_waveforms(
                    net, sta, location, comp, t1 - 2, t2 + 2
                )  #Be careful, this may query more than 1 trace channel i.e. '00','01'...
                tr_exist = True
                break
            except:
                #print('Attempts %d'%(i_attempt),net, sta, "*", comp, t1-2, t2+2)
                time.sleep(2)  #wait 2 sec and try again later...
                i_attempt += 1
                continue

        if not (tr_exist):
            #some unexpected error when attemp to client.get_waveforms
            #print("No data for "+net+" "+sta+" "+comp+" "+str(t1)+" "+str(t2))
            continue
        else:
            #print("Data available:",net, sta, location, comp, t1-2, t2+2)
            tr.merge(method=1,
                     interpolation_samples=-1,
                     fill_value='interpolate')
            tr.detrend('linear')
            tr.trim(starttime=t1 - 2,
                    endtime=t2 + 2,
                    nearest_sample=1,
                    pad=1,
                    fill_value=0)
            if filter:
                tr.filter("bandpass", freqmin=filter[0], freqmax=filter[1])
            tr.interpolate(sampling_rate=sampling_rate,
                           starttime=t1,
                           method='linear')
            tr.trim(starttime=t1,
                    endtime=t2,
                    nearest_sample=1,
                    pad=1,
                    fill_value=0)
            assert len(
                tr) == 1, 'Unexpecting error when query:%s %s %s %s %s %s' % (
                    net, sta, location, comp, t1 - 2, t2 + 2)
            #tr[0].stats.location = tr[0].stats.location+'.'+Phase
            st += tr.copy()
            #save name, time and "phase" info for later relocation
            #.ms only gives starttime (know arrival time) but not P or S wave
            sav_net_sta_comp.append(net + '.' + sta + '.' + comp + '.' +
                                    location)
            sav_phase.append(Phase)
            #tmp_arrT = arr.strftime('%Y-%m-%dT%H:%M:%S.%f') #arrival time in isoformat i.e. 2018-05-05T17:44:18 or 2018-05-05T17:44:23.960000
            tmp_arrT = arr.strftime(
                '%Y-%m-%dT%H:%M:%S.%f')[:-4]  #accuracy to 0.01 sec
            #if len(tmp_arrT)==26:
            #    tmp_arrT = tmp_arrT[:-4]
            #print('Time:',tmp_arrT)
            sav_arr.append(tmp_arrT)
            sav_travel.append(arr - OT)  #travel time (relative to the origin)
    #=========IMPORTANT NOTE!!! the order of these array are NOT necessarily the same as st=====================================================
    #=========When there are both P and S data in the same net.sta.chn.loc, the order will be messed up when write in .ms file==================
    #=========Use these info as extra caution===================================================================================================
    #=========2020.11.12 Update: When calling bulk_make_template will will be runing additional re-order(All_info_reorder) to all the All_info==
    #=========================so the data generated from download_tools.bulk_make_template will be ready to use (the right order as .ms file)===
    All_info['net_sta_comp'] = np.array(sav_net_sta_comp)
    All_info['phase'] = np.array(sav_phase)
    All_info['arrival'] = np.array(sav_arr)  #absolute arrival time
    All_info['travel'] = np.array(sav_travel)  #phase travel time (sec)
    All_info['OT_template'] = OT.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-4]
    All_info['tcs_length'] = tcs_length
    All_info['filter'] = filter
    All_info['eqloc'] = [df['Lon'], df['Lat'], df['Depth']]
    All_info['eqmag'] = df['Magnitude']
    All_info['evid'] = eventid
    return st, All_info
Пример #23
0
def _prep_data_for_correlation(stream, templates, template_names=None,
                               force_stream_epoch=True):
    """
    Check that all channels are the same length and that all channels have data
    for both template and stream.

    Works in place on data - will cut to shortest length

    :param stream: Stream to compare data to
    :param templates:
        List of streams that will be forced to have the same channels as stream
    :param template_names:
        List of strings same length as templates
    :type force_stream_epoch: bool
    :param force_stream_epoch:
        Whether to force all channels in stream to cover the same time period

    :return: stream, templates, template_names (if template_names given)
    """
    n_templates = len(templates)
    template_samp_rates = {
        tr.stats.sampling_rate for template in templates for tr in template}
    stream_samp_rates = {tr.stats.sampling_rate for tr in stream}
    samp_rates = template_samp_rates.union(stream_samp_rates)
    assert len(samp_rates) == 1, "Sampling rates differ"
    samp_rate = samp_rates.pop()

    out_stream = Stream()

    named = True
    if template_names is None:
        named = False
        template_names = range(n_templates)

    # Work out shapes.
    stream_start = min([tr.stats.starttime for tr in stream])
    stream_end = max([tr.stats.endtime for tr in stream])
    if force_stream_epoch:
        stream_length = int(samp_rate * (stream_end - stream_start)) + 1
    else:
        stream_length = max([tr.stats.npts for tr in stream])

    template_length = {
        tr.stats.npts for template in templates for tr in template}
    assert len(template_length) == 1, "Template traces not all the same length"
    template_length = template_length.pop()

    stream_ids = {tr.id for tr in stream}

    # Need to ensure that a channel can be in the template multiple times.
    template_ids = {stream_id: [] for stream_id in stream_ids}
    for template in templates:
        # Only include those in the stream.
        channels_in_template = {
            tr.id for tr in template}.intersection(stream_ids)
        for channel in channels_in_template:
            template_ids[channel].append(len(template.select(id=channel)))

    template_ids = {key: max(value) for key, value in template_ids.items()
                    if len(value) > 0}

    seed_ids = sorted(
        [key.split('.') + [i] for key, value in template_ids.items()
         for i in range(value)])
    seed_ids = [('.'.join(seed_id[0:-1]), seed_id[-1]) for seed_id in seed_ids]

    for channel_number, seed_id in enumerate(template_ids.keys()):
        stream_data = np.zeros(stream_length, dtype=np.float32)
        stream_channel = stream.select(id=seed_id)
        if len(stream_channel) > 1:
            raise NotImplementedError(
                "Multiple channels in continuous data for {0}".format(seed_id))
        stream_channel = stream_channel[0]
        if stream_channel.stats.npts == stream_length:
            stream_data = stream_channel.data
        else:
            Logger.info('Data for {0} is not as long as needed, '
                        'padding'.format(stream_channel.id))
            if force_stream_epoch:
                start_pad = int(samp_rate * (
                        stream_channel.stats.starttime - stream_start))
                end_pad = stream_length - (
                        start_pad + stream_channel.stats.npts)
                # In some cases there will be one sample missing when sampling
                # time-stamps are not set consistently between channels, this
                # results in start_pad and end_pad being len==0
                if start_pad == 0 and end_pad == 0:
                    Logger.debug("Start and end pad are both zero, padding "
                                 "at one end")
                    if (stream_channel.stats.starttime - stream_start) > (
                       stream_end - stream_channel.stats.endtime):
                        start_pad = int(
                            stream_length - stream_channel.stats.npts)
                    else:
                        end_pad = int(
                            stream_length - stream_channel.stats.npts)
                stream_channel.stats.starttime -= (start_pad / samp_rate)
            else:
                start_pad = 0
                end_pad = stream_length - stream_channel.stats.npts
            if end_pad == 0:
                stream_data[start_pad:] = stream_channel.data
            else:
                stream_data[start_pad:-end_pad] = stream_channel.data
        header = stream_channel.stats.copy()
        header.npts = stream_length
        out_stream += Trace(data=stream_data, header=header)

    # Initialize nan template for speed.
    nan_channel = np.full(template_length, np.nan, dtype=np.float32)
    nan_template = Stream()
    for _seed_id in seed_ids:
        net, sta, loc, chan = _seed_id[0].split('.')
        nan_template += Trace(header=Stats({
            'network': net, 'station': sta, 'location': loc,
            'channel': chan, 'starttime': UTCDateTime(),
            'npts': template_length, 'sampling_rate': samp_rate}))

    # Remove templates with no matching channels
    filt = np.ones(len(template_names)).astype(bool)
    for i, template in enumerate(templates):
        template_ids = {tr.id for tr in template}
        if len(template_ids.intersection(stream_ids)) == 0:
            filt[i] = 0

    _out = dict(zip(
        [_tn for _tn, _filt in zip(template_names, filt) if _filt],
        [_t for _t, _filt in zip(templates, filt) if _filt]))

    if len(_out) != len(templates):
        Logger.debug("Some templates not used due to no matching channels")

    # Fill out the templates with nan channels
    for template_name, template in _out.items():
        template_starttime = min([tr.stats.starttime for tr in template])
        out_template = nan_template.copy()
        for channel_number, _seed_id in enumerate(seed_ids):
            seed_id, channel_index = _seed_id
            template_channel = template.select(id=seed_id)
            if len(template_channel) <= channel_index:
                out_template[channel_number].data = nan_channel
                out_template[channel_number].stats.starttime = \
                    template_starttime
            else:
                out_template[channel_number] = template_channel[channel_index]
        _out.update({template_name: out_template})

    out_templates = list(_out.values())
    out_template_names = list(_out.keys())

    if named:
        return out_stream, out_templates, out_template_names
    return out_stream, out_templates
Пример #24
0
    ntemp=read(path+sta[k]+'.HNN.mseed')
    ntemp.trim(starttime=time_epi)
    ntemp[0].data=(ntemp[0].data/1e6)/9.81
    etemp=read(path+sta[k]+'.HNE.mseed')
    etemp.trim(starttime=time_epi)
    etemp[0].data=(etemp[0].data/1e6)/9.81
    ztemp=read(path+sta[k]+'.HNZ.mseed')
    ztemp.trim(starttime=time_epi)
    ztemp[0].data=(ztemp[0].data/1e6)/9.81
    
    n+=ntemp[0]
    e+=etemp[0]
    z+=ztemp[0]
    
#Make time series of pga
npga=n.copy()
epga=e.copy()
zpga=z.copy()
N=n[0].stats.npts
for ksta in range(len(sta)):
    for ktime in range(N):
        if ktime==0:
            npga[ksta].data[ktime]=0
            epga[ksta].data[ktime]=0
            zpga[ksta].data[ktime]=0
        else:
            npga[ksta].data[ktime]=abs(n[ksta].data[0:ktime]).max()
            epga[ksta].data[ktime]=abs(e[ksta].data[0:ktime]).max()
            zpga[ksta].data[ktime]=abs(z[ksta].data[0:ktime]).max()
    
#Decimate time series to 2Hz
Пример #25
0
def paracorr(par):
    """Computation of noise correlation functions
    
    Compute noise correlation functions according to specifications parameter
    dictionary ``par``. This function is most conviniently used as a python
    program passing the parameter file as argument. This use is explained in
    the tutorial on correlation.
    
    The processing is performed in the following sequence
    
     * Data is read in typically day-long chunks ideally contained in a single 
       file to speed up reading time
     * Preprocessing on the long sequences to avoid dominating influence of 
       perturbing signals if processed in shorter chunks.
     * dividing these long sequences into shorter ones (typically an hour)
     * time domain preprocessing
     * frequency domain preprocessing
     * correlation
     * if ``direct_output`` is present data is directly writen by individual
       processes
     * optionally rotation of correlation tensor into ZRT system (not possible
       in combination with direct output
     * combination of correlation traces of subsequent time segments in 
       correlation matrices
     * optionally delete traces of individual time segments
    
    :type par: dict
    :param par: processing parameters
    """


    # initialize MPI
    comm = MPI.COMM_WORLD
    rank = comm.Get_rank()
    psize = comm.Get_size()
    
    # set up the logger
    logger = logging.getLogger('paracorr')
    hdlr = logging.FileHandler(os.path.join(par['log_dir'],'%s_paracorr_%d.log' % (
                        par['execution_start'],rank)))
    formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
    hdlr.setFormatter(formatter)
    logger.addHandler(hdlr) 
    logger.setLevel(logging.DEBUG)

    comb_list = combine_station_channels(par['net']['stations'],
                            par['net']['channels'],par['co']['combination_method'])

    sttimes = datetime_list(par['co']['read_start'], par['co']['read_end'], inc=par['co']['read_inc'])	## loop over 24hrs/whole days
    time_inc = datetime.timedelta(seconds=par['co']['read_len'])
    lle_df = lat_lon_ele_load(par['net']['coordinate_file'])
    res_dir = par['co']['res_dir']
    station_list = par['net']['stations']
    channel_list = par['net']['channels']


    program_start = UTCDateTime()

    # bulid a dictionary that caches the streams that reside in the same file
    stream_cache = {}
    for station in par['net']['stations']:
        stream_cache.update({station:{}})
        for channel in par['net']['channels']:
            stream_cache[station].update({channel:Stream().append(Trace())})


    # mapping of stations to processes
    pmap = (np.arange(len(station_list))*psize)/len(station_list)
    # indecies for stations to be worked on by each process
    st_ind = np.where(pmap == rank)[0]

    # number of subdivision of read length
    if 'subdivision' in par['co'].keys():
        nsub = int(np.ceil((float(par['co']['read_len']) - par['co']['subdivision']['corr_len'])
                                      /par['co']['subdivision']['corr_inc'])+1)
    else:
        nsub = 1

    # loop over times
    pathname = os.path.join(res_dir, correlation_subdir_name(sttimes[0]))
    print '\nrank %d of %d'  % (rank,psize)
    logger.debug('Rank %d of %d Beginning execution.'  % (rank,psize))
    for sttime in sttimes:
        if rank == 0:
            print "\n>>> Working on %s at %s:" % (sttime,UTCDateTime())
            logger.debug("\n>>> Working on %s at %s:" % (sttime,UTCDateTime()))
        usttime = UTCDateTime(sttime)
        # fill cache and extract current stream: only done by process 0
        cst = Stream()
        # loop over stations different stations for every process
        for this_ind in st_ind:
            station = station_list[this_ind]
            tst = Stream()
            for channel in channel_list:
                print channel, station
                try:
                    if ((len(stream_cache[station][channel])==0) or 
                            (not ((stream_cache[station][channel][0].stats['starttime']<=usttime) &
                            (stream_cache[station][channel][0].stats['endtime']>=(usttime+par['co']['read_len']))))):
                        stream_cache[station][channel] = read_from_filesystem('%s.*.%s' %(station, channel), sttime, sttime+time_inc, par['net']['fss'], trim=False)
                        if not stream_cache[station][channel]:
                            logger.warning("%s %s at %s: No trace read." % (station, channel, sttime))
                            continue
                        samp_flag = False
                        for tr in stream_cache[station][channel]:
                            if tr.stats.sampling_rate != par['co']['sampling_rate']:
                                samp_flag = True
                        if samp_flag:
                            logger.warning("%s %s at %s: Mismatching sampling rate." % (station, channel, sttime))
                            stream_cache[station][channel] = Stream()
                            continue
                        if par['co']['decimation'] > 1:
                            sst = stream_cache[station][channel].split()
                            sst.decimate(par['co']['decimation'])
                            stream_cache[station][channel] = deepcopy(sst.merge())
                    ttst = stream_cache[station][channel].copy().trim(starttime=usttime,
                                                 endtime=usttime+par['co']['read_len'])
                    get_valid_traces(ttst)
                    tst +=ttst
                except:
                    logger.warning("%s %s at %s: %s" % (station, channel, sttime, sys.exc_info()[0]))
            cst += tst
        cst = stream_add_lat_lon_ele(cst,lle_df)
        # initial preprocessing on long time series
        if 'preProcessing' in par['co'].keys():
            for procStep in par['co']['preProcessing']:
                cst = procStep['function'](cst,**procStep['args'])

        # create output path
        pathname = os.path.join(par['co']['res_dir'],correlation_subdir_name(sttime))
        if rank == 0:
            create_path(pathname)
                
        # broadcast every station to every process    
        st = Stream()
        for pind in range(psize):
            pst = Stream()
            if rank == pind:
                pst = deepcopy(cst)
            pst = comm.bcast(pst, root=pind)
            st += pst

        ## do correlations
        if len(st) == 0:
            logger.warning("%s: No traces to correlate." % (sttime))
        else:
            targs = deepcopy(par['co']['corr_args'])
            if 'direct_output' in targs.keys():
                targs['direct_output']['base_dir'] = pathname
            # loop over subdivisions
            for subn in range(nsub):
                if nsub > 1:
                    sub_st = st.copy().trim(starttime=UTCDateTime(sttime)+
                                    subn*par['co']['subdivision']['corr_inc'],
                                    endtime=UTCDateTime(sttime)+subn*par['co']['subdivision']['corr_inc']+
                                    par['co']['subdivision']['corr_len'])
                    get_valid_traces(sub_st)
                else:
                    sub_st = st
                targs['combinations'] = select_available_combinations(sub_st,comb_list,targs)
                if len(targs['combinations']) == 0:
                    continue
                cst = px.stream_pxcorr(sub_st,targs,comm=comm)
                # if 'direct_output' in targs.keys() cst is empty and the 
                # following will not be executed
                if cst:
                    if par['co']['rotation']:
                        rcst = px.rotate_multi_corr_stream(cst)
                    else:
                        rcst = cst
                
                    # distributed writing
                    # mapping of stations to processes
                    pmap = (np.arange(len(rcst))*psize)/len(rcst)
                    # indecies for stations to be worked on by each process
                    tr_ind = np.where(pmap == rank)[0]
                    logger.debug('Process %d starting to write %d traces to %s.' % (rank,len(tr_ind),pathname))
                    this_st = Stream()
                    for this_ind in tr_ind:
                        this_st.append(rcst[this_ind])
                    convert_to_matlab(this_st,'trace',pathname)
            
        # if there is a subdivision of read traces
        comm.barrier()
        if ('subdivision' in par['co']) and (rank == 0):
            logger.debug('combining subdivisions')
            # combine traces to matrix
            corr_mat_create_from_traces(pathname, pathname, delete_trace_files=True)
            if par['co']['subdivision']['recombine_subdivision']:
                flist = dir_read(pathname,'mat__*.mat')
                for fl in flist:
                    try:
                        mat = mat_to_ndarray(fl)
                        tr = corr_mat_extract_trace(mat,method='norm_mean')
                        save_dict_to_matlab_file(fl.replace('mat__','tr__'),tr)
                        if par['co']['subdivision']['delete_subdivisions']:
                            os.remove(fl)
                    except:
                        pass



    program_end = UTCDateTime()

    print 'rank %d execution time' % rank, program_end-program_start
    logger.debug('Rank %d of %d End execution.'  % (rank,psize))
Пример #26
0
def run_tutorial(plot=False,
                 process_len=3600,
                 num_cores=cpu_count(),
                 **kwargs):
    """Main function to run the tutorial dataset."""
    # First we want to load our templates
    template_names = glob.glob('tutorial_template_*.ms')

    if len(template_names) == 0:
        raise IOError('Template files not found, have you run the template ' +
                      'creation tutorial?')

    templates = [read(template_name) for template_name in template_names]

    # Work out what stations we have and get the data for them
    stations = []
    for template in templates:
        for tr in template:
            stations.append((tr.stats.station, tr.stats.channel))
    # Get a unique list of stations
    stations = list(set(stations))

    # We will loop through the data chunks at a time, these chunks can be any
    # size, in general we have used 1 day as our standard, but this can be
    # as short as five minutes (for MAD thresholds) or shorter for other
    # threshold metrics. However the chunk size should be the same as your
    # template process_len.

    # You should test different parameters!!!
    start_time = UTCDateTime(2016, 1, 4)
    end_time = UTCDateTime(2016, 1, 5)
    chunks = []
    chunk_start = start_time
    while chunk_start < end_time:
        chunk_end = chunk_start + process_len
        if chunk_end > end_time:
            chunk_end = end_time
        chunks.append((chunk_start, chunk_end))
        chunk_start += process_len

    unique_detections = []

    # Set up a client to access the GeoNet database
    client = Client("GEONET")

    # Note that these chunks do not rely on each other, and could be paralleled
    # on multiple nodes of a distributed cluster, see the SLURM tutorial for
    # an example of this.
    for t1, t2 in chunks:
        # Generate the bulk information to query the GeoNet database
        bulk_info = []
        for station in stations:
            bulk_info.append(('NZ', station[0], '*',
                              station[1][0] + 'H' + station[1][-1], t1, t2))

        # Note this will take a little while.
        print('Downloading seismic data, this may take a while')
        st = client.get_waveforms_bulk(bulk_info)
        # Merge the stream, it will be downloaded in chunks
        st.merge()

        # Pre-process the data to set frequency band and sampling rate
        # Note that this is, and MUST BE the same as the parameters used for
        # the template creation.
        print('Processing the seismic data')
        st = pre_processing.shortproc(st,
                                      lowcut=2.0,
                                      highcut=9.0,
                                      filt_order=4,
                                      samp_rate=20.0,
                                      num_cores=num_cores,
                                      starttime=t1,
                                      endtime=t2)
        # Convert from list to stream
        st = Stream(st)

        # Now we can conduct the matched-filter detection
        detections = match_filter.match_filter(template_names=template_names,
                                               template_list=templates,
                                               st=st,
                                               threshold=8.0,
                                               threshold_type='MAD',
                                               trig_int=6.0,
                                               plotvar=plot,
                                               plotdir='.',
                                               cores=num_cores,
                                               plot_format='png',
                                               **kwargs)

        # Now lets try and work out how many unique events we have just to
        # compare with the GeoNet catalog of 20 events on this day in this
        # sequence
        for master in detections:
            keep = True
            for slave in detections:
                if not master == slave and abs(master.detect_time -
                                               slave.detect_time) <= 1.0:
                    # If the events are within 1s of each other then test which
                    # was the 'best' match, strongest detection
                    if not master.detect_val > slave.detect_val:
                        keep = False
                        print('Removed detection at %s with cccsum %s' %
                              (master.detect_time, master.detect_val))
                        print('Keeping detection at %s with cccsum %s' %
                              (slave.detect_time, slave.detect_val))
                        break
            if keep:
                unique_detections.append(master)
                print('Detection at :' + str(master.detect_time) +
                      ' for template ' + master.template_name +
                      ' with a cross-correlation sum of: ' +
                      str(master.detect_val))
                # We can plot these too
                if plot:
                    stplot = st.copy()
                    template = templates[template_names.index(
                        master.template_name)]
                    lags = sorted([tr.stats.starttime for tr in template])
                    maxlag = lags[-1] - lags[0]
                    stplot.trim(starttime=master.detect_time - 10,
                                endtime=master.detect_time + maxlag + 10)
                    plotting.detection_multiplot(stplot, template,
                                                 [master.detect_time.datetime])
    print('We made a total of ' + str(len(unique_detections)) + ' detections')
    return unique_detections
Пример #27
0
def plot_compare3(event_no, traces1, traces2, traces3, start_buff, end_buff,
                  taper, taper_frac, plot_scale_fac, filt, freq_min, freq_max,
                  only_common, min_dist, max_dist, basin_width, basin,
                  basin_box, norm_each, dist_norm, CI_only, component1,
                  component2, component3, fig_inc, shift1, shift2, shift3):
    # component 1 is E, 2 is N, 3 is Z
    # H is cvmhy, H_simp is cvmhn
    # S is cvms426-223, S_simp is cvms400-100
    # norm_each == 1 scales the peak of each trace to the same size
    #    otherwise, the amplitude is normalized to 10 km, with the scale set with plot_scale_fac

    from obspy import UTCDateTime
    from obspy import Stream
    from obspy import read
    from obspy.geodetics import gps2dist_azimuth
    from termcolor import colored
    import numpy as np
    import os
    import sys
    import matplotlib.pyplot as plt
    import time
    import warnings

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

    start_time_wc = time.time()
    print(colored('Running plot_compare_two', 'cyan'))

    # Box limits
    Lon_min = -118.8
    Lat_min = 33.3
    Lon_max = -117.5
    Lat_max = 34.6

    if basin_box:
        Lon_min = -118.3
        Lat_min = 33.75
        Lon_max = -118.0
        Lat_max = 34.05

    if basin_box and basin:
        print('Invoking both basin and basin_box parameter, RETHINK!')
        sys.exit()

    if event_no == '15481673':  # no 29
        event_dir = 'LaHa2014'
    elif event_no == '10410337':  # no 14
        event_dir = 'Ingl2009'
    elif event_no == '14383980':  # no 29
        event_dir = 'Chin2008'
    elif event_no == '14312160':  # no 29
        event_dir = 'Chat2007'
    elif event_no == '9703873':  # no 29
        event_dir = 'BeHi2001'
    else:
        print('Number does not correspond to a valid event')
        sys.exit()

    verbose = False  # print every distance of a station
    if component2 == 'same':
        component2 = component1
        component3 = component1

    #%% Look up origin time, lat, lon
    ev_file = '/Users/vidale/Documents/GitHub/LAB/Quakes/Files/event_list.txt'
    file_ev = open(ev_file, 'r')
    for line in file_ev:  # pull numbers off all the lines
        split_line = line.split()
        event = split_line[0]
        if event == event_no:
            ev_lat = float(split_line[3])
            ev_lon = float(split_line[2])
            t1 = UTCDateTime(split_line[5])
            date_label = split_line[5][0:10]
            year1 = split_line[5][0:4]
    print(event_dir + ': ev_no ' + event_no + ' ' + str(t1) + ' Lat-Lon ' +
          str(ev_lat) + ' ' + str(ev_lon))

    #%% Read in bad traces file
    badt_file = '/Users/vidale/Documents/GitHub/LAB/Quakes/Files/bad_traces.txt'
    file_badt = open(badt_file, 'r')
    badt_lines = file_badt.readlines()
    badt_event = []
    badt_station = []
    badt_compo = []

    for line in badt_lines:  # pull numbers off all the lines
        split_line = line.split()
        badt_event.append(split_line[0])
        badt_station.append(split_line[1])
        badt_compo.append(split_line[2])
    print(str(len(badt_event)) + ' bad traces in list')

    #%% Read in station location file
    sta_file = '/Users/vidale/Documents/GitHub/LAB/Quakes/Files/stations.txt'
    file_st = open(sta_file, 'r')
    line = file_st.readline()  # read first line to skip header information
    lines = file_st.readlines()
    print(str(len(lines)) + ' stations read from ' + sta_file)

    # Load station names and coords into arrays
    st_netw = []
    st_name = []
    st_lat = []
    st_lon = []
    # Calculated values
    st_dist = []
    st_az = []
    st_baz = []

    for line in lines:
        split_line = line.split()
        st_netw.append(split_line[0])
        st_name.append(split_line[1])
        st_lat.append(split_line[2])
        st_lon.append(split_line[3])
        distance = gps2dist_azimuth(ev_lat, ev_lon, float(
            split_line[2]), float(split_line[3]))  # Get distance and azimuth
        st_dist.append(distance[0] / 1000.)  # distance
        st_az.append(distance[1])  # azimuth
        st_baz.append(distance[2])  # back-azimuth
    print('Number of stations in list is ' + str(len(st_name)))

    #%% Load data and synthetic waveforms
    sgrams1 = Stream()
    fname = '/Users/vidale/Documents/Research/BasinsLA/Seis/' + event_dir + '/' + traces1 + '/' + component1 + '.mseed'
    print(fname + ' is 1st file to be read')
    sgrams1 = read(fname)

    len1_in = len(sgrams1)
    print(colored(traces1 + ' has ' + str(len1_in) + ' traces', 'magenta'))
    if len(sgrams1) == 0:
        print('No stations found in traces1, quit now!')
        sys.exit()
    print('1st gather, 1st data trace has : ' + str(len(sgrams1[0].data)) +
          ' time pts ')
    print('1st gather, 1st trace starts at ' +
          str(sgrams1[0].stats.starttime) + ', event at ' + str(t1))

    if traces2 != 'no':
        sgrams2 = Stream()
        fname = '/Users/vidale/Documents/Research/BasinsLA/Seis/' + event_dir + '/' + traces2 + '/' + component2 + '.mseed'
        print(fname + ' is 2nd file to be read')
        sgrams2 = read(fname)
        len2_in = len(sgrams2)
        print(colored(traces2 + ' has ' + str(len2_in) + ' traces', 'magenta'))
        print('2nd gather, 1st data trace has : ' + str(len(sgrams2[0].data)) +
              ' time pts ')
        print('2nd gather, 1st trace starts at ' +
              str(sgrams2[0].stats.starttime))

    if traces3 != 'no':
        sgrams3 = Stream()
        fname = '/Users/vidale/Documents/Research/BasinsLA/Seis/' + event_dir + '/' + traces3 + '/' + component3 + '.mseed'
        print(fname + ' is 3rd file to be read')
        sgrams3 = read(fname)
        len3_in = len(sgrams3)
        print(colored(traces3 + ' has ' + str(len3_in) + ' traces', 'magenta'))
        print('3rd gather, 1st data trace has : ' + str(len(sgrams3[0].data)) +
              ' time pts ')
        print('3rd gather, 1st trace starts at ' +
              str(sgrams3[0].stats.starttime))

    if shift1 != 0:  #  added to shift traces, done just before plotting for inscrutable reasons
        print(
            colored(
                'BE AWARE! trace1 gets shifted earlier ' + str(shift1) +
                ' seconds!', 'magenta'))
    if traces2 != 'no':
        if shift2 != 0:
            print(
                colored(
                    'BE AWARE! trace2 gets shifted earlier ' + str(shift2) +
                    ' seconds!', 'magenta'))
    if traces3 != 'no':
        if shift3 != 0:
            print(
                colored(
                    'BE AWARE! trace3 gets shifted earlier ' + str(shift3) +
                    ' seconds!', 'magenta'))

    # if src_name1 == 'Ric' and traces1 == 'data':  #  added to easily correct the 0.4s shift Ricardo's data needs
    #     print('trace1 gets 0.33s Ric shift')
    #     shift1 += 0.33
    # print('src_name2 and traces2 are ' + src_name2 + '  ' + traces2)
    # if traces2 != 'no' and src_name2 == 'Ric' and traces2 == 'data':
    #     print('trace2 gets 0.33s Ric shift')
    #     shift2 += 0.33
    # if traces3 != 'no' and src_name3 == 'Ric' and traces3 == 'data':
    #     print('trace3 gets 0.33s Ric shift')
    #     shift3 += 0.33

    # Ricardo's synthetics need an amp correction
    # if src_name1 == 'Ric' and (traces1 == 'CVM_S' or traces1 == 'CVM_H'):  #  Hercules factor of 100
    #     print('trace1 gets Ric syn 100X mult')
    #     lenS = len(sgrams1)
    #     for ii in range(lenS):
    #         lendata = len(sgrams1[ii].data)
    #         for iii in range(lendata):
    #             sgrams1[ii].data[iii] *= 100.
    # if traces2 != 'no':
    #     if src_name2 == 'Ric' and (traces2 == 'CVM_S' or traces2 == 'CVM_H'):
    #         print('trace2 gets Ric syn 100X mult')
    #         lenS = len(sgrams2)
    #         for ii in range(lenS):
    #             lendata = len(sgrams2[ii].data)
    #             for iii in range(lendata):
    #                 sgrams2[ii].data[iii] *= 100.
    # if traces3 != 'no':
    #     if src_name3 == 'Ric' and (traces3 == 'CVM_S' or traces3 == 'CVM_H'):
    #         print('trace3 gets Ric syn 100X mult')
    #         lenS = len(sgrams3)
    #         for ii in range(lenS):
    #             lendata = len(sgrams3[ii].data)
    #             for iii in range(lendata):
    #                 sgrams3[ii].data[iii] *= 100.

    if traces2 != 'no':
        len2_in = len(sgrams2)
        print(traces2 + '  has ' + str(len2_in) + ' traces')
        if len(sgrams2) == 0:
            print('No stations found in traces2, quit now!')
            sys.exit()
        print('2nd gather, 1st trace has : ' + str(len(sgrams2[0].data)) +
              ' time pts ')
        print('2nd gather, 1st trace starts at ' +
              str(sgrams2[0].stats.starttime) + ', event at ' + str(t1))
    if traces3 != 'no':
        len3_in = len(sgrams3)
        print(traces3 + '  has ' + str(len3_in) + ' traces')
        if len(sgrams3) == 0:
            print('No stations found in traces3, quit now!')
            sys.exit()
        print('3rd gather, 1st trace has : ' + str(len(sgrams3[0].data)) +
              ' time pts ')
        print('3rd gather, 1st trace starts at ' +
              str(sgrams3[0].stats.starttime) + ', event at ' + str(t1))

    #%%  Keep only data that is not on list of bad traces
    # either individual components or A for all components
    sgrams1_good = Stream()
    for tr in sgrams1:  # examine traces one by one
        do_write = 1
        for ii in range(len(badt_event)):
            if event_no == badt_event[ii] and tr.stats.station == badt_station[
                    ii]:  # find station in inventory
                if badt_compo[ii] == 'Z' or badt_compo[ii] == 'A':
                    do_write = 0
        if do_write == 1:
            sgrams1_good += tr
    print('After rejecting listed bad traces, ' + str(len(sgrams1_good)) +
          ' out of ' + str(len1_in) + ' traces remain in 1st gather.')

    if traces2 != 'no':
        sgrams2_good = Stream()
        for tr in sgrams2:  # examine traces one by one
            do_write = 1
            for ii in range(len(badt_event)):
                if event_no == badt_event[
                        ii] and tr.stats.station == badt_station[
                            ii]:  # find station in inventory
                    if badt_compo[ii] == 'Z' or badt_compo[ii] == 'A':
                        do_write = 0
            if do_write == 1:
                sgrams2_good += tr
        print('After rejecting listed bad traces, ' + str(len(sgrams2_good)) +
              ' out of ' + str(len2_in) + ' traces remain in 2nd gather.')

    if traces3 != 'no':
        sgrams3_good = Stream()
        for tr in sgrams3:  # examine traces one by one
            do_write = 1
            for ii in range(len(badt_event)):
                if event_no == badt_event[
                        ii] and tr.stats.station == badt_station[
                            ii]:  # find station in inventory
                    if badt_compo[ii] == 'Z' or badt_compo[ii] == 'A':
                        do_write = 0
            if do_write == 1:
                sgrams3_good += tr
        print('After rejecting listed bad traces, ' + str(len(sgrams3_good)) +
              ' out of ' + str(len3_in) + ' traces remain in 2nd gather.')

    if len(sgrams1_good) == 0:
        print('No seismogram found for traces1, quit now!')
        sys.exit()

#%% test for traces not in station list, but really want to test stations that meet all inclusion criteria
#     if traces2 != 'no':
#     for tr1 in sgrams1_good: # examine traces one by one
#         if tr1.stats.station not in st_name:  # is station in station list?
#              if traces1 == 'Data':
#                 print('Wasted data trace, not on station list! station ' + tr1.stats.station + ' network ' + tr1.stats.network)
#              else:
#                 print('Wasted syn trace, not on station list! station ' + tr1.stats.station + ' network ' + tr1.stats.network)

#%%  Keep only common traces
    sgrams1_comm = Stream()
    sgrams2_comm = Stream()
    sgrams3_comm = Stream()
    if only_common == True:
        if traces2 != 'no':
            for tr1 in sgrams1_good:  # examine traces one by one
                if tr1.stats.station in st_name:  # find station in station list
                    ii = st_name.index(tr1.stats.station)
                    for tr2 in sgrams2_good:  # examine traces one by one
                        if tr2.stats.station == tr1.stats.station:  # find station in station list
                            if traces3 != 'no':
                                for tr3 in sgrams3_good:  # examine traces one by one
                                    if tr3.stats.station == tr1.stats.station:  # find station in station list
                                        in_CI = (tr1.stats.network == 'CI')
                                        if in_CI == True or CI_only == False:
                                            sgrams1_comm += tr1
                                            sgrams2_comm += tr2
                                            sgrams3_comm += tr3
                            else:
                                in_CI = (tr1.stats.network == 'CI')
                                if in_CI == True or CI_only == False:
                                    sgrams1_comm += tr1
                                    sgrams2_comm += tr2
            print('1st, 2nd, 3rd sets of seismograms have ' +
                  str(len(sgrams1_comm)) + ', ' + str(len(sgrams2_comm)) +
                  ', and ' + str(len(sgrams3_comm)) + ' common stations')
        else:
            sgrams1_comm = Stream()
            sgrams1_comm = sgrams1_good.copy()
    else:
        sgrams1_comm = sgrams1_good
        if traces2 != 'no':
            sgrams2_comm = sgrams2_good
        if traces3 != 'no':
            sgrams3_comm = sgrams3_good
        print('Not common: 1st, 2nd, 3rd sets of seismograms have ' +
              str(len(sgrams1_comm)) + ', ' + str(len(sgrams2_comm)) +
              ', and ' + str(len(sgrams3_comm)))

#%% Select data and syn, only need one, don't need 2nd gather for map
# select data by distance (and azimuth?), and cull synthetics to match data

    sgrams1_select = Stream()
    for tr in sgrams1_comm:  # examine traces one by one
        if (tr.stats.network == 'CI' or CI_only == False or traces1 == 'CVM_H'
                or traces1 == 'CVM_S' or traces1 == 'Jon_CVM_S'):
            found_it = 0
            for ii in range(
                    len(st_name)):  # find matching entry in station roster
                if tr.stats.station == st_name[
                        ii]:  # find station in inventory
                    found_it = 1
                    flat = float(st_lat[ii])
                    flon = float(st_lon[ii])
                    if verbose == True:
                        print(tr.stats.station + ' X Lat ' + str(flat) +
                              ' Lon ' + str(flon))
                    if (st_dist[ii] < max_dist and st_dist[ii] > min_dist
                        ) and (flat < Lat_max and flat > Lat_min
                               and flon < Lon_max and flon > Lon_min):
                        # exclude stations too far away or outside the map box
                        # basin is roughly within 15 km of these 3 stations
                        distance = gps2dist_azimuth(33.99053, -118.36171,
                                                    float(flat),
                                                    float(flon))  # station BHP
                        dist1 = distance[0] / 1000  # convert m to km
                        distance = gps2dist_azimuth(33.88110, -118.17568,
                                                    float(flat),
                                                    float(flon))  # station LTP
                        dist2 = distance[0] / 1000
                        distance = gps2dist_azimuth(33.80776, -117.98116,
                                                    float(flat),
                                                    float(flon))  # station BRE
                        dist3 = distance[0] / 1000
                        if verbose == True:
                            print(tr.stats.station + ' Lat ' + str(flat) +
                                  ' Lon ' + str(flon))
#                         print(tr.stats.station + ' ' + str(dist1) + ' ' + str(dist2) + ' ' + str(dist3))
#                         keep stations only within X km of basin axis
                        if (basin == False) or (dist1 < basin_width) or (
                                dist2 < basin_width) or (dist3 < basin_width):
                            #                             print('selected: ' + tr.stats.station)
                            tr.stats.distance = st_dist[ii]
                            tr.stats.stla = st_lat[ii]
                            tr.stats.stlo = st_lon[ii]
                            sgrams1_select += tr
            # if found_it == 0:
            #     print('Not on station list! station ' + tr.stats.station + ' network ' + tr.stats.network)
    print('Sgrams 1: Within ' + str(max_dist) +
          ' km distance and optional basin culling, ' +
          str(len(sgrams1_select)) + ' traces remain for plotting.')

    if traces2 != 'no':
        sgrams2_select = Stream()
        for tr in sgrams2_comm:  # examine traces one by one
            if (tr.stats.network == 'CI' or CI_only == False
                    or traces2 == 'CVM_H' or traces2 == 'CVM_S'
                    or traces2 == 'Jon_CVM_S'):
                found_it = 0
                for ii in range(
                        len(st_name)):  # find matching entry in station roster
                    if tr.stats.station == st_name[
                            ii]:  # find station in inventory
                        found_it = 1
                        flat = float(st_lat[ii])
                        flon = float(st_lon[ii])
                        if (st_dist[ii] < max_dist and st_dist[ii] > min_dist
                            ) and (flat < Lat_max and flat > Lat_min
                                   and flon < Lon_max and flon > Lon_min):
                            # exclude stations too far away or outside the map box
                            # basin is roughly within 15 km of these 3 stations
                            distance = gps2dist_azimuth(
                                33.99053, -118.36171, float(flat),
                                float(flon))  # station BHP
                            dist1 = distance[0] / 1000  # convert m to km
                            distance = gps2dist_azimuth(
                                33.88110, -118.17568, float(flat),
                                float(flon))  # station LTP
                            dist2 = distance[0] / 1000
                            distance = gps2dist_azimuth(
                                33.80776, -117.98116, float(flat),
                                float(flon))  # station BRE
                            dist3 = distance[0] / 1000
                            #                    print(tr.stats.station + ' ' + str(dist1) + ' ' + str(dist2) + ' ' + str(dist3))
                            if (basin == False) or (dist1 < basin_width) or (
                                    dist2 < basin_width
                            ) or (
                                    dist3 < basin_width
                            ):  # keep stations only within X km of basin axis
                                #                        print('selected: ' + tr.stats.station)
                                tr.stats.distance = st_dist[ii]
                                tr.stats.stla = st_lat[ii]
                                tr.stats.stlo = st_lon[ii]
                                sgrams2_select += tr
                # if found_it == 0:
                #     print('Not on station list! station ' + tr.stats.station + ' network ' + tr.stats.network)
        print('Sgrams 2: Within ' + str(max_dist) +
              ' km distance and optional basin culling, ' +
              str(len(sgrams2_select)) + ' traces remain for plotting.')
    if len(sgrams1_select) == 0:
        print('No common stations found, quit now!')
        sys.exit()

    if traces3 != 'no':
        sgrams3_select = Stream()
        for tr in sgrams3_comm:  # examine traces one by one
            if (tr.stats.network == 'CI' or CI_only == False
                    or traces3 == 'CVM_H' or traces3 == 'CVM_S'
                    or traces3 == 'Jon_CVM_S'):
                found_it = 0
                for ii in range(
                        len(st_name)):  # find matching entry in station roster
                    if tr.stats.station == st_name[
                            ii]:  # find station in inventory
                        found_it = 1
                        flat = float(st_lat[ii])
                        flon = float(st_lon[ii])
                        if (st_dist[ii] < max_dist and st_dist[ii] > min_dist
                            ) and (flat < Lat_max and flat > Lat_min
                                   and flon < Lon_max and flon > Lon_min):
                            # exclude stations too far away or outside the map box
                            # basin is roughly within 15 km of these 3 stations
                            distance = gps2dist_azimuth(
                                33.99053, -118.36171, float(flat),
                                float(flon))  # station BHP
                            dist1 = distance[0] / 1000  # convert m to km
                            distance = gps2dist_azimuth(
                                33.88110, -118.17568, float(flat),
                                float(flon))  # station LTP
                            dist2 = distance[0] / 1000
                            distance = gps2dist_azimuth(
                                33.80776, -117.98116, float(flat),
                                float(flon))  # station BRE
                            dist3 = distance[0] / 1000
                            #                    print(tr.stats.station + ' ' + str(dist1) + ' ' + str(dist2) + ' ' + str(dist3))
                            if (basin == False) or (dist1 < basin_width) or (
                                    dist2 < basin_width
                            ) or (
                                    dist3 < basin_width
                            ):  # keep stations only within X km of basin axis
                                #                        print('selected: ' + tr.stats.station)
                                tr.stats.distance = st_dist[ii]
                                tr.stats.stla = st_lat[ii]
                                tr.stats.stlo = st_lon[ii]
                                sgrams3_select += tr
                # if found_it == 0:
                #     print('Not on station list! station ' + tr.stats.station + ' network ' + tr.stats.network)
        print('Sgrams 3: Within ' + str(max_dist) +
              ' km distance and optional basin culling, ' +
              str(len(sgrams3_select)) + ' traces remain for plotting.')
    if len(sgrams1_select) == 0:
        print('No common stations found, quit now!')
        sys.exit()

    #%%  detrend, taper, filter
    if taper:
        sgrams1_select.taper(taper_frac)
        if traces2 != 'no':
            sgrams2_select.taper(taper_frac)
        if traces3 != 'no':
            sgrams3_select.taper(taper_frac)
    if filt:
        sgrams1_select.filter('bandpass',
                              freqmin=freq_min,
                              freqmax=freq_max,
                              corners=4,
                              zerophase=True)
        if traces2 != 'no':
            sgrams2_select.filter('bandpass',
                                  freqmin=freq_min,
                                  freqmax=freq_max,
                                  corners=4,
                                  zerophase=True)
        if traces3 != 'no':
            sgrams3_select.filter('bandpass',
                                  freqmin=freq_min,
                                  freqmax=freq_max,
                                  corners=4,
                                  zerophase=True)
    if taper:
        sgrams1_select.taper(taper_frac)
        if traces2 != 'no':
            sgrams2_select.taper(taper_frac)
        if traces3 != 'no':
            sgrams3_select.taper(taper_frac)

    #%% plot traces, different windows in case multiple components are to be compared
    if component1 == 'Z':
        fig_index = 13 + fig_inc
    elif component1 == 'N':
        fig_index = 14 + fig_inc
    elif component1 == 'E':
        fig_index = 15 + fig_inc
    elif component1 == 'R':
        fig_index = 16 + fig_inc
    elif component1 == 'T':
        fig_index = 17 + fig_inc

    plt.close(fig_index)
    plt.figure(fig_index, figsize=(12, 12))
    plt.xlim(start_buff, end_buff)
    plt.ylim(min_dist, max_dist)

    # find max of absolute amplitude
    maxD1 = 0
    for tr in sgrams1_select:
        tr_max = max(abs(tr.data))
        if tr_max > maxD1:
            maxD = tr_max

    if traces2 != 'no':
        maxD2 = 0
        for tr in sgrams2_select:
            tr_max = max(abs(tr.data))
            if tr_max > maxD2:
                maxD2 = tr_max
        maxD = max(maxD, maxD2)

    if traces3 != 'no':
        maxD3 = 0
        for tr in sgrams3_select:
            tr_max = max(abs(tr.data))
            if tr_max > maxD3:
                maxD3 = tr_max
        maxD = max(maxD, maxD3)

    # find max normalized to 10 km distance (i.e., amp divided by distance, assumes 1/R amp fall-off)
    maxD_N = 0
    for tr in sgrams1_select:
        if verbose == True:
            print(f'Distance is {tr.stats.distance:6.4f}' + '  ' +
                  tr.stats.station)
        tr_max = max(abs(tr.data)) * tr.stats.distance
        if tr_max > maxD_N:
            maxD_N = tr_max

    print(f'Peak amp of trace1 is {maxD1:6.4f}')
    if traces2 != 'no':
        print(f'Peak amp of trace2 is {maxD2:6.4f}')
        print(f'Peak amp of both traces is {maxD:6.4f}')
    if traces3 != 'no':
        print(f'Peak amp of trace3 is {maxD3:6.4f}')
        print(f'Peak amp of both traces is {maxD:6.4f}')
    print(f'Normed amp of data is {maxD_N:6.4f}')
    plot_fac = plot_scale_fac * (max_dist - min_dist) / maxD_N

    for tr in sgrams1_select:
        dist_offset = tr.stats.distance  # km
        if dist_offset > min_dist and dist_offset < max_dist:  # distance range to plot
            pnum = max(abs(tr.data))
            printee2 = f'{tr.stats.station} {pnum:5.3f} cm/s'
            plt.text(s=printee2,
                     x=end_buff * 0.7,
                     y=dist_offset + (max_dist - min_dist) * 0.02,
                     color='black'
                     )  #label traces and note amplitude (cm, cm/s, cm/s^^2)
            if traces1 == 'CVM_H' or traces1 == 'CVM_S' or traces1 == 'Jon_CVM_S':
                tr.stats.starttime = t1
#             print('1st gather: trace ' + tr.stats.station + 'starts at ' + str(tr.stats.starttime))
            ttt = np.arange(len(tr.data)) * tr.stats.delta + (
                tr.stats.starttime -
                t1) - shift1  # impose manual shift of shift1
            color_p = 'red'
            if traces2 == 'no':
                color_p = 'black'
            if norm_each:
                plt.plot(ttt, (tr.data - np.median(tr.data)) * plot_scale_fac /
                         (tr.data.max() - tr.data.min()) + dist_offset,
                         color=color_p)
            elif dist_norm == True:  # normalized to 20 km distance
                plt.plot(ttt, (tr.data * plot_fac *
                               (tr.stats.distance / 20.)) + dist_offset,
                         color=color_p)
            else:
                plt.plot(ttt, (tr.data * plot_fac) + dist_offset,
                         color=color_p)

    if traces2 != 'no':
        for tr in sgrams2_select:
            dist_offset = tr.stats.distance
            if dist_offset > min_dist and dist_offset < max_dist:
                if traces2 == 'CVM_H' or traces2 == 'CVM_S' or traces2 == 'Jon_CVM_S':
                    tr.stats.starttime = t1
#                 print('2nd gather: trace ' + tr.stats.station + 'starts at ' + str(tr.stats.starttime))
                ttt = np.arange(len(tr.data)) * tr.stats.delta + (
                    tr.stats.starttime -
                    t1) - shift2  # impose manual shift of shift2 seconds
                if norm_each:
                    plt.plot(ttt,
                             (tr.data - np.median(tr.data)) * plot_scale_fac /
                             (tr.data.max() - tr.data.min()) + dist_offset,
                             color='green')
                elif dist_norm == True:  # normalized to 20 km distance
                    plt.plot(ttt, (tr.data * plot_fac *
                                   (tr.stats.distance / 20.)) + dist_offset,
                             color='green')
                else:
                    plt.plot(ttt, (tr.data * plot_fac) + dist_offset,
                             color='green')

    if traces3 != 'no':
        for tr in sgrams3_select:
            dist_offset = tr.stats.distance
            if dist_offset > min_dist and dist_offset < max_dist:
                if traces3 == 'CVM_H' or traces3 == 'CVM_S' or traces3 == 'Jon_CVM_S':
                    tr.stats.starttime = t1
#                 print('3rd gather: trace ' + tr.stats.station + 'starts at ' + str(tr.stats.starttime))
                ttt = np.arange(len(tr.data)) * tr.stats.delta + (
                    tr.stats.starttime -
                    t1) - shift3  # impose manual shift of shift2 seconds
                if norm_each:
                    plt.plot(ttt,
                             (tr.data - np.median(tr.data)) * plot_scale_fac /
                             (tr.data.max() - tr.data.min()) + dist_offset,
                             color='blue')
                elif dist_norm == True:  # normalized to 20 km distance
                    plt.plot(ttt, (tr.data * plot_fac *
                                   (tr.stats.distance / 20.)) + dist_offset,
                             color='blue')
                else:
                    plt.plot(ttt, (tr.data * plot_fac) + dist_offset,
                             color='blue')

    plt.xlabel('Time (s)')
    plt.ylabel('Epicentral distance from event (km)')
    #     plt.title(fname1[8:18] + ' vs ' + fname2[8:18] + ' vs ' + fname3[8:18])
    if traces2 != 'no':
        if traces3 != 'no':
            plt.title(event_dir + '  R:  ' + traces1 + ' ' + component1 +
                      '  Green:  ' + traces2 + ' ' + component2 + '  Blue:  ' +
                      traces3 + ' ' + component3)
        else:
            plt.title(event_dir + '  Red: ' + traces1 + ' ' + component1 +
                      '   Green:  ' + traces2 + ' ' + component2)
    else:
        plt.title(event_dir + ' ' + traces1 + ' ' + component1)
    plt.show()

    elapsed_time_wc = time.time() - start_time_wc
    print('This job took ' + str(elapsed_time_wc) + ' seconds')
    os.system('say "Done"')
Пример #28
0
def waveforms_matrix(home,project_name,fault_name,rupture_name,station_file,GF_list,
                model_name,run_name,epicenter,time_epi,integrate,tsunami,hot_start,
                resample,beta,rupture_speed,num_windows,dt,NFFT):
    '''
    To supplant waveforms() it needs to include resmapling and all that jazz...
    
    This routine will take synthetics and apply a slip dsitribution. It will delay each 
    subfault by the appropriate rupture time and linearly superimpose all of them. Output
    will be one sac waveform file per direction of motion (NEU) for each station defined in the
    station_file. Depending on the specified rake angle at each subfault the code will compute 
    the contribution to dip and strike slip directions. It will also compute the moment at that
    subfault and scale it according to the unit amount of momeent (1e15 N-m)
    
    IN:
        home: Home directory
        project_name: Name of the problem
        rupture_name: Name of rupture description file
        station_file: File with coordinates of stations
        model_Name: Name of Earth structure model file
        integrate: =0 if you want output to be velocity, =1 if you want output to de displacement
       
    OUT:
        Nothing
    '''
    from numpy import loadtxt,genfromtxt,allclose,vstack,deg2rad,array,sin,cos,where,zeros,arange
    from obspy import read,Stream,Trace
    from string import rjust
    import datetime
    import gc
    from mudpy.inverse import getG
    from linecache import getline
    from os import remove
    
    print 'Solving for kinematic problem'
    #Output where?
    outpath=home+project_name+'/output/forward_models/'
    logpath=home+project_name+'/logs/'
    log=''
    #Time for log file
    now=datetime.datetime.now()
    now=now.strftime('%b-%d-%H%M')
    #load source
    #source=loadtxt(home+project_name+'/forward_models/'+rupture_name,ndmin=2)
    #Load stations
    station_file=home+project_name+'/data/station_info/'+station_file
    staname=genfromtxt(station_file,dtype="S6",usecols=0)
    #Now load rupture model
    mss=genfromtxt(home+project_name+'/forward_models/'+rupture_name,usecols=8)
    mds=genfromtxt(home+project_name+'/forward_models/'+rupture_name,usecols=9)
    m=zeros(2*len(mss))
    i=arange(0,2*len(mss),2)
    m[i]=mss
    i=arange(1,2*len(mds),2)
    m[i]=mds
    #What am I processing v or d ?
    if integrate==1:
        vord='disp'
    else:
        vord='vel'
    #Load gflist
    gfsta=genfromtxt(home+project_name+'/data/station_info/'+GF_list,usecols=0,skip_header=1,dtype='S6')
    #Loop over stations
    for ksta in range(hot_start,len(staname)):
        print 'Working on station '+staname[ksta]+' ('+str(ksta+1)+'/'+str(len(staname))+')'
        #Initalize output
        n=Stream()
        e=Stream()
        z=Stream()
        sta=staname[ksta]
        #Make dummy data
        ndummy=Stream(Trace())
        ndummy[0].data=zeros(int(NFFT))
        ndummy[0].stats.delta=dt
        ndummy[0].stats.starttime=time_epi
        edummy=ndummy.copy()
        udummy=ndummy.copy()
        ndummy.write(home+project_name+'/data/waveforms/'+sta+'.'+vord+'.n',format='SAC')
        edummy.write(home+project_name+'/data/waveforms/'+sta+'.'+vord+'.e',format='SAC')
        udummy.write(home+project_name+'/data/waveforms/'+sta+'.'+vord+'.u',format='SAC')
        #Extract only one station from GF_list file
        ista=int(where(gfsta==staname[ksta])[0])+2
        #Make mini GF_file
        tmpgf='tmpfwd.gflist'
        try:
            remove(home+project_name+'/data/station_info/'+tmpgf)
        except:
            pass
        gflist=getline(home+project_name+'/data/station_info/'+GF_list,ista)
        f=open(home+project_name+'/data/station_info/'+tmpgf,'w')
        f.write('# Headers\n')
        f.write(gflist)
        f.close()
        #Get matrix for one station to all sources
        G_from_file=False
        G_name='tmpfwd'
        G=getG(home,project_name,fault_name,model_name,tmpgf,G_from_file,G_name,epicenter,
                rupture_speed,num_windows,decimate=None,bandpass=None,tsunami=False)
        # Matrix multiply and separate data streams
        d=G.dot(m)
        n=ndummy.copy()
        e=edummy.copy()
        u=udummy.copy()
        ncut=len(d)/3
        n[0].data=d[0:ncut]
        e[0].data=d[ncut:2*ncut]
        u[0].data=d[2*ncut:3*ncut]
        # Write to file
        n.write(home+project_name+'/output/forward_models/'+run_name+'.'+gfsta[ksta]+'.'+vord+'.n',format='SAC')
        e.write(home+project_name+'/output/forward_models/'+run_name+'.'+gfsta[ksta]+'.'+vord+'.e',format='SAC')
        u.write(home+project_name+'/output/forward_models/'+run_name+'.'+gfsta[ksta]+'.'+vord+'.u',format='SAC')
Пример #29
0
def obtain_timeshifts(st):
    '''function to compute time shift of overlapping files              '''
    '''st: obspy stream containing one station channel pair             '''

    # check if all traces are from same station/channel
    for tr in st:
        if tr.stats.station == st[0].stats.station:
            pass
        else:
            raise NameError("Traces in st recorded by different stations")
        if tr.stats.channel == st[0].stats.channel:
            pass
        else:
            raise NameError("Traces in st recorded on different channels")

    # empty stream for time corrected traces; add first trace
    cor = Stream()
    # "take_previous" determines if first trace is subject to syncronization to previous mseed file
    take_previous = False
    # check if previous mseed file is available
    try:
        stn = st[0].stats.station
        chn = st[0].stats.channel
        stime = st[0].stats.starttime
        prev = read("/scratch/flindner/G7Jul01/MSEED/4D.%s..%s.%04d%02d%02d%02d*" \
                % (stn, chn, stime.year, stime.month, stime.day, stime.hour), starttime=stime - 5)
        cor += prev[0]
        # available -> synchronize
        take_previous = True
    except:
        pass
    # in case of no sync to previous file, start with timeshift between first and second trace
    if take_previous == False:
        cor += st[0]
        st = st[1:]
        warnings.warn("Synchronization to previous MSEED file failed!")
    # empty list for dts
    dts = []
    
    # add next trace
    for i, tr in enumerate(st):
        cor += tr
        # helper stream - overlap
        helper = cor.copy()
        t1 = helper[1].stats.starttime
        t2 = helper[0].stats.endtime
        helper.trim(t1, t2)
        delta = helper[0].stats.delta
        npts = helper[0].stats.npts
        # even number of samples
        if (npts % 2) != 0: 
            helper.trim(t1, t2-delta)
            delta = helper[0].stats.delta
        
        # cross-correlation in order to obtain timeshift
        xcorr = np.correlate(helper[0].data, helper[1].data, "same")
        xcorr = Trace(data=xcorr)
        xcorr.stats.delta = delta
        xcorr.resample(1000)
        ndelta = xcorr.stats.delta
        nnpts = xcorr.stats.npts
        dt = (np.argmax(xcorr.data) - nnpts/2.) * ndelta
        dt = round(dt, 3)
        # add estimated time shift to second trace
        cor[1].stats.starttime += dt
    
        # test if time series are perfectly aligned
        check = True
        count = 0
        while check:
            start = cor[1].stats.starttime
            end = cor[0].stats.endtime
            test = cor.slice(start, end)
            eq_st = False
            if round(test[0].stats.starttime.timestamp, 4) == round(test[1].stats.starttime.timestamp, 4):
                eq_st = True
            eq_ar = np.array_equal(test[0].data, test[1].data)
            if eq_ar == True and eq_st == True:
                # if perfectly aligned, quit testing loop
                check = False  
                if count == 0:
                    dts.append(dt) 
            # if not perfectly aligned, undo time shift obtained by xcorr and shift manually
            else:
                if count == 0:
                    # undo time shift obtained from xcorr
                    cor[1].stats.starttime -= dt
                    mshift = int((end - start) / delta) + 1000
                    cor[1].stats.starttime -= mshift*delta
                elif count > 0:
                    cor[1].stats.starttime += delta
                cor[1].stats.starttime.microsecond = np.round(cor[1].stats.starttime.microsecond, 3)
            count += 1
        # in case of take_previous == True, remove previous file from stream (was just needed for time sync)
        if i==0 and take_previous:
            for tr in cor.select(network="4D"):
                cor.remove(tr)
        # time shift obtained from shifting
        if count > 1:
            dt = (-mshift + count - 2) * delta
            dts.append(dt) 

        cor.merge(method=1)
    # check number of written times shifts
    dts = np.asarray(dts)
    if len(dts) != len(st):
        raise ValueError("Number of time shifts is not correct")
    return dts, take_previous
Пример #30
0
    def test_coincidence_trigger(self):
        """
        Test network coincidence trigger.
        """
        st = Stream()
        files = ["BW.UH1._.SHZ.D.2010.147.cut.slist.gz",
                 "BW.UH2._.SHZ.D.2010.147.cut.slist.gz",
                 "BW.UH3._.SHZ.D.2010.147.cut.slist.gz",
                 "BW.UH4._.EHZ.D.2010.147.cut.slist.gz"]
        for filename in files:
            filename = os.path.join(self.path, filename)
            st += read(filename)
        # some prefiltering used for UH network
        st.filter('bandpass', freqmin=10, freqmax=20)

        # 1. no weighting, no stations specified, good settings
        # => 3 events, no false triggers
        # for the first test we make some additional tests regarding types
        res = coincidence_trigger("recstalta", 3.5, 1, st.copy(), 3, sta=0.5,
                                  lta=10)
        self.assertTrue(isinstance(res, list))
        self.assertEqual(len(res), 3)
        expected_keys = ['time', 'coincidence_sum', 'duration', 'stations',
                         'trace_ids']
        expected_types = [UTCDateTime, float, float, list, list]
        for item in res:
            self.assertTrue(isinstance(item, dict))
            for key, _type in zip(expected_keys, expected_types):
                self.assertIn(key, item)
                self.assertTrue(isinstance(item[key], _type))
        self.assertGreater(res[0]['time'], UTCDateTime("2010-05-27T16:24:31"))
        self.assertTrue(res[0]['time'] < UTCDateTime("2010-05-27T16:24:35"))
        self.assertTrue(4.2 < res[0]['duration'] < 4.8)
        self.assertEqual(res[0]['stations'], ['UH3', 'UH2', 'UH1', 'UH4'])
        self.assertEqual(res[0]['coincidence_sum'], 4)
        self.assertGreater(res[1]['time'], UTCDateTime("2010-05-27T16:26:59"))
        self.assertTrue(res[1]['time'] < UTCDateTime("2010-05-27T16:27:03"))
        self.assertTrue(3.2 < res[1]['duration'] < 3.7)
        self.assertEqual(res[1]['stations'], ['UH2', 'UH3', 'UH1'])
        self.assertEqual(res[1]['coincidence_sum'], 3)
        self.assertGreater(res[2]['time'], UTCDateTime("2010-05-27T16:27:27"))
        self.assertTrue(res[2]['time'] < UTCDateTime("2010-05-27T16:27:33"))
        self.assertTrue(4.2 < res[2]['duration'] < 4.4)
        self.assertEqual(res[2]['stations'], ['UH3', 'UH2', 'UH1', 'UH4'])
        self.assertEqual(res[2]['coincidence_sum'], 4)

        # 2. no weighting, station selection
        # => 2 events, no false triggers
        trace_ids = ['BW.UH1..SHZ', 'BW.UH3..SHZ', 'BW.UH4..EHZ']
        # raises "UserWarning: At least one trace's ID was not found"
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always', UserWarning)
            re = coincidence_trigger("recstalta", 3.5, 1, st.copy(), 3,
                                     trace_ids=trace_ids, sta=0.5, lta=10)
            self.assertEqual(len(w), 1)
            self.assertIn("At least one trace's ID was not", str(w[0]))
        self.assertEqual(len(re), 2)
        self.assertGreater(re[0]['time'],
                           UTCDateTime("2010-05-27T16:24:31"))
        self.assertTrue(re[0]['time'] < UTCDateTime("2010-05-27T16:24:35"))
        self.assertTrue(4.2 < re[0]['duration'] < 4.8)
        self.assertEqual(re[0]['stations'], ['UH3', 'UH1', 'UH4'])
        self.assertEqual(re[0]['coincidence_sum'], 3)
        self.assertGreater(re[1]['time'],
                           UTCDateTime("2010-05-27T16:27:27"))
        self.assertTrue(re[1]['time'] < UTCDateTime("2010-05-27T16:27:33"))
        self.assertTrue(4.2 < re[1]['duration'] < 4.4)
        self.assertEqual(re[1]['stations'], ['UH3', 'UH1', 'UH4'])
        self.assertEqual(re[1]['coincidence_sum'], 3)

        # 3. weighting, station selection
        # => 3 events, no false triggers
        trace_ids = {'BW.UH1..SHZ': 0.4, 'BW.UH2..SHZ': 0.35,
                     'BW.UH3..SHZ': 0.4, 'BW.UH4..EHZ': 0.25}
        res = coincidence_trigger("recstalta", 3.5, 1, st.copy(), 1.0,
                                  trace_ids=trace_ids, sta=0.5, lta=10)
        self.assertEqual(len(res), 3)
        self.assertGreater(res[0]['time'], UTCDateTime("2010-05-27T16:24:31"))
        self.assertTrue(res[0]['time'] < UTCDateTime("2010-05-27T16:24:35"))
        self.assertTrue(4.2 < res[0]['duration'] < 4.8)
        self.assertEqual(res[0]['stations'], ['UH3', 'UH2', 'UH1', 'UH4'])
        self.assertEqual(res[0]['coincidence_sum'], 1.4)
        self.assertGreater(res[1]['time'], UTCDateTime("2010-05-27T16:26:59"))
        self.assertTrue(res[1]['time'] < UTCDateTime("2010-05-27T16:27:03"))
        self.assertTrue(3.2 < res[1]['duration'] < 3.7)
        self.assertEqual(res[1]['stations'], ['UH2', 'UH3', 'UH1'])
        self.assertEqual(res[1]['coincidence_sum'], 1.15)
        self.assertGreater(res[2]['time'], UTCDateTime("2010-05-27T16:27:27"))
        self.assertTrue(res[2]['time'] < UTCDateTime("2010-05-27T16:27:33"))
        self.assertTrue(4.2 < res[2]['duration'] < 4.4)
        self.assertEqual(res[2]['stations'], ['UH3', 'UH2', 'UH1', 'UH4'])
        self.assertEqual(res[2]['coincidence_sum'], 1.4)

        # 4. weighting, station selection, max_len
        # => 2 events, no false triggers, small event does not overlap anymore
        trace_ids = {'BW.UH1..SHZ': 0.6, 'BW.UH2..SHZ': 0.6}
        # raises "UserWarning: At least one trace's ID was not found"
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always', UserWarning)
            re = coincidence_trigger("recstalta", 3.5, 1, st.copy(), 1.2,
                                     trace_ids=trace_ids,
                                     max_trigger_length=0.13, sta=0.5, lta=10)
            self.assertEqual(len(w), 2)
            self.assertIn("At least one trace's ID was not", str(w[0]))
            self.assertIn("At least one trace's ID was not", str(w[1]))
        self.assertEqual(len(re), 2)
        self.assertGreater(re[0]['time'],
                           UTCDateTime("2010-05-27T16:24:31"))
        self.assertTrue(re[0]['time'] < UTCDateTime("2010-05-27T16:24:35"))
        self.assertTrue(0.2 < re[0]['duration'] < 0.3)
        self.assertEqual(re[0]['stations'], ['UH2', 'UH1'])
        self.assertEqual(re[0]['coincidence_sum'], 1.2)
        self.assertGreater(re[1]['time'],
                           UTCDateTime("2010-05-27T16:27:27"))
        self.assertTrue(re[1]['time'] < UTCDateTime("2010-05-27T16:27:33"))
        self.assertTrue(0.18 < re[1]['duration'] < 0.2)
        self.assertEqual(re[1]['stations'], ['UH2', 'UH1'])
        self.assertEqual(re[1]['coincidence_sum'], 1.2)

        # 5. station selection, extremely sensitive settings
        # => 4 events, 1 false triggers
        # raises "UserWarning: At least one trace's ID was not found"
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always', UserWarning)
            res = coincidence_trigger("recstalta", 2.5, 1, st.copy(), 2,
                                      trace_ids=['BW.UH1..SHZ', 'BW.UH3..SHZ'],
                                      sta=0.3, lta=5)
            self.assertEqual(len(w), 2)
            self.assertIn("At least one trace's ID was not", str(w[0]))
            self.assertIn("At least one trace's ID was not", str(w[1]))
        self.assertEqual(len(res), 5)
        self.assertGreater(res[3]['time'], UTCDateTime("2010-05-27T16:27:01"))
        self.assertTrue(res[3]['time'] < UTCDateTime("2010-05-27T16:27:02"))
        self.assertTrue(1.5 < res[3]['duration'] < 1.7)
        self.assertEqual(res[3]['stations'], ['UH3', 'UH1'])
        self.assertEqual(res[3]['coincidence_sum'], 2.0)

        # 6. same as 5, gappy stream
        # => same as 5 (almost, duration of 1 event changes by 0.02s)
        st2 = st.copy()
        tr1 = st2.pop(0)
        t1 = tr1.stats.starttime
        t2 = tr1.stats.endtime
        td = t2 - t1
        tr1a = tr1.slice(starttime=t1, endtime=t1 + 0.45 * td)
        tr1b = tr1.slice(starttime=t1 + 0.6 * td, endtime=t1 + 0.94 * td)
        st2.insert(1, tr1a)
        st2.insert(3, tr1b)
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always', UserWarning)
            res = coincidence_trigger("recstalta", 2.5, 1, st2, 2,
                                      trace_ids=['BW.UH1..SHZ', 'BW.UH3..SHZ'],
                                      sta=0.3, lta=5)
            self.assertEqual(len(w), 2)
            self.assertIn("At least one trace's ID was not", str(w[0]))
            self.assertIn("At least one trace's ID was not", str(w[1]))
        self.assertEqual(len(res), 5)
        self.assertGreater(res[3]['time'], UTCDateTime("2010-05-27T16:27:01"))
        self.assertTrue(res[3]['time'] < UTCDateTime("2010-05-27T16:27:02"))
        self.assertTrue(1.5 < res[3]['duration'] < 1.7)
        self.assertEqual(res[3]['stations'], ['UH3', 'UH1'])
        self.assertEqual(res[3]['coincidence_sum'], 2.0)

        # 7. same as 3 but modify input trace ids and check output of trace_ids
        # and other additional information with ``details=True``
        st2 = st.copy()
        st2[0].stats.network = "XX"
        st2[1].stats.location = "99"
        st2[1].stats.network = ""
        st2[1].stats.location = "99"
        st2[1].stats.channel = ""
        st2[2].stats.channel = "EHN"
        st2[3].stats.network = ""
        st2[3].stats.channel = ""
        st2[3].stats.station = ""
        trace_ids = {'XX.UH1..SHZ': 0.4, '.UH2.99.': 0.35,
                     'BW.UH3..EHN': 0.4, '...': 0.25}
        res = coincidence_trigger("recstalta", 3.5, 1, st2, 1.0,
                                  trace_ids=trace_ids, details=True,
                                  sta=0.5, lta=10)
        self.assertEqual(len(res), 3)
        self.assertGreater(res[0]['time'], UTCDateTime("2010-05-27T16:24:31"))
        self.assertTrue(res[0]['time'] < UTCDateTime("2010-05-27T16:24:35"))
        self.assertTrue(4.2 < res[0]['duration'] < 4.8)
        self.assertEqual(res[0]['stations'], ['UH3', 'UH2', 'UH1', ''])
        self.assertEqual(res[0]['trace_ids'][0], st2[2].id)
        self.assertEqual(res[0]['trace_ids'][1], st2[1].id)
        self.assertEqual(res[0]['trace_ids'][2], st2[0].id)
        self.assertEqual(res[0]['trace_ids'][3], st2[3].id)
        self.assertEqual(res[0]['coincidence_sum'], 1.4)
        self.assertGreater(res[1]['time'], UTCDateTime("2010-05-27T16:26:59"))
        self.assertTrue(res[1]['time'] < UTCDateTime("2010-05-27T16:27:03"))
        self.assertTrue(3.2 < res[1]['duration'] < 3.7)
        self.assertEqual(res[1]['stations'], ['UH2', 'UH3', 'UH1'])
        self.assertEqual(res[1]['trace_ids'][0], st2[1].id)
        self.assertEqual(res[1]['trace_ids'][1], st2[2].id)
        self.assertEqual(res[1]['trace_ids'][2], st2[0].id)
        self.assertEqual(res[1]['coincidence_sum'], 1.15)
        self.assertGreater(res[2]['time'], UTCDateTime("2010-05-27T16:27:27"))
        self.assertTrue(res[2]['time'] < UTCDateTime("2010-05-27T16:27:33"))
        self.assertTrue(4.2 < res[2]['duration'] < 4.4)
        self.assertEqual(res[2]['stations'], ['UH3', 'UH2', 'UH1', ''])
        self.assertEqual(res[2]['trace_ids'][0], st2[2].id)
        self.assertEqual(res[2]['trace_ids'][1], st2[1].id)
        self.assertEqual(res[2]['trace_ids'][2], st2[0].id)
        self.assertEqual(res[2]['trace_ids'][3], st2[3].id)
        self.assertEqual(res[2]['coincidence_sum'], 1.4)
        expected_keys = ['cft_peak_wmean', 'cft_std_wmean', 'cft_peaks',
                         'cft_stds']
        expected_types = [float, float, list, list]
        for item in res:
            for key, _type in zip(expected_keys, expected_types):
                self.assertIn(key, item)
                self.assertTrue(isinstance(item[key], _type))
        # check some of the detailed info
        ev = res[-1]
        self.assertAlmostEqual(ev['cft_peak_wmean'], 18.101139518271076)
        self.assertAlmostEqual(ev['cft_std_wmean'], 4.800051726246676)
        self.assertAlmostEqual(ev['cft_peaks'][0], 18.985548683223936)
        self.assertAlmostEqual(ev['cft_peaks'][1], 16.852175794415011)
        self.assertAlmostEqual(ev['cft_peaks'][2], 18.64005853900883)
        self.assertAlmostEqual(ev['cft_peaks'][3], 17.572363634564621)
        self.assertAlmostEqual(ev['cft_stds'][0], 4.8909448258821362)
        self.assertAlmostEqual(ev['cft_stds'][1], 4.4446373508521804)
        self.assertAlmostEqual(ev['cft_stds'][2], 5.3499401252675964)
        self.assertAlmostEqual(ev['cft_stds'][3], 4.2723814539487703)
Пример #31
0
class RFData(object):
    """
    A RFData object contains Class attributes that associate
    station information with a single event (i.e., earthquake) 
    metadata, corresponding raw and rotated seismograms and 
    receiver functions.

    Note
    ----
    The object is initialized with the ``sta`` field only, and
    other attributes are added to the object as the analysis proceeds.

    Parameters
    ----------
    sta : object
        Object containing station information - from :mod:`~stdb` database.
    meta : :class:`~rfpy.rfdata.Meta`
        Object of metadata information for single event (initially set to None)
    data : :class:`~obspy.core.Stream`
        Stream object containing the three-component seismograms (either
        un-rotated or rotated by the method :func:`~rfpy.rfdata.rotate`)

    """
    def __init__(self, sta):

        # Load example data if initializing empty object
        if sta == 'demo' or sta == 'Demo':
            print("Uploading demo data - station NY.MMPY")
            import os
            import pickle
            sta = pickle.load(
                open(
                    os.path.join(os.path.dirname(__file__), "examples/data",
                                 "MMPY.pkl"), 'rb'))['NY.MMPY']

        # Attributes from parameters
        self.sta = sta

        # Initialize meta and data objects as None
        self.meta = None
        self.data = None

    def add_event(self,
                  event,
                  gacmin=30.,
                  gacmax=90.,
                  phase='P',
                  returned=False):
        """
        Adds event metadata to RFData object, including travel time info 
        of P wave. 

        Parameters
        ----------
        event : :class:`~obspy.core.event`
            Event XML object
        returned : bool
            Whether or not to return the ``accept`` attribute

        Returns
        -------
        accept : bool
            Whether or not the object is accepted for further analysis

        """
        from obspy.geodetics.base import gps2dist_azimuth as epi
        from obspy.geodetics import kilometer2degrees as k2d
        from obspy.taup import TauPyModel
        from obspy.core.event.event import Event

        if event == 'demo' or event == 'Demo':
            from obspy.clients.fdsn import Client
            from obspy.core import UTCDateTime
            client = Client()
            # Get catalogue using deployment start and end
            event = client.get_events(
                starttime=UTCDateTime('2014-06-30T19:00:00'),
                endtime=UTCDateTime('2014-06-30T21:00:00'),
                minmagnitude=6.0,
                maxmagnitude=6.5)[0]
            print(event.short_str())

        if not isinstance(event, Event):
            raise (Exception("Event has incorrect type"))

        # Store as object attributes
        self.meta = Meta(sta=self.sta,
                         event=event,
                         gacmin=gacmin,
                         gacmax=gacmax,
                         phase=phase)

        if returned:
            return self.meta.accept

    def add_data(self, stream, returned=False, new_sr=5.):
        """
        Adds stream as object attribute

        Parameters
        ----------
        stream : :class:`~obspy.core.Stream`
            Stream container for NEZ seismograms
        returned : bool
            Whether or not to return the ``accept`` attribute

        Attributes
        ----------
        zne_data : :class:`~obspy.core.Stream`
            Stream container for NEZ seismograms

        Returns
        -------
        accept : bool
            Whether or not the object is accepted for further analysis

        """

        if not self.meta:
            raise (Exception("No meta data available - aborting"))

        if not self.meta.accept:
            return

        # Load demo data
        if stream == 'demo' or stream == 'Demo':
            import os
            import pickle
            file = open(
                os.path.join(os.path.dirname(__file__), "examples/data",
                             "ZNE_Data.pkl"), "rb")
            stream = pickle.load(file)
            print(stream)

        if not isinstance(stream, Stream):
            raise (Exception("Event has incorrect type"))

        try:
            self.data = stream

            if not np.allclose([tr.stats.npts
                                for tr in stream[1:]], stream[0].stats.npts):
                self.meta.accept = False

            # Filter Traces
            if not stream[0].stats.sampling_rate == new_sr:
                self.data.filter('lowpass',
                                 freq=0.5 * new_sr,
                                 corners=2,
                                 zerophase=True)
                self.data.resample(new_sr, no_filter=True)

        except:
            print("Error: Not all channels are available")
            self.meta.accept = False

        if returned:
            return self.meta.accept

    def download_data(self,
                      client,
                      stdata=[],
                      ndval=np.nan,
                      new_sr=5.,
                      dts=120.,
                      returned=False,
                      verbose=False):
        """
        Downloads seismograms based on event origin time and
        P phase arrival.

        Parameters
        ----------
        client : :class:`~obspy.client.fdsn.Client`
            Client object
        ndval : float
            Fill in value for missing data
        new_sr : float
            New sampling rate (Hz)
        dts : float
            Time duration (sec)
        stdata : List
            Station list
        returned : bool
            Whether or not to return the ``accept`` attribute

        Returns
        -------
        accept : bool
            Whether or not the object is accepted for further analysis

        Attributes
        ----------
        data : :class:`~obspy.core.Stream`
            Stream containing :class:`~obspy.core.Trace` objects

        """

        if self.meta is None:
            raise (Exception("Requires event data as attribute - aborting"))

        if not self.meta.accept:
            return

        # Define start time for request
        tstart = self.meta.time + self.meta.ttime - dts
        tend = self.meta.time + self.meta.ttime + dts

        # Get waveforms
        print("* Requesting Waveforms: ")
        print("*    Startime: " + tstart.strftime("%Y-%m-%d %H:%M:%S"))
        print("*    Endtime:  " + tend.strftime("%Y-%m-%d %H:%M:%S"))

        # Download data
        err, stream = utils.download_data(client=client,
                                          sta=self.sta,
                                          start=tstart,
                                          end=tend,
                                          stdata=stdata,
                                          ndval=ndval,
                                          new_sr=new_sr,
                                          verbose=verbose)

        # Store as attributes with traces in dictionary
        try:
            trE = stream.select(component='E')[0]
            trN = stream.select(component='N')[0]
            trZ = stream.select(component='Z')[0]
            self.data = Stream(traces=[trZ, trN, trE])

            # Filter Traces and resample
            self.data.filter('lowpass',
                             freq=0.5 * new_sr,
                             corners=2,
                             zerophase=True)
            self.data.resample(new_sr, no_filter=True)

        # If there is no ZNE, perhaps there is Z12?
        except:

            try:
                tr1 = stream.select(component='1')[0]
                tr2 = stream.select(component='2')[0]
                trZ = stream.select(component='Z')[0]
                self.data = Stream(traces=[trZ, tr1, tr2])

                # Filter Traces and resample
                self.data.filter('lowpass',
                                 freq=0.5 * new_sr,
                                 corners=2,
                                 zerophase=True)
                self.data.resample(new_sr, no_filter=True)

                # Save Z12 components in case it's necessary for later
                self.dataZ12 = self.data.copy()

                # Rotate from Z12 to ZNE using StDb azcorr attribute
                self.rotate(align='ZNE')

            except:
                self.meta.accept = False

        if returned:
            return self.meta.accept

    def rotate(self, vp=None, vs=None, align=None):
        """
        Rotates 3-component seismograms from vertical (Z),
        east (E) and north (N) to longitudinal (L), 
        radial (Q) and tangential (T) components of motion.
        Note that the method 'rotate' from ``obspy.core.stream.Stream``
        is used for the rotation ``'ZNE->ZRT'`` and ``'ZNE->LQT'``.
        Rotation ``'ZNE->PVH'`` is implemented separately here 
        due to different conventions.

        Parameters
        ----------
        vp : float
            P-wave velocity at surface (km/s)
        vs : float
            S-wave velocity at surface (km/s)
        align : str
            Alignment of coordinate system for rotation
            ('ZRT', 'LQT', or 'PVH')

        Returns
        -------
        rotated : bool
            Whether or not the object has been rotated

        """

        if not self.meta.accept:
            return

        if self.meta.rotated:
            print("Data have been rotated already - continuing")
            return

        # Use default values from meta data if arguments are not specified
        if not align:
            align = self.meta.align

        if align == 'ZNE':
            from obspy.signal.rotate import rotate2zne

            # Copy traces
            trZ = self.data.select(component='Z')[0].copy()
            trN = self.data.select(component='1')[0].copy()
            trE = self.data.select(component='2')[0].copy()

            azim = self.sta.azcorr

            # Try EBS with left handed system
            Z, N, E = rotate2zne(trZ.data, 0., -90., trN.data, azim, 0.,
                                 trE.data, azim + 90., 0.)

            # Z, N, E = rotate2zne(trZ.data, 0., -90., trN.data,
            #                      azim, 0., trE.data, azim+90., 0.)
            trN.data = N
            trE.data = E

            # Update stats of streams
            trN.stats.channel = trN.stats.channel[:-1] + 'N'
            trE.stats.channel = trE.stats.channel[:-1] + 'E'

            self.data = Stream(traces=[trZ, trN, trE])

        elif align == 'ZRT':
            self.data.rotate('NE->RT', back_azimuth=self.meta.baz)
            self.meta.align = align
            self.meta.rotated = True

        elif align == 'LQT':
            self.data.rotate('ZNE->LQT',
                             back_azimuth=self.meta.baz,
                             inclination=self.meta.inc)
            for tr in self.data:
                if tr.stats.channel.endswith('Q'):
                    tr.data = -tr.data
            self.meta.align = align
            self.meta.rotated = True

        elif align == 'PVH':

            # First rotate to ZRT
            self.data.rotate('NE->RT', back_azimuth=self.meta.baz)

            # Copy traces
            trP = self.data.select(component='Z')[0].copy()
            trV = self.data.select(component='R')[0].copy()
            trH = self.data.select(component='T')[0].copy()

            slow = self.meta.slow
            if not vp:
                vp = self.meta.vp
            if not vs:
                vs = self.meta.vs

            # Vertical slownesses
            # P vertical slowness
            qp = np.sqrt(1. / vp / vp - slow * slow)
            # S vertical slowness
            qs = np.sqrt(1. / vs / vs - slow * slow)

            # Elements of rotation matrix
            m11 = slow * vs * vs / vp
            m12 = -(1. - 2. * vs * vs * slow * slow) / (2. * vp * qp)
            m21 = (1. - 2. * vs * vs * slow * slow) / (2. * vs * qs)
            m22 = slow * vs

            # Rotation matrix
            rot = np.array([[-m11, m12], [-m21, m22]])

            # Vector of Radial and Vertical
            r_z = np.array([trV.data, trP.data])

            # Rotation
            vec = np.dot(rot, r_z)

            # Extract P and SV, SH components
            trP.data = vec[0, :]
            trV.data = vec[1, :]
            trH.data = -trH.data / 2.

            # Update stats of streams
            trP.stats.channel = trP.stats.channel[:-1] + 'P'
            trV.stats.channel = trV.stats.channel[:-1] + 'V'
            trH.stats.channel = trH.stats.channel[:-1] + 'H'

            # Over-write data attribute
            self.data = Stream(traces=[trP, trV, trH])
            self.meta.align = align
            self.meta.rotated = True

        else:
            raise (Exception("incorrect 'align' argument"))

    def calc_snr(self, dt=30., fmin=0.05, fmax=1.):
        """
        Calculates signal-to-noise ratio on either Z, L or P component

        Parameters
        ----------
        dt : float
            Duration (sec)
        fmin : float
            Minimum frequency corner for SNR filter (Hz)
        fmax : float
            Maximum frequency corner for SNR filter (Hz)

        Attributes
        ----------
        snr : float
            Signal-to-noise ratio on vertical component (dB)
        snrh : float
            Signal-to-noise ratio on radial component (dB)

        """

        if not self.meta.accept:
            return

        if self.meta.snr:
            print("SNR already calculated - continuing")
            return

        t1 = self.meta.time + self.meta.ttime

        # SNR for dominant component ('Z', 'L' or 'P')
        comp = self.meta.align[0]

        # Copy trace to signal and noise traces
        trSig = self.data.select(component=comp)[0].copy()
        trNze = self.data.select(component=comp)[0].copy()

        trSig.detrend().taper(max_percentage=0.05)
        trNze.detrend().taper(max_percentage=0.05)

        # Filter between 0.1 and 1.0 (dominant P wave frequencies)
        trSig.filter('bandpass',
                     freqmin=fmin,
                     freqmax=fmax,
                     corners=2,
                     zerophase=True)
        trNze.filter('bandpass',
                     freqmin=fmin,
                     freqmax=fmax,
                     corners=2,
                     zerophase=True)

        # Trim around P-wave arrival
        trSig.trim(t1, t1 + dt)
        trNze.trim(t1 - dt, t1)

        # Calculate root mean square (RMS)
        srms = np.sqrt(np.mean(np.square(trSig.data)))
        nrms = np.sqrt(np.mean(np.square(trNze.data)))

        # Calculate signal/noise ratio in dB
        self.meta.snr = 10 * np.log10(srms * srms / nrms / nrms)

        # SNR for radial component ('R', 'Q' or 'V')
        comp = self.meta.align[1]

        # Copy trace to signal and noise traces
        trSig = self.data.select(component=comp)[0].copy()
        trNze = self.data.select(component=comp)[0].copy()

        trSig.detrend().taper(max_percentage=0.05)
        trNze.detrend().taper(max_percentage=0.05)

        # Filter between 0.1 and 1.0 (dominant P wave frequencies)
        trSig.filter('bandpass',
                     freqmin=fmin,
                     freqmax=fmax,
                     corners=2,
                     zerophase=True)
        trNze.filter('bandpass',
                     freqmin=fmin,
                     freqmax=fmax,
                     corners=2,
                     zerophase=True)

        # Trim around P-wave arrival
        trSig.trim(t1, t1 + dt)
        trNze.trim(t1 - dt, t1)

        # Calculate root mean square (RMS)
        srms = np.sqrt(np.mean(np.square(trSig.data)))
        nrms = np.sqrt(np.mean(np.square(trNze.data)))

        # Calculate signal/noise ratio in dB
        self.meta.snrh = 10 * np.log10(srms * srms / nrms / nrms)

    def deconvolve(self,
                   phase='P',
                   vp=None,
                   vs=None,
                   align=None,
                   method='wiener',
                   pre_filt=None,
                   gfilt=None,
                   wlevel=0.01):
        """
        Deconvolves three-compoent data using one component as the source wavelet.
        The source component is always taken as the dominant compressional 
        component, which can be either 'Z', 'L', or 'P'. 

        Parameters
        ----------
        vp : float
            P-wave velocity at surface (km/s)
        vs : float
            S-wave velocity at surface (km/s)
        align : str
            Alignment of coordinate system for rotation
            ('ZRT', 'LQT', or 'PVH')
        method : str
            Method for deconvolution. Options are 'wiener' or 
            'multitaper'
        gfilt : float
            Center frequency of Gaussian filter (Hz). 
        wlevel : float
            Water level used in ``method='water'``.

        Attributes
        ----------
        rf : :class:`~obspy.core.Stream`
            Stream containing the receiver function traces

        """

        if not self.meta.accept:
            return

        def _npow2(x):
            return 1 if x == 0 else 2**(x - 1).bit_length()

        def _pad(array, n):
            tmp = np.zeros(n)
            tmp[:array.shape[0]] = array
            return tmp

        def _gauss_filt(dt, nft, f0):
            df = 1. / (nft * dt)
            nft21 = int(0.5 * nft + 1)
            f = df * np.arange(nft21)
            w = 2. * np.pi * f
            gauss = np.zeros(nft)
            gauss[:nft21] = np.exp(-0.25 * (w / f0)**2.) / dt
            gauss[nft21:] = np.flip(gauss[1:nft21 - 1])
            return gauss

        def _decon(parent, daughter1, daughter2, noise, nn, method):

            # Get length, zero padding parameters and frequencies
            dt = parent.stats.delta

            # Wiener or Water level deconvolution
            if method == 'wiener' or method == 'water':

                # npad = _npow2(nn*2)
                npad = nn
                freqs = np.fft.fftfreq(npad, d=dt)

                # Fourier transform
                Fp = np.fft.fft(parent.data, n=npad)
                Fd1 = np.fft.fft(daughter1.data, n=npad)
                Fd2 = np.fft.fft(daughter2.data, n=npad)
                Fn = np.fft.fft(noise.data, n=npad)

                # Auto and cross spectra
                Spp = np.real(Fp * np.conjugate(Fp))
                Sd1p = Fd1 * np.conjugate(Fp)
                Sd2p = Fd2 * np.conjugate(Fp)
                Snn = np.real(Fn * np.conjugate(Fn))

                # Final processing depends on method
                if method == 'wiener':
                    Sdenom = Spp + Snn
                elif method == 'water':
                    phi = np.amax(Spp) * wlevel
                    Sdenom = Spp
                    Sdenom[Sdenom < phi] = phi

            # Multitaper deconvolution
            elif method == 'multitaper':

                from spectrum import dpss

                npad = nn
                # Re-check length and pad with zeros if necessary
                if not np.allclose([
                        tr.stats.npts
                        for tr in [parent, daughter1, daughter2, noise]
                ], npad):
                    parent.data = _pad(parent.data, npad)
                    daughter1.data = _pad(daughter1.data, npad)
                    daughter2.data = _pad(daughter2.data, npad)
                    noise.data = _pad(noise.data, npad)

                freqs = np.fft.fftfreq(npad, d=dt)

                NW = 2.5
                Kmax = int(NW * 2 - 2)
                [tapers, eigenvalues] = dpss(npad, NW, Kmax)

                # Get multitaper spectrum of data
                Fp = np.fft.fft(np.multiply(tapers.transpose(), parent.data))
                Fd1 = np.fft.fft(
                    np.multiply(tapers.transpose(), daughter1.data))
                Fd2 = np.fft.fft(
                    np.multiply(tapers.transpose(), daughter2.data))
                Fn = np.fft.fft(np.multiply(tapers.transpose(), noise.data))

                # Auto and cross spectra
                Spp = np.sum(np.real(Fp * np.conjugate(Fp)), axis=0)
                Sd1p = np.sum(Fd1 * np.conjugate(Fp), axis=0)
                Sd2p = np.sum(Fd2 * np.conjugate(Fp), axis=0)
                Snn = np.sum(np.real(Fn * np.conjugate(Fn)), axis=0)

                # Denominator
                Sdenom = Spp + Snn

            else:
                print("Method not implemented")
                pass

            # Apply Gaussian filter?
            if gfilt:
                gauss = _gauss_filt(dt, npad, gfilt)
                gnorm = np.sum(gauss) * (freqs[1] - freqs[0]) * dt
            else:
                gauss = np.ones(npad)
                gnorm = 1.

            # Copy traces
            rfp = parent.copy()
            rfd1 = daughter1.copy()
            rfd2 = daughter2.copy()

            # Spectral division and inverse transform
            rfp.data = np.fft.fftshift(
                np.real(np.fft.ifft(gauss * Spp / Sdenom)) / gnorm)
            rfd1.data = np.fft.fftshift(
                np.real(np.fft.ifft(gauss * Sd1p / Sdenom)) / gnorm)
            rfd2.data = np.fft.fftshift(
                np.real(np.fft.ifft(gauss * Sd2p / Sdenom)) / gnorm)
            # rfd1.data = np.fft.fftshift(np.real(np.fft.ifft(
            #     gauss*Sd1p/Sdenom))/np.amax(rfp.data)/gnorm)
            # rfd2.data = np.fft.fftshift(np.real(np.fft.ifft(
            #     gauss*Sd2p/Sdenom))/np.amax(rfp.data)/gnorm)

            return rfp, rfd1, rfd2

        if not self.meta.rotated:
            print("Warning: Data have not been rotated yet - rotating now")
            self.rotate(vp=vp, vs=vs, align=align)

        if not self.meta.snr:
            print("Warning: SNR has not been calculated - " +
                  "calculating now using default")
            self.calc_snr()

        if hasattr(self, 'rf'):
            print("Warning: Data have been deconvolved already - passing")
            return

        # Get the name of components (order is critical here)
        cL = self.meta.align[0]
        cQ = self.meta.align[1]
        cT = self.meta.align[2]

        # Define signal and noise
        trL = self.data.select(component=cL)[0].copy()
        trQ = self.data.select(component=cQ)[0].copy()
        trT = self.data.select(component=cT)[0].copy()
        trNl = self.data.select(component=cL)[0].copy()
        trNq = self.data.select(component=cQ)[0].copy()

        if phase == 'P' or 'PP':

            # Get signal length (i.e., seismogram to deconvolve) from trace length
            dts = len(trL.data) * trL.stats.delta / 2.
            nn = int(round((dts - 5.) * trL.stats.sampling_rate)) + 1

            # Crop traces for signal (-5. to dts-10 sec)
            trL.trim(self.meta.time + self.meta.ttime - 5.,
                     self.meta.time + self.meta.ttime + dts - 10.,
                     nearest_sample=False,
                     pad=nn,
                     fill_value=0.)
            trQ.trim(self.meta.time + self.meta.ttime - 5.,
                     self.meta.time + self.meta.ttime + dts - 10.,
                     nearest_sample=False,
                     pad=nn,
                     fill_value=0.)
            trT.trim(self.meta.time + self.meta.ttime - 5.,
                     self.meta.time + self.meta.ttime + dts - 10.,
                     nearest_sample=False,
                     pad=nn,
                     fill_value=0.)
            # Crop trace for noise (-dts to -5 sec)
            trNl.trim(self.meta.time + self.meta.ttime - dts,
                      self.meta.time + self.meta.ttime - 5.,
                      nearest_sample=False,
                      pad=nn,
                      fill_value=0.)
            trNq.trim(self.meta.time + self.meta.ttime - dts,
                      self.meta.time + self.meta.ttime - 5.,
                      nearest_sample=False,
                      pad=nn,
                      fill_value=0.)

        elif phase == 'S' or 'SKS':
            # Get signal length (i.e., seismogram to deconvolve) from trace length
            dts = len(trL.data) * trL.stats.delta / 2.

            # Crop traces for signal (-5. to dts-10 sec)
            trL.trim(self.meta.time + self.meta.ttime + 25. - dts / 2.,
                     self.meta.time + self.meta.ttime + 25.)
            trQ.trim(self.meta.time + self.meta.ttime + 25. - dts / 2.,
                     self.meta.time + self.meta.ttime + 25.)
            trT.trim(self.meta.time + self.meta.ttime + 25. - dts / 2.,
                     self.meta.time + self.meta.ttime + 25.)
            # Crop trace for noise (-dts to -5 sec)
            trNl.trim(self.meta.time + self.meta.ttime - dts,
                      self.meta.time + self.meta.ttime - dts / 2.)
            trNq.trim(self.meta.time + self.meta.ttime - dts,
                      self.meta.time + self.meta.ttime - dts / 2.)

        # Demean, detrend, taper, demean, detrend
        trL.detrend('demean').detrend('linear').taper(max_percentage=0.05,
                                                      max_length=2.)
        trQ.detrend('demean').detrend('linear').taper(max_percentage=0.05,
                                                      max_length=2.)
        trT.detrend('demean').detrend('linear').taper(max_percentage=0.05,
                                                      max_length=2.)
        trNl.detrend('demean').detrend('linear').taper(max_percentage=0.05,
                                                       max_length=2.)
        trNq.detrend('demean').detrend('linear').taper(max_percentage=0.05,
                                                       max_length=2.)
        trL.detrend('demean').detrend('linear')
        trQ.detrend('demean').detrend('linear')
        trT.detrend('demean').detrend('linear')
        trNl.detrend('demean').detrend('linear')
        trNq.detrend('demean').detrend('linear')
        # Stream(traces=[trL, trQ, trT, trNl, trNq]).plot(size=(600,300))

        # Pre-filter waveforms before deconvolution
        if pre_filt:
            trL.filter('bandpass',
                       freqmin=pre_filt[0],
                       freqmax=pre_filt[1],
                       corners=2,
                       zerophase=True)
            trQ.filter('bandpass',
                       freqmin=pre_filt[0],
                       freqmax=pre_filt[1],
                       corners=2,
                       zerophase=True)
            trT.filter('bandpass',
                       freqmin=pre_filt[0],
                       freqmax=pre_filt[1],
                       corners=2,
                       zerophase=True)
            trNl.filter('bandpass',
                        freqmin=pre_filt[0],
                        freqmax=pre_filt[1],
                        corners=2,
                        zerophase=True)
            trNq.filter('bandpass',
                        freqmin=pre_filt[0],
                        freqmax=pre_filt[1],
                        corners=2,
                        zerophase=True)
            # Stream(traces=[trL, trQ, trT, trNl, trNq]).plot(size=(600,300))

            # Demean, detrend, taper, demean, detrend
            trL.detrend('linear').taper(max_percentage=0.05,
                                        max_length=2.).detrend('linear')
            trQ.detrend('linear').taper(max_percentage=0.05,
                                        max_length=2.).detrend('linear')
            trT.detrend('linear').taper(max_percentage=0.05,
                                        max_length=2.).detrend('linear')
            trNl.detrend('linear').taper(max_percentage=0.05,
                                         max_length=2.).detrend('linear')
            trNq.detrend('linear').taper(max_percentage=0.05,
                                         max_length=2.).detrend('linear')
        # Stream(traces=[trL, trQ, trT, trNl, trNq]).plot(size=(600,300))

        # Deconvolve
        if phase == 'P' or 'PP':
            rfL, rfQ, rfT = _decon(trL, trQ, trT, trNl, nn, method)

        elif phase == 'S' or 'SKS':
            rfQ, rfL, rfT = _decon(trQ, trL, trT, trNq, nn, method)
        # Stream(traces=[rfQ, rfL, rfT]).plot(size=(600,300))

        # Update stats of streams
        rfL.stats.channel = 'RF' + self.meta.align[0]
        rfQ.stats.channel = 'RF' + self.meta.align[1]
        rfT.stats.channel = 'RF' + self.meta.align[2]

        self.rf = Stream(traces=[rfL, rfQ, rfT])

    def calc_cc(self):

        if not self.meta.accept:
            return

        if not hasattr(self, 'rf'):
            raise (Exception("Warning: Receiver functions are not available"))

        obs_L = self.data[0].copy()
        obs_Q = self.data[1].copy()
        obs_rfQ = self.rf[1].copy()
        sr = obs_L.stats.sampling_rate

        # Filter using SNR bandpass
        obs_L.detrend().taper(max_percentage=0.05, max_length=2.)
        obs_Q.detrend().taper(max_percentage=0.05, max_length=2.)
        obs_L.filter('bandpass', freqmin=0.05, freqmax=1.)
        obs_Q.filter('bandpass', freqmin=0.05, freqmax=1.)
        obs_rfQ.filter('bandpass', freqmin=0.05, freqmax=1.)

        # Convolve L with rfQ to obtain predicted Q
        pred_Q = obs_Q.copy()
        pred_Q.stats.channel = 'PRR'
        ind1 = int((len(obs_rfQ.data) / 2.))
        ind2 = ind1 + len(obs_L.data)
        pred_Q.data = np.convolve(obs_L.data, obs_rfQ.data,
                                  mode='full')[ind1:ind2]

        # trim all traces from 0 to 20. sec following P-wave (fftshift first)
        obs_L.data = np.fft.fftshift(obs_L.data)[0:int(sr * 10.)]
        obs_Q.data = np.fft.fftshift(obs_Q.data)[0:int(sr * 10.)]
        pred_Q.data = np.fft.fftshift(pred_Q.data)[0:int(sr * 10.)]

        # Get cross correlation coefficient between observed and predicted Q
        self.meta.cc = np.corrcoef(obs_Q.data, pred_Q.data)[0][1]

        # print(self.meta.cc)
        # test = Stream(traces=[obs_L, obs_Q, pred_Q])
        # test.plot()

    def to_stream(self):
        """
        Method to switch from RFData object to Stream object.
        This allows easier manipulation of the receiver functions
        for post-processing.

        """

        if not self.meta.accept:
            return

        def _add_rfstats(trace):
            trace.stats.snr = self.meta.snr
            trace.stats.snrh = self.meta.snrh
            trace.stats.cc = self.meta.cc
            trace.stats.slow = self.meta.slow
            trace.stats.baz = self.meta.baz
            trace.stats.gac = self.meta.gac
            trace.stats.stlo = self.sta.longitude
            trace.stats.stla = self.sta.latitude
            trace.stats.evlo = self.meta.lon
            trace.stats.evla = self.meta.lat
            trace.stats.vp = self.meta.vp
            trace.stats.vs = self.meta.vs
            trace.stats.phase = self.meta.phase
            trace.stats.is_rf = True
            nn = self.rf[0].stats.npts
            sr = self.rf[0].stats.sampling_rate
            trace.stats.taxis = np.arange(-nn / 2., nn / 2.) / sr
            return trace

        if not hasattr(self, 'rf'):
            raise (Exception("Warning: Receiver functions are not available"))

        stream = self.rf
        for tr in stream:
            tr = _add_rfstats(tr)

        return stream

    def save(self, file):
        """
        Saves RFData object to file

        Parameters
        ----------
        file : str
            File name for RFData object

        """

        import pickle
        output = open(file, 'wb')
        pickle.dump(self, output)
        output.close()
Пример #32
0
def pro5stack2d(eq_num,
                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,
                ARRAY=0,
                NS=False,
                decimate_fac=0,
                ref_loc=0,
                ref_lat=36.3,
                ref_lon=138.5,
                stack_option=1):

    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
    from termcolor import colored
    print(colored('Running pro5b_stack2d', 'cyan'))

    env_stack = 0  # flag to stack envelopes instead of oscillating seismograms
    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 location file
    if ARRAY == 0:  # Hinet set
        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
        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
    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])
    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()

#%% Input parameters
# 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)

    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(
        round(1 + (slowR_hi - slowR_lo) / slow_delta))  # number of slownesses
    slowT_n = int(
        round(1 + (slowT_hi - slowT_lo) / slow_delta))  # number of slownesses
    stack_nt = int(
        round(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]

    # testing slownesses in indexing
    print(
        str(slowR_n) + ' radial slownesses, ' + str(slowT_n) +
        ' trans slownesses, ')
    print('Radial     slownesses 0' + ' ' + str(stack_Rslows[0]) + '   '
          'end' + ' ' + str(stack_Rslows[-1]))
    print('Transverse slownesses 1' + ' ' + str(stack_Tslows[0]) + '   '
          'end' + ' ' + str(stack_Tslows[-1]))

    #%% 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(round(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_back_az = ref_dist_az[2]

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

    for tr in st:  # traces one by one, find lat-lon by searching entire inventory.  Inefficient but cheap
        if tr.stats.station in st_names:  # find station in station list
            ii = st_names.index(tr.stats.station)
            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

            if NS == False:
                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(round(slowR_i * slowT_n + slowT_i))
                    # could do a little better by sampling finer, 20 sps?, before applying statics in pro3

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

                    if stack_option == 1:  #  Wei's much faster method
                        arr = tr.data
                        nshift = round(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[indx].data[nbeg1:nend1] += arr[nbeg2:nend2]
            done += 1
            if done % 100 == 0:
                print('Done stacking ' + str(done) + ' out of ' +
                      str(len(st)) + ' stations.')
        else:
            print(tr.stats.station + ' not found in station list')

#%% 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, no_filter=True)

#%%  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(f'This job took   {elapsed_time_wc:.1f}   seconds')
    os.system('say "Done"')
Пример #33
0
def run_tutorial(plot=False):
    """Main function to run the tutorial dataset."""

    from eqcorrscan.utils import pre_processing
    from eqcorrscan.utils import plotting
    from eqcorrscan.core import match_filter
    import glob

    # This import section copes with namespace changes between obspy versions
    import obspy
    if int(obspy.__version__.split('.')[0]) >= 1:
        from obspy.clients.fdsn import Client
    else:
        from obspy.fdsn import Client
    from obspy import UTCDateTime, Stream, read

    # First we want to load our templates
    template_names = glob.glob('tutorial_template_*.ms')

    if len(template_names) == 0:
        raise IOError('Template files not found, have you run the template ' +
                      'creation tutorial?')

    templates = [read(template_name) for template_name in template_names]

    # Work out what stations we have and get the data for them
    stations = []
    for template in templates:
        for tr in template:
            stations.append((tr.stats.station, tr.stats.channel))
    # Get a unique list of stations
    stations = list(set(stations))

    # We are going to look for detections on the day of our template, however, to
    # generalize, we will write a loop through the days between our templates, in
    # this case that is only one day.

    template_days = []
    for template in templates:
        template_days.append(template[0].stats.starttime.date)
    template_days = sorted(template_days)
    kdays = (template_days[-1] - template_days[0]).days + 1

    unique_detections = []

    for i in range(kdays):
        t1 = UTCDateTime(template_days[0]) + (86400 * i)
        t2 = t1 + 86400

        # Generate the bulk information to query the GeoNet database
        bulk_info = []
        for station in stations:
            bulk_info.append(('NZ', station[0], '*',
                              station[1][0] + 'H' + station[1][-1], t1, t2))

        # Set up a client to access the GeoNet database
        client = Client("GEONET")

        # Note this will take a little while.
        print('Downloading seismic data, this may take a while')
        st = client.get_waveforms_bulk(bulk_info)
        # Merge the stream, it will be downloaded in chunks
        st.merge(fill_value='interpolate')

        # Work out what data we actually have to cope with possible lost data
        stations = list(set([tr.stats.station for tr in st]))

        # Set how many cores we want to parallel across, we will set this to four
        # as this is the number of templates, if your machine has fewer than four
        # cores/CPUs the multiprocessing will wait until there is a free core.
        # Setting this to be higher than the number of templates will have no
        # increase in speed as only detections for each template are computed in
        # parallel.  It may also slow your processing by using more memory than
        # needed, to the extent that swap may be filled.
        ncores = 4

        # Pre-process the data to set frequency band and sampling rate
        # Note that this is, and MUST BE the same as the parameters used for the
        # template creation.
        print('Processing the seismic data')
        st = pre_processing.dayproc(st, lowcut=2.0, highcut=9.0,
                                    filt_order=4, samp_rate=20.0,
                                    debug=0, starttime=t1, num_cores=ncores)
        # Convert from list to stream
        st = Stream(st)

        # Now we can conduct the matched-filter detection
        detections = match_filter.match_filter(template_names=template_names,
                                               template_list=templates,
                                               st=st, threshold=8.0,
                                               threshold_type='MAD',
                                               trig_int=6.0, plotvar=plot,
                                               plotdir='.', cores=ncores,
                                               tempdir=False, debug=1,
                                               plot_format='jpg')

        # Now lets try and work out how many unique events we have just to compare
        # with the GeoNet catalog of 20 events on this day in this sequence
        for master in detections:
            keep = True
            for slave in detections:
                if not master == slave and\
                   abs(master.detect_time - slave.detect_time) <= 1.0:
                    # If the events are within 1s of each other then test which
                    # was the 'best' match, strongest detection
                    if not master.detect_val > slave.detect_val:
                        keep = False
                        break
            if keep:
                unique_detections.append(master)

    print('We made a total of ' + str(len(unique_detections)) + ' detections')

    for detection in unique_detections:
        print('Detection at :' + str(detection.detect_time) +
              ' for template ' + detection.template_name +
              ' with a cross-correlation sum of: ' +
              str(detection.detect_val))
        # We can plot these too
        if plot:
            stplot = st.copy()
            template = templates[template_names.index(detection.template_name)]
            lags = sorted([tr.stats.starttime for tr in template])
            maxlag = lags[-1] - lags[0]
            stplot.trim(starttime=detection.detect_time - 10,
                        endtime=detection.detect_time + maxlag + 10)
            plotting.detection_multiplot(stplot, template,
                                         [detection.detect_time.datetime])
    return unique_detections
ref2_st = st_ref2[0].data

#print("here 0")
#%%

#client = Client('138.253.112.23', 16022) # ip, port - ip's 138.253.113.19 or 138.253.112.23
t1 = UTCDateTime(2019, 10, 1, 0, 0,
                 0)  #the format is year:day_of_the_year:month
t2 = t1 + 24 * 60 * 60
sts = Stream()
sts = client.get_waveforms(net, sta, loc, cha, t1, t2)
#print("here 1")
sts.detrend(type='linear')
sts.detrend(type='demean')

sts1 = sts.copy()
sts2 = sts.copy()
sts3 = sts.copy()

fb1 = sts1.filter(type='bandpass', freqmin=12, freqmax=22)
#fb1.plot(type = 'dayplot',interval=15,starttime=t1, endtime=t2)

fb2 = sts2.filter(type='bandpass', freqmin=0.5, freqmax=22)
fb2.plot(type='dayplot', interval=30, starttime=t1, endtime=t2)

fb3 = sts3.filter(type='bandpass', freqmin=0.5, freqmax=5)
#fb3.plot(type = 'dayplot',interval=60,starttime=t1, endtime=t2)

#%%

trace_high = fb1[0]
Пример #35
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"')
Пример #36
0
def run_tutorial(plot=False):
    """Main function to run the tutorial dataset."""

    from eqcorrscan.utils import pre_processing
    from eqcorrscan.utils import plotting
    from eqcorrscan.core import match_filter
    import glob
    from multiprocessing import cpu_count

    # This import section copes with namespace changes between obspy versions
    import obspy
    if int(obspy.__version__.split('.')[0]) >= 1:
        from obspy.clients.fdsn import Client
    else:
        from obspy.fdsn import Client
    from obspy import UTCDateTime, Stream, read

    # First we want to load our templates
    template_names = glob.glob('tutorial_template_*.ms')

    if len(template_names) == 0:
        raise IOError('Template files not found, have you run the template ' +
                      'creation tutorial?')

    templates = [read(template_name) for template_name in template_names]

    # Work out what stations we have and get the data for them
    stations = []
    for template in templates:
        for tr in template:
            stations.append((tr.stats.station, tr.stats.channel))
    # Get a unique list of stations
    stations = list(set(stations))

    # We will loop through the data chunks at a time, these chunks can be any
    # size, in general we have used 1 day as our standard, but this can be
    # as short as five minutes (for MAD thresholds) or shorter for other
    # threshold metrics. However the chunk size should be the same as your
    # template process_len.

    # You should test different parameters!!!
    start_time = UTCDateTime(2016, 1, 4)
    end_time = UTCDateTime(2016, 1, 5)
    process_len = 3600
    chunks = []
    chunk_start = start_time
    while chunk_start < end_time:
        chunk_end = chunk_start + process_len
        if chunk_end > end_time:
            chunk_end = end_time
        chunks.append((chunk_start, chunk_end))
        chunk_start += process_len

    unique_detections = []
    detections = []

    # Set up a client to access the GeoNet database
    client = Client("GEONET")

    # Note that these chunks do not rely on each other, and could be paralleled
    # on multiple nodes of a distributed cluster, see the SLURM tutorial for
    # an example of this.
    for t1, t2 in chunks:
        # Generate the bulk information to query the GeoNet database
        bulk_info = []
        for station in stations:
            bulk_info.append(('NZ', station[0], '*',
                              station[1][0] + 'H' + station[1][-1], t1, t2))

        # Note this will take a little while.
        print('Downloading seismic data, this may take a while')
        st = client.get_waveforms_bulk(bulk_info)
        # Merge the stream, it will be downloaded in chunks
        st.merge(fill_value='interpolate')

        # Set how many cores we want to parallel across, we will set this to four
        # as this is the number of templates, if your machine has fewer than four
        # cores/CPUs the multiprocessing will wait until there is a free core.
        # Setting this to be higher than the number of templates will have no
        # increase in speed as only detections for each template are computed in
        # parallel.  It may also slow your processing by using more memory than
        # needed, to the extent that swap may be filled.
        if cpu_count() < 4:
            ncores = cpu_count()
        else:
            ncores = 4

        # Pre-process the data to set frequency band and sampling rate
        # Note that this is, and MUST BE the same as the parameters used for the
        # template creation.
        print('Processing the seismic data')
        st = pre_processing.shortproc(st,
                                      lowcut=2.0,
                                      highcut=9.0,
                                      filt_order=4,
                                      samp_rate=20.0,
                                      debug=2,
                                      num_cores=ncores,
                                      starttime=t1,
                                      endtime=t2)
        # Convert from list to stream
        st = Stream(st)

        # Now we can conduct the matched-filter detection
        detections += match_filter.match_filter(template_names=template_names,
                                                template_list=templates,
                                                st=st,
                                                threshold=8.0,
                                                threshold_type='MAD',
                                                trig_int=6.0,
                                                plotvar=plot,
                                                plotdir='.',
                                                cores=ncores,
                                                tempdir=False,
                                                debug=1,
                                                plot_format='jpg')

    # Now lets try and work out how many unique events we have just to compare
    # with the GeoNet catalog of 20 events on this day in this sequence
    for master in detections:
        keep = True
        for slave in detections:
            if not master == slave and\
               abs(master.detect_time - slave.detect_time) <= 1.0:
                # If the events are within 1s of each other then test which
                # was the 'best' match, strongest detection
                if not master.detect_val > slave.detect_val:
                    keep = False
                    break
        if keep:
            unique_detections.append(master)

    print('We made a total of ' + str(len(unique_detections)) + ' detections')

    for detection in unique_detections:
        print('Detection at :' + str(detection.detect_time) +
              ' for template ' + detection.template_name +
              ' with a cross-correlation sum of: ' + str(detection.detect_val))
        # We can plot these too
        if plot:
            stplot = st.copy()
            template = templates[template_names.index(detection.template_name)]
            lags = sorted([tr.stats.starttime for tr in template])
            maxlag = lags[-1] - lags[0]
            stplot.trim(starttime=detection.detect_time - 10,
                        endtime=detection.detect_time + maxlag + 10)
            plotting.detection_multiplot(stplot, template,
                                         [detection.detect_time.datetime])
    return unique_detections
Пример #37
0
        #Get station to hypocenter delta distance
        delta = locations2degrees(coords[k, 1], coords[k, 0], epicenter[1],
                                  epicenter[0])
        #Get pwave travel-time to site
        tt = getTravelTimes(delta, epicenter[2])
        tp = timedelta(seconds=float64(tt[0]['time']))
        #Trim
        n[0].trim(starttime=time_epi + tp - tmin, endtime=time_epi + tp + tmax)
        e[0].trim(starttime=time_epi + tp - tmin, endtime=time_epi + tp + tmax)
        u[0].trim(starttime=time_epi + tp - tmin, endtime=time_epi + tp + tmax)
        #Remove first epoch
        n[0].data = n[0].data - n[0].data[0]
        e[0].data = e[0].data - e[0].data[0]
        u[0].data = u[0].data - u[0].data[0]
        #Create velocity time series (copy displacememnts then replace data)
        nvel = n.copy()
        evel = e.copy()
        uvel = u.copy()
        nvel[0].data = diff(n[0].data) / n[0].stats.delta
        evel[0].data = diff(e[0].data) / e[0].stats.delta
        uvel[0].data = diff(u[0].data) / u[0].stats.delta
        #Write displacememnts to file
        n.write(path + 'trim/' + sta + '.LXN.sac', format='SAC')
        e.write(path + 'trim/' + sta + '.LXE.sac', format='SAC')
        u.write(path + 'trim/' + sta + '.LXZ.sac', format='SAC')
        #Write velocity to file
        nvel.write(path + 'trim/' + sta + '.LYN.sac', format='SAC')
        evel.write(path + 'trim/' + sta + '.LYE.sac', format='SAC')
        uvel.write(path + 'trim/' + sta + '.LYZ.sac', format='SAC')

if make_plots: