Example #1
0
 def test_remove_unclustered_parallel(self):
     distance_cutoff = 100
     cat_back = remove_unclustered(self.cat.copy(), distance_cutoff,
                                   num_threads=4)
     assert len(cat_back) < len(self.cat)
     for i, event in enumerate(cat_back):
         master_ori = event.preferred_origin() or event.origins[0]
         distances = []
         for j, other_event in enumerate(cat_back):
             slave_ori = (other_event.preferred_origin() or
                          other_event.origins[0])
             if i == j:
                 continue
             distances.append(dist_calc(
                 (master_ori.latitude, master_ori.longitude,
                  master_ori.depth / 1000),
                 (slave_ori.latitude, slave_ori.longitude,
                  slave_ori.depth / 1000)))
         assert any(np.array(distances) < distance_cutoff)
     # Check that all events not in the catalog are correctly ignored
     for i, event in enumerate(self.cat):
         if event not in cat_back:
             master_ori = event.preferred_origin() or event.origins[0]
             distances = []
             for j, other_event in enumerate(self.cat):
                 slave_ori = (other_event.preferred_origin() or
                              other_event.origins[0])
                 if i == j:
                     continue
                 distances.append(dist_calc(
                     (master_ori.latitude, master_ori.longitude,
                      master_ori.depth / 1000),
                     (slave_ori.latitude, slave_ori.longitude,
                      slave_ori.depth / 1000)))
             assert all(np.array(distances) > distance_cutoff)
Example #2
0
 def test_space_cluster(self):
     """Test the wrapper around dist_mat_km."""
     groups = catalog_cluster(
         catalog=self.cat, thresh=self.distance_threshold,
         metric="distance", show=False)
     self.assertEqual(len([ev for group in groups for ev in group]),
                      len(self.cat))
     # Check that events within each group are within distance-threshold
     for group in groups:
         if len(group) > 1:
             master_ori = group[0].preferred_origin() or group[0].origins[0]
             for event in group[1:]:
                 slave_ori = event.preferred_origin() or event.origins[0]
                 self.assertLessEqual(dist_calc(
                     (master_ori.latitude, master_ori.longitude,
                      master_ori.depth / 1000),
                     (slave_ori.latitude, slave_ori.longitude,
                      slave_ori.depth / 1000)), self.distance_threshold)
     # Check that groups are separated by at least distance-threshold
     for i, group in enumerate(groups):
         for event in group:
             master_ori = event.preferred_origin() or event.origins[0]
             for j, other_group in enumerate(groups):
                 if i == j:
                     continue
                 for other_event in other_group:
                     slave_ori = (
                         other_event.preferred_origin() or
                         other_event.origins[0])
                     self.assertGreater(dist_calc(
                         (master_ori.latitude, master_ori.longitude,
                          master_ori.depth / 1000),
                         (slave_ori.latitude, slave_ori.longitude,
                          slave_ori.depth / 1000)), self.distance_threshold)
Example #3
0
 def test_space_time_cluster(self):
     """Test clustering in space and time."""
     groups = space_time_cluster(
         catalog=self.cat, t_thresh=self.time_threshold,
         d_thresh=self.distance_threshold)
     self.assertEqual(len([ev for group in groups for ev in group]),
                      len(self.cat))
     # Check that events within each group are within distance-threshold and
     # time-threshold
     for group in groups:
         if len(group) > 1:
             master_ori = group[0].preferred_origin() or group[0].origins[0]
             for event in group[1:]:
                 slave_ori = event.preferred_origin() or event.origins[0]
                 self.assertLessEqual(dist_calc(
                     (master_ori.latitude, master_ori.longitude,
                      master_ori.depth / 1000),
                     (slave_ori.latitude, slave_ori.longitude,
                      slave_ori.depth / 1000)), self.distance_threshold)
                 self.assertLessEqual(abs(master_ori.time - slave_ori.time),
                                      self.time_threshold)
     # Check that groups are separated by at least distance-threshold,
     # or, by some time.
     for i, group in enumerate(groups):
         master_times = []
         for master in group:
             master_ori = (
                 master.preferred_origin() or master.origins[0])
             master_times.append(master_ori.time)
         master_times.sort()
         master_median_time = master_times[0] + np.median(
             [m - master_times[0] for m in master_times])
         # Just use the origin of one event
         master_ori = group[0].preferred_origin() or group[0].origins[0]
         for j, other_group in enumerate(groups):
             if i == j:
                 continue
             slave_times = []
             for slave in other_group:
                 slave_ori = (
                     slave.preferred_origin() or slave.origins[0])
                 slave_times.append(slave_ori.time)
             slave_times.sort()
             slave_median_time = slave_times[0] + np.median(
                 [s - slave_times[0] for s in slave_times])
             for event in other_group:
                 slave_ori = event.preferred_origin() or event.origins[0]
                 separation = dist_calc(
                     (master_ori.latitude, master_ori.longitude,
                      master_ori.depth / 1000),
                     (slave_ori.latitude, slave_ori.longitude,
                      slave_ori.depth / 1000))
                 if separation < self.distance_threshold:
                     self.assertGreater(
                         abs(master_median_time - slave_median_time),
                         self.time_threshold)
                 else:
                     self.assertGreater(separation, self.distance_threshold)
Example #4
0
def template_extents(cat, temp_cat, temp_list='all', param='avg_dist', show=True):
    """
    Measure parameters of the areal extent of template detections
    :param cat: Detections catalog
    :param temp_cat: Templates catalog
    :param param: What parameter are we measuring? Average template-detection distance or area?
    :return:
    """

    dets_dict = template_det_cats(cat, temp_list)
    temp_dict = {str(ev.resource_id).split('/')[-1]: ev for ev in temp_cat
                 if str(ev.resource_id).split('/')[-1] in temp_list}
    # Remove keys which aren't common
    for key in temp_dict.keys():
        if key not in dets_dict:
            del temp_dict[key]
    param_dict = {}
    for key, ev in temp_dict.iteritems():
        temp_o = ev.origins[-1]
        if param == 'avg_dist':
            param_dict[key] = np.mean([dist_calc((temp_o.latitude, temp_o.longitude, temp_o.depth / 1000.0),
                                                 (det.origins[-1].latitude,
                                                  det.origins[-1].longitude,
                                                  det.origins[-1].depth / 1000.0)) for det in dets_dict[key]])
    ax = sns.distplot([avg for key, avg in param_dict.iteritems()])
    ax.set_title('')
    ax.set_xlabel('Average template-detection distance per template (km)')
    fig = plt.gcf()
    if show:
        fig.show()
    return fig
Example #5
0
def dist_mat_km(catalog):
    """
    Function to compute the distance matrix for all events in a catalog - \
    will give physical distance in kilometers.

    :type catalog: List of obspy.Catalog
    :param catalog: Catalog for which to compute the distance matrix

    :returns: ndarray - distance matrix
    """
    from eqcorrscan.utils.mag_calc import dist_calc

    # Initialize square matrix
    dist_mat = np.array([np.array([0.0] * len(catalog))] * len(catalog))
    # Calculate distance vector for each event
    for i, master in enumerate(catalog):
        mast_list = []
        master_tup = (master.preferred_origin().latitude,
                      master.preferred_origin().longitude,
                      master.preferred_origin().depth // 1000)
        for slave in catalog:
            slave_tup = (slave.preferred_origin().latitude,
                         slave.preferred_origin().longitude,
                         slave.preferred_origin().depth // 1000)
            mast_list.append(dist_calc(master_tup, slave_tup))
        # Sort the list into the dist_mat structure
        for j in range(i, len(catalog)):
            dist_mat[i, j] = mast_list[j]
    # Reshape the distance matrix
    for i in range(1, len(catalog)):
        for j in range(i):
            dist_mat[i, j] = dist_mat.T[i, j]
    return dist_mat
Example #6
0
def dist_mat_km(catalog):
    """
    Function to compute the distance matrix for all events in a catalog - \
    will give physical distance in kilometers.

    :type catalog: List of obspy.Catalog
    :param catalog: Catalog for which to compute the distance matrix

    :returns: ndarray - distance matrix
    """
    from eqcorrscan.utils.mag_calc import dist_calc

    # Initialize square matrix
    dist_mat = np.array([np.array([0.0] * len(catalog))] *
                        len(catalog))
    # Calculate distance vector for each event
    for i, master in enumerate(catalog):
        mast_list = []
        master_tup = (master.preferred_origin().latitude,
                      master.preferred_origin().longitude,
                      master.preferred_origin().depth // 1000)
        for slave in catalog:
            slave_tup = (slave.preferred_origin().latitude,
                         slave.preferred_origin().longitude,
                         slave.preferred_origin().depth // 1000)
            mast_list.append(dist_calc(master_tup, slave_tup))
        # Sort the list into the dist_mat structure
        for j in range(i, len(catalog)):
            dist_mat[i, j] = mast_list[j]
    # Reshape the distance matrix
    for i in range(1, len(catalog)):
        for j in range(i):
            dist_mat[i, j] = dist_mat.T[i, j]
    return dist_mat
Example #7
0
 def test_dist_calc(self):
     """
     Test the distance calculation that comes with mag_calc.
     """
     self.assertEqual(dist_calc((0, 0, 0), (0, 0, 10)), 10)
     self.assertEqual(round(dist_calc((0, 0, 0), (0, 1, 0))), 111)
     self.assertEqual(round(dist_calc((0, 0, 0), (1, 0, 0))), 111)
     self.assertEqual(round(dist_calc((45, 45, 0), (45, 45, 10))), 10)
     self.assertEqual(round(dist_calc((45, 45, 0), (45, 46, 0))), 79)
     self.assertEqual(round(dist_calc((45, 45, 0), (46, 45, 0))), 111)
     self.assertEqual(round(dist_calc((90, 90, 0), (90, 90, 10))), 10)
     self.assertEqual(round(dist_calc((90, 90, 0), (90, 89, 0))), 0)
     self.assertEqual(round(dist_calc((90, 90, 0), (89, 90, 0))), 111)
Example #8
0
 def test_dist_calc(self):
     """
     Test the distance calculation that comes with mag_calc.
     """
     from eqcorrscan.utils.mag_calc import dist_calc
     self.assertEqual(dist_calc((0, 0, 0), (0, 0, 10)), 10)
     self.assertEqual(round(dist_calc((0, 0, 0), (0, 1, 0))), 111)
     self.assertEqual(round(dist_calc((0, 0, 0), (1, 0, 0))), 111)
     self.assertEqual(round(dist_calc((45, 45, 0), (45, 45, 10))), 10)
     self.assertEqual(round(dist_calc((45, 45, 0), (45, 46, 0))), 79)
     self.assertEqual(round(dist_calc((45, 45, 0), (46, 45, 0))), 111)
     self.assertEqual(round(dist_calc((90, 90, 0), (90, 90, 10))), 10)
     self.assertEqual(round(dist_calc((90, 90, 0), (90, 89, 0))), 0)
     self.assertEqual(round(dist_calc((90, 90, 0), (89, 90, 0))), 111)
Example #9
0
def plot_rand_correlation(cat, d_thresh, temp_dict, stream_dict):
    """
    Calculate cross correlation coefficients for events located further than d_thresh from
    each other. This should represent correlation of uncorrelated signals and help determine
    ccval_cutoff
    :param cat:
    :param dist_thresh:
    :param temp_dict:
    :param stream_dict:
    :return:
    """
    from eqcorrscan.utils.mag_calc import dist_calc
    from eqcorrscan.core.match_filter import normxcorr2
    import numpy as np

    corrs = []
    for i, ev in enumerate(cat):
        print i
        ev_tup = (ev.preferred_origin().latitude, ev.preferred_origin().longitude,
                  ev.preferred_origin().depth / 1000.)
        for ev2 in cat[i+1:]:
            ev_tup2 = (ev2.preferred_origin().latitude, ev2.preferred_origin().longitude,
                      ev2.preferred_origin().depth / 1000.)
            dist = dist_calc(ev_tup, ev_tup2)
            if dist > d_thresh:
                det_rid = str(ev2.resource_id).split('/')[-1]
                temp_rid = str(ev.resource_id).split('/')[-1].split('_')[0]
                temp = temp_dict[temp_rid + '_1sec']
                stream = stream_dict[det_rid]
                for pk in ev.picks:
                    if pk.phase_hint == 'P':
                        sta = pk.waveform_id.station_code
                        chan = pk.waveform_id.channel_code
                        if len(temp.select(station=sta, channel=chan)) > 0:
                            tr = temp.select(station=sta,
                                             channel=chan)[0]
                        else:
                            continue
                        if len(stream.select(station=sta, channel=chan)) > 0:
                            st_tr = stream.select(station=sta,
                                                  channel=chan)[0]
                        else:
                            continue
                        # # still correcting for 0.1 sec pre-pick time here...gross
                        # pk_samp =
                        # corr_start = pk_samp - 5
                        # corr_end = pk_samp + 6
                        ccc = normxcorr2(tr.data, st_tr.data[140:201])[0]
                        corrs.append(max(ccc.max(), ccc.min(), key=abs))
    return corrs
Example #10
0
def event_diff_dict(cat1, cat2):
    """
    Generates a dictionary of differences between two catalogs with identical events
    :type cat1: obspy.Catalog
    :param cat1: catalog of events parallel to cat2
    :type cat2: obspy.Catalog
    :param cat2: catalog of events parallel to cat1
    :return: dict
    """

    diff_dict = {}
    for ev1 in cat1:
        ev1_o = ev1.origins[-1]
        ev2 = [ev for ev in cat2 if ev.resource_id == ev1.resource_id][0]
        ev2_o = ev2.origins[-1]
        diff_dict[ev1.resource_id] = {'dist': dist_calc((ev1_o.latitude,
                                                         ev1_o.longitude,
                                                         ev1_o.depth / 1000.00),
                                                        (ev2_o.latitude,
                                                         ev2_o.longitude,
                                                         ev2_o.depth / 1000.00)),
                                      'pick_residuals1': {'P': [], 'S': []},
                                      'pick_residuals2': {'P': [], 'S': []},
                                      'cat1_RMS': ev1_o.quality.standard_error,
                                      'cat2_RMS': ev2_o.quality.standard_error,
                                      'RMS_change': ev2_o.quality.standard_error - ev1_o.quality.standard_error,
                                      'cat1_picks': len(ev1.picks),
                                      'cat2_picks': len(ev2.picks),
                                      'x_diff': ev2_o.longitude - ev1_o.longitude,
                                      'y_diff': ev2_o.latitude - ev1_o.latitude,
                                      'z_diff': ev2_o.depth - ev1_o.depth,
                                      'min_uncert_diff': ev2_o.origin_uncertainty.min_horizontal_uncertainty -
                                                         ev1_o.origin_uncertainty.min_horizontal_uncertainty,
                                      'max_uncert_diff': ev2_o.origin_uncertainty.max_horizontal_uncertainty -
                                                         ev1_o.origin_uncertainty.max_horizontal_uncertainty,
                                      'az_max_uncert_diff': ev2_o.origin_uncertainty.azimuth_max_horizontal_uncertainty -
                                                            ev1_o.origin_uncertainty.azimuth_max_horizontal_uncertainty}
        for ar in ev1_o.arrivals:
            phs = ar.pick_id.get_referred_object().phase_hint
            diff_dict[ev1.resource_id]['pick_residuals1'][phs].append((ar.pick_id.get_referred_object().waveform_id.station_code + '.' +
                                                                       ar.pick_id.get_referred_object().waveform_id.channel_code,
                                                                       ar.time_residual))
        for ar in ev2_o.arrivals:
            phs = ar.pick_id.get_referred_object().phase_hint
            diff_dict[ev1.resource_id]['pick_residuals2'][phs].append((ar.pick_id.get_referred_object().waveform_id.station_code + '.' +
                                                                       ar.pick_id.get_referred_object().waveform_id.channel_code,
                                                                       ar.time_residual))
    return diff_dict
Example #11
0
def space_time_cluster(detections, t_thresh, d_thresh):
    """
    Function to cluster detections in space and time, use to seperate \
    repeaters from other events.

    :type detections: list
    :param detections: List of tuple of tuple of location (lat, lon, depth \
        (km)), and time as a datetime object
    :type t_thresh: float
    :param t_thresh: Maximum inter-event time threshold in seconds
    :type d_thresh: float
    :param d_thresh: Maximum inter-event distance in km

    :returns: List of tuple (detections, clustered) and list of indices of\
            clustered detections
    """
    from eqcorrscan.utils.mag_calc import dist_calc

    # Ensure they are sorted by time first, not that we need it.
    detections.sort(key=lambda tup: tup[1])
    clustered = []
    clustered_indices = []
    for master_ind, master in enumerate(detections):
        keep = False
        mast_o = master.preferred_origin()
        mast_time = mast_o.time
        mast_loc = (mast_o.latitude, mast_o.longitude, mast_o.depth // 1000)
        for slave in detections:
            slave_o = slave.preferred_origin()
            slave_time = slave_o.time
            slave_loc = (slave_o.latitude, slave_o.longitude,
                         slave_o.depth // 1000)
            if not master.resource_id == slave.resource_id and\
               abs((mast_time - slave_time).total_seconds()) <= t_thresh and\
               dist_calc(mast_loc, slave_loc) <= d_thresh:
                # If the slave events is close in time and space to the master
                # keep it and break out of the loop.
                keep = True
                break
        if keep:
            clustered.append(master)
            clustered_indices.append(master_ind)

    return clustered, clustered_indices
Example #12
0
def space_time_cluster(detections, t_thresh, d_thresh):
    """
    Function to cluster detections in space and time, use to seperate \
    repeaters from other events.

    :type detections: list
    :param detections: List of tuple of tuple of location (lat, lon, depth \
        (km)), and time as a datetime object
    :type t_thresh: float
    :param t_thresh: Maximum inter-event time threshold in seconds
    :type d_thresh: float
    :param d_thresh: Maximum inter-event distance in km

    :returns: List of tuple (detections, clustered) and list of indices of\
            clustered detections
    """
    from eqcorrscan.utils.mag_calc import dist_calc

    # Ensure they are sorted by time first, not that we need it.
    detections.sort(key=lambda tup: tup[1])
    clustered = []
    clustered_indices = []
    for master_ind, master in enumerate(detections):
        keep = False
        mast_o = master.preferred_origin()
        mast_time = mast_o.time
        mast_loc = (mast_o.latitude, mast_o.longitude, mast_o.depth // 1000)
        for slave in detections:
            slave_o = slave.preferred_origin()
            slave_time = slave_o.time
            slave_loc = (slave_o.latitude, slave_o.longitude,
                         slave_o.depth // 1000)
            if not master.resource_id == slave.resource_id and\
               abs((mast_time - slave_time).total_seconds()) <= t_thresh and\
               dist_calc(mast_loc, slave_loc) <= d_thresh:
                # If the slave events is close in time and space to the master
                # keep it and break out of the loop.
                keep = True
                break
        if keep:
            clustered.append(master)
            clustered_indices.append(master_ind)

    return clustered, clustered_indices
Example #13
0
def dist_mat_km(catalog):
    """
    Compute the distance matrix for all a catalog using epicentral separation.

    Will give physical distance in kilometers.

    :type catalog: obspy.core.event.Catalog
    :param catalog: Catalog for which to compute the distance matrix

    :returns: distance matrix
    :rtype: :class:`numpy.ndarray`
    """
    # Initialize square matrix
    dist_mat = np.array([np.array([0.0] * len(catalog))] *
                        len(catalog))
    # Calculate distance vector for each event
    for i, master in enumerate(catalog):
        mast_list = []
        if master.preferred_origin():
            master_ori = master.preferred_origin()
        else:
            master_ori = master.origins[-1]
        master_tup = (master_ori.latitude,
                      master_ori.longitude,
                      master_ori.depth // 1000)
        for slave in catalog:
            if slave.preferred_origin():
                slave_ori = slave.preferred_origin()
            else:
                slave_ori = slave.origins[-1]
            slave_tup = (slave_ori.latitude,
                         slave_ori.longitude,
                         slave_ori.depth // 1000)
            mast_list.append(dist_calc(master_tup, slave_tup))
        # Sort the list into the dist_mat structure
        for j in range(i, len(catalog)):
            dist_mat[i, j] = mast_list[j]
    # Reshape the distance matrix
    for i in range(1, len(catalog)):
        for j in range(i):
            dist_mat[i, j] = dist_mat.T[i, j]
    return dist_mat
Example #14
0
 def test_dist_mat_km(self):
     """Test spacial clustering."""
     dist_mat = dist_mat_km(self.cat)
     self.assertEqual(len(dist_mat), len(self.cat))
     # Diagonal should be zeros
     self.assertTrue(np.all(dist_mat.diagonal() == 0))
     # Should be symmetric
     for i in range(len(self.cat)):
         for j in range(len(self.cat)):
             self.assertEqual(dist_mat[i, j], dist_mat[j, i])
             master_ori = (self.cat[i].preferred_origin()
                           or self.cat[i].origins[0])
             slave_ori = (self.cat[j].preferred_origin()
                          or self.cat[j].origins[0])
             self.assertAlmostEqual(
                 dist_mat[i, j],
                 dist_calc((master_ori.latitude, master_ori.longitude,
                            master_ori.depth / 1000),
                           (slave_ori.latitude, slave_ori.longitude,
                            slave_ori.depth / 1000)), 6)
Example #15
0
def space_time_cluster(detections, t_thresh, d_thresh):
    """
    Function to cluster detections in space and time, use to seperate repeaters
    from other events

    :type detections: List
    :param detections: List of tuple of tuple of location (lat, lon, depth (km)),\
            and time as a datetime object
    :type t_thresh: float
    :param t_thresh: Maximum inter-event time threshold in seconds
    :type d_thresh: float
    :param d_thresh: Maximum inter-event distance in km

    :returns: List of tuple (detections, clustered) and list of indeces of\
            clustered detections
    """
    from eqcorrscan.utils.mag_calc import dist_calc
    import datetime as dt
    # Ensure they are sorted by time first, not that we need it.
    detections.sort(key=lambda tup:tup[1])
    clustered=[]
    clustered_indeces=[]
    for master_ind, master in enumerate(detections):
        keep=False
        for slave in detections:
            if not master==slave and\
               abs((master[1] - slave[1]).total_seconds()) <= t_thresh and \
               dist_calc(master[0], slave[0]) <= d_thresh:
                # If the slave events is close in time and space to the master
                # keep it and break out of the loop.
                keep=True
                break
        if keep:
            clustered.append(master)
            clustered_indeces.append(master_ind)

    return clustered, clustered_indeces
Example #16
0
 def test_write_catalog(self):
     """
     Simple testing function for the write_catalogue function in \
     catalog_to_dd.
     """
     from eqcorrscan.utils.catalog_to_dd import write_catalog
     from eqcorrscan.utils.mag_calc import dist_calc
     from eqcorrscan.utils import sfile_util
     import glob
     import os
     # Set forced variables
     maximum_seperation = 1  # Maximum inter-event seperation in km
     minimum_links = 8  # Minimum inter-event links to generate a pair
     # We have to make an event list first
     testing_path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                                 'test_data', 'REA', 'TEST_')
     sfile_list = glob.glob(os.path.join(testing_path, '*L.S??????'))
     event_ids = list(range(len(sfile_list)))
     event_list = zip(event_ids, sfile_list)
     write_catalog(event_list=event_list,
                   max_sep=maximum_seperation,
                   min_link=minimum_links)
     self.assertTrue(os.path.isfile('dt.ct'))
     # Check dt.ct file, should contain only a few linked events
     dt_file_out = open('dt.ct', 'r')
     event_pairs = []
     for i, line in enumerate(dt_file_out):
         if line[0] == '#':
             if i != 0:
                 # Check the number of links
                 self.assertTrue(len(event_links) >= minimum_links)
                 # Check the distance between events
                 event_1_name = [event[1] for event in event_list
                                 if event[0] ==
                                 int(event_pair.split()[1])][0]
                 event_2_name = [event[1] for event in event_list
                                 if event[0] ==
                                 int(event_pair.split()[2])][0]
                 event_1 = sfile_util.readheader(event_1_name)
                 event_2 = sfile_util.readheader(event_2_name)
                 event_1_location = (event_1.origins[0].latitude,
                                     event_1.origins[0].longitude,
                                     event_1.origins[0].depth / 1000)
                 event_2_location = (event_2.origins[0].latitude,
                                     event_2.origins[0].longitude,
                                     event_2.origins[0].depth / 1000)
                 hypocentral_seperation = dist_calc(event_1_location,
                                                    event_2_location)
                 self.assertTrue(hypocentral_seperation <
                                 maximum_seperation)
                 # Check that the differential times are accurate
                 event_1_picks = sfile_util.readpicks(event_1_name).picks
                 event_2_picks = sfile_util.readpicks(event_2_name).picks
                 for pick_pair in event_links:
                     station = pick_pair.split()[0]
                     event_1_travel_time_output = pick_pair.split()[1]
                     event_2_travel_time_output = pick_pair.split()[2]
                     weight = pick_pair.split()[3]
                     phase = pick_pair.split()[4]
                     # Extract the relevant pick information from the
                     # two sfiles
                     for pick in event_1_picks:
                         if pick.waveform_id.station_code == station:
                             if pick.phase_hint[0].upper() == phase:
                                 event_1_pick = pick
                     for pick in event_2_picks:
                         if pick.waveform_id.station_code == station:
                             if pick.phase_hint[0].upper() == phase:
                                 event_2_pick = pick
                     # Calculate the travel-time
                     event_1_travel_time_input = event_1_pick.time -\
                         event_1.origins[0].time
                     event_2_travel_time_input = event_2_pick.time -\
                         event_2.origins[0].time
                     self.assertEqual(event_1_travel_time_input,
                                      float(event_1_travel_time_output))
                     self.assertEqual(event_2_travel_time_input,
                                      float(event_2_travel_time_output))
             event_pair = line
             event_pairs.append(line)
             event_links = []
         else:
             event_links.append(line)
     self.assertTrue(os.path.isfile('phase.dat'))
     dt_file_out.close()
     os.remove('phase.dat')
     os.remove('dt.ct')
     if os.path.isfile('dt.ct2'):
         os.remove('dt.ct2')
Example #17
0
def write_correlations(event_list,
                       wavbase,
                       extract_len,
                       pre_pick,
                       shift_len,
                       lowcut=1.0,
                       highcut=10.0,
                       max_sep=8,
                       min_link=8,
                       cc_thresh=0.0,
                       plotvar=False,
                       debug=0):
    """
    Write a dt.cc file for hypoDD input for a given list of events.

    Takes an input list of events and computes pick refinements by correlation.
    Outputs two files, dt.cc and dt.cc2, each provides a different weight,
    dt.cc uses weights of the cross-correlation, and dt.cc2 provides weights
    as the square of the cross-correlation.

    :type event_list: list
    :param event_list: List of tuples of event_id (int) and sfile (String)
    :type wavbase: str
    :param wavbase: Path to the seisan wave directory that the wavefiles in the
                    S-files are stored
    :type extract_len: float
    :param extract_len: Length in seconds to extract around the pick
    :type pre_pick: float
    :param pre_pick: Time before the pick to start the correlation window
    :type shift_len: float
    :param shift_len: Time to allow pick to vary
    :type lowcut: float
    :param lowcut: Lowcut in Hz - default=1.0
    :type highcut: float
    :param highcut: Highcut in Hz - default=10.0
    :type max_sep: float
    :param max_sep: Maximum separation between event pairs in km
    :type min_link: int
    :param min_link: Minimum links for an event to be paired
    :type cc_thresh: float
    :param cc_thresh: Threshold to include cross-correlation results.
    :type plotvar: bool
    :param plotvar: To show the pick-correction plots, defualts to False.
    :type debug: int
    :param debug: Variable debug levels from 0-5, higher=more output.

    .. warning:: This is not a fast routine!

    .. warning::
        In contrast to seisan's corr routine, but in accordance with the
        hypoDD manual, this outputs corrected differential time.

    .. note::
        Currently we have not implemented a method for taking
        unassociated event objects and wavefiles.  As such if you have events \
        with associated wavefiles you are advised to generate Sfiles for each \
        event using the sfile_util module prior to this step.

    .. note::
        There is no provision to taper waveforms within these functions, if you
        desire this functionality, you should apply the taper before calling
        this.  Note the :func:`obspy.Trace.taper` functions.
    """
    warnings.filterwarnings(action="ignore",
                            message="Maximum of cross correlation " +
                            "lower than 0.8: *")
    corr_list = []
    f = open('dt.cc', 'w')
    f2 = open('dt.cc2', 'w')
    k_events = len(list(event_list))
    for i, master in enumerate(event_list):
        master_sfile = master[1]
        if debug > 1:
            print('Computing correlations for master: %s' % master_sfile)
        master_event_id = master[0]
        master_event = read_nordic(master_sfile)[0]
        master_picks = master_event.picks
        master_ori_time = master_event.origins[0].time
        master_location = (master_event.origins[0].latitude,
                           master_event.origins[0].longitude,
                           master_event.origins[0].depth / 1000.0)
        master_wavefiles = readwavename(master_sfile)
        masterpath = glob.glob(wavbase + os.sep + master_wavefiles[0])
        if masterpath:
            masterstream = read(masterpath[0])
        if len(master_wavefiles) > 1:
            for wavefile in master_wavefiles:
                try:
                    masterstream += read(os.join(wavbase, wavefile))
                except:
                    raise IOError("Couldn't find wavefile")
                    continue
        for j in range(i + 1, k_events):
            # Use this tactic to only output unique event pairings
            slave_sfile = event_list[j][1]
            if debug > 2:
                print('Comparing to event: %s' % slave_sfile)
            slave_event_id = event_list[j][0]
            slave_wavefiles = readwavename(slave_sfile)
            try:
                slavestream = read(wavbase + os.sep + slave_wavefiles[0])
            except:
                raise IOError('No wavefile found: ' + slave_wavefiles[0] +
                              ' ' + slave_sfile)
            if len(slave_wavefiles) > 1:
                for wavefile in slave_wavefiles:
                    try:
                        slavestream += read(wavbase + os.sep + wavefile)
                    except IOError:
                        print('No waveform found: %s' %
                              (wavbase + os.sep + wavefile))
                        continue
            # Write out the header line
            event_text = '#' + str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10) + ' 0.0   \n'
            event_text2 = '#' + str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10) + ' 0.0   \n'
            slave_event = read_nordic(slave_sfile)[0]
            slave_picks = slave_event.picks
            slave_ori_time = slave_event.origins[0].time
            slave_location = (slave_event.origins[0].latitude,
                              slave_event.origins[0].longitude,
                              slave_event.origins[0].depth / 1000.0)
            if dist_calc(master_location, slave_location) > max_sep:
                if debug > 0:
                    print('Seperation exceeds max_sep: %s' %
                          (dist_calc(master_location, slave_location)))
                continue
            links = 0
            phases = 0
            for pick in master_picks:
                if not hasattr(pick, 'phase_hint') or \
                                len(pick.phase_hint) == 0:
                    warnings.warn('No phase-hint for pick:')
                    print(pick)
                    continue
                if pick.phase_hint[0].upper() not in ['P', 'S']:
                    warnings.warn('Will only use P or S phase picks')
                    print(pick)
                    continue
                    # Only use P and S picks, not amplitude or 'other'
                # Find station, phase pairs
                # Added by Carolin
                slave_matches = [
                    p for p in slave_picks if hasattr(p, 'phase_hint')
                    and p.phase_hint == pick.phase_hint and
                    p.waveform_id.station_code == pick.waveform_id.station_code
                ]

                if masterstream.select(station=pick.waveform_id.station_code,
                                       channel='*' +
                                       pick.waveform_id.channel_code[-1]):
                    mastertr = masterstream.\
                        select(station=pick.waveform_id.station_code,
                               channel='*' +
                               pick.waveform_id.channel_code[-1])[0]
                elif debug > 1:
                    print('No waveform data for ' +
                          pick.waveform_id.station_code + '.' +
                          pick.waveform_id.channel_code)
                    print(pick.waveform_id.station_code + '.' +
                          pick.waveform_id.channel_code + ' ' + slave_sfile +
                          ' ' + master_sfile)
                    break
                # Loop through the matches
                for slave_pick in slave_matches:
                    if slavestream.select(
                            station=slave_pick.waveform_id.station_code,
                            channel='*' +
                            slave_pick.waveform_id.channel_code[-1]):
                        slavetr = slavestream.\
                            select(station=slave_pick.waveform_id.station_code,
                                   channel='*' + slave_pick.waveform_id.
                                   channel_code[-1])[0]
                    else:
                        print('No slave data for ' +
                              slave_pick.waveform_id.station_code + '.' +
                              slave_pick.waveform_id.channel_code)
                        print(pick.waveform_id.station_code + '.' +
                              pick.waveform_id.channel_code + ' ' +
                              slave_sfile + ' ' + master_sfile)
                        break
                    # Correct the picks
                    try:
                        correction, cc =\
                            xcorr_pick_correction(
                                pick.time, mastertr, slave_pick.time,
                                slavetr, pre_pick, extract_len - pre_pick,
                                shift_len, filter="bandpass",
                                filter_options={'freqmin': lowcut,
                                                'freqmax': highcut},
                                plot=plotvar)
                        # Get the differential travel time using the
                        # corrected time.
                        # Check that the correction is within the allowed shift
                        # This can occur in the obspy routine when the
                        # correlation function is increasing at the end of the
                        # window.
                        if abs(correction) > shift_len:
                            warnings.warn('Shift correction too large, ' +
                                          'will not use')
                            continue
                        correction = (pick.time - master_ori_time) -\
                            (slave_pick.time + correction - slave_ori_time)
                        links += 1
                        if cc >= cc_thresh:
                            weight = cc
                            phases += 1
                            # added by Caro
                            event_text += pick.waveform_id.station_code.\
                                ljust(5) + _cc_round(correction, 3).\
                                rjust(11) + _cc_round(weight, 3).rjust(8) +\
                                ' ' + pick.phase_hint + '\n'
                            event_text2 += pick.waveform_id.station_code\
                                .ljust(5) + _cc_round(correction, 3).\
                                rjust(11) +\
                                _cc_round(weight * weight, 3).rjust(8) +\
                                ' ' + pick.phase_hint + '\n'
                            if debug > 3:
                                print(event_text)
                        else:
                            print('cc too low: %s' % cc)
                        corr_list.append(cc * cc)
                    except:
                        msg = "Couldn't compute correlation correction"
                        warnings.warn(msg)
                        continue
            if links >= min_link and phases > 0:
                f.write(event_text)
                f2.write(event_text2)
    if plotvar:
        plt.hist(corr_list, 150)
        plt.show()
    # f.write('\n')
    f.close()
    f2.close()
    return
Example #18
0
def write_catalog(event_list, max_sep=8, min_link=8, debug=0):
    """
    Generate a dt.ct for hypoDD for a series of events.

    Takes input event list from
    :func:`eqcorrscan.utils.catalog_to_dd.write_event` as a list of tuples of
    event id and sfile.  It will read the pick information from the seisan
    formated s-file using the sfile_util utilities.

    :type event_list: list
    :param event_list: List of tuples of event_id (int) and sfile (String)
    :type max_sep: float
    :param max_sep: Maximum separation between event pairs in km
    :type min_link: int
    :param min_link:
        Minimum links for an event to be paired, e.g. minimum number of picks
        from the same station and channel (and phase) that are shared between
        two events for them to be paired.
    :type debug: int
    :param debug: Debug output level.

    :returns: list of stations that have been used in this catalog

    .. note::
        We have not yet implemented a method for taking unassociated event
        objects and wavefiles.  As such if you have events with associated
        wavefiles you are advised to generate Sfiles for each event using
        the :mod:`eqcorrscan.utils.sfile_util` module prior to this step.
    """
    # Cope with possibly being passed a zip in python 3.x
    event_list = list(event_list)
    f = open('dt.ct', 'w')
    f2 = open('dt.ct2', 'w')
    fphase = open('phase.dat', 'w')
    stations = []
    evcount = 0
    for i, master in enumerate(event_list):
        master_sfile = master[1]
        master_event_id = master[0]
        master_event = read_nordic(master_sfile)[0]
        master_ori_time = master_event.origins[0].time
        master_location = (master_event.origins[0].latitude,
                           master_event.origins[0].longitude,
                           master_event.origins[0].depth / 1000)
        if len(master_event.magnitudes) > 0:
            master_magnitude = master_event.magnitudes[0].mag or ' '
        else:
            master_magnitude = ' '
        header = '# ' + \
            master_ori_time.strftime('%Y  %m  %d  %H  %M  %S.%f') +\
            ' ' + str(master_location[0]).ljust(8) + ' ' +\
            str(master_location[1]).ljust(8) + ' ' +\
            str(master_location[2]).ljust(4) + ' ' +\
            str(master_magnitude).ljust(4) + ' 0.0 0.0 0.0' +\
            str(master_event_id).rjust(4)
        fphase.write(header + '\n')
        for pick in master_event.picks:
            if not hasattr(pick, 'phase_hint') or len(pick.phase_hint) == 0:
                warnings.warn('No phase-hint for pick:')
                print(pick)
                continue
            if pick.phase_hint[0].upper() in ['P', 'S']:
                weight = [
                    arrival.time_weight
                    for arrival in master_event.origins[0].arrivals
                    if arrival.pick_id == pick.resource_id
                ][0]
                # Convert seisan weight to hypoDD 0-1 weights
                if weight == 0:
                    weight = 1.0
                elif weight == 9:
                    weight = 0.0
                else:
                    weight = 1 - weight / 4.0
                fphase.write(pick.waveform_id.station_code + '  ' +
                             _cc_round(pick.time -
                                       master_ori_time, 3).rjust(6) + '   ' +
                             str(weight).ljust(5) + pick.phase_hint + '\n')
        for j in range(i + 1, len(event_list)):
            # Use this tactic to only output unique event pairings
            slave_sfile = event_list[j][1]
            slave_event_id = event_list[j][0]
            # Write out the header line
            event_text = '#' + str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10) + '\n'
            event_text2 = '#' + str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10) + '\n'
            slave_event = read_nordic(slave_sfile)[0]
            slave_ori_time = slave_event.origins[0].time
            slave_location = (slave_event.origins[0].latitude,
                              slave_event.origins[0].longitude,
                              slave_event.origins[0].depth / 1000)
            if dist_calc(master_location, slave_location) > max_sep:
                continue
            links = 0  # Count the number of linkages
            for pick in master_event.picks:
                if not hasattr(pick, 'phase_hint') or\
                                len(pick.phase_hint) == 0:
                    continue
                if pick.phase_hint[0].upper() not in ['P', 'S']:
                    continue
                    # Only use P and S picks, not amplitude or 'other'
                # Added by Carolin
                slave_matches = [
                    p for p in slave_event.picks
                    if hasattr(p, 'phase_hint') and p.phase_hint ==
                    pick.phase_hint and p.waveform_id.station_code.upper() ==
                    pick.waveform_id.station_code.upper()
                ]
                # Loop through the matches
                for slave_pick in slave_matches:
                    links += 1
                    master_weight = [
                        arrival.time_weight
                        for arrival in master_event.origins[0].arrivals
                        if arrival.pick_id == pick.resource_id
                    ][0]
                    slave_weight = [
                        arrival.time_weight
                        for arrival in slave_event.origins[0].arrivals
                        if arrival.pick_id == slave_pick.resource_id
                    ][0]
                    master_weight = str(int(master_weight))
                    slave_weight = str(int(slave_weight))
                    event_text += pick.waveform_id.station_code.ljust(5) +\
                        _cc_round(pick.time - master_ori_time, 3).rjust(11) +\
                        _cc_round(slave_pick.time -
                                  slave_ori_time, 3).rjust(8) +\
                        _av_weight(master_weight, slave_weight).rjust(7) +\
                        ' ' + pick.phase_hint + '\n'
                    # Added by Carolin
                    event_text2 += pick.waveform_id.station_code.ljust(5) +\
                        _cc_round(pick.time - master_ori_time, 3).rjust(11) +\
                        _cc_round(slave_pick.time -
                                  slave_ori_time, 3).rjust(8) +\
                        _av_weight(master_weight, slave_weight).rjust(7) +\
                        ' ' + pick.phase_hint + '\n'
                    stations.append(pick.waveform_id.station_code)
            if links >= min_link:
                f.write(event_text)
                f2.write(event_text2)
                evcount += 1
    print('You have ' + str(evcount) + ' links')
    # f.write('\n')
    f.close()
    f2.close()
    fphase.close()
    return list(set(stations))
Example #19
0
def write_correlations(event_list, wavbase, extract_len, pre_pick, shift_len,
                       lowcut=1.0, highcut=10.0, max_sep=4, min_link=8,
                       coh_thresh=0.0, coherence_weight=True, plotvar=False):
    """
    Function to write a dt.cc file for hypoDD input - takes an input list of
    events and computes pick refienements by correlation.

    :type event_list: list of tuple
    :param event_list: List of tuples of event_id (int) and sfile (String)
    :type wavbase: str
    :param wavbase: Path to the seisan wave directory that the wavefiles in the
                    S-files are stored
    :type extract_len: float
    :param extract_len: Length in seconds to extract around the pick
    :type pre_pick: float
    :param pre_pick: Time before the pick to start the correlation window
    :type shift_len: float
    :param shift_len: Time to allow pick to vary
    :type lowcut: float
    :param lowcut: Lowcut in Hz - default=1.0
    :type highcut: float
    :param highcut: Highcut in Hz - deafult=10.0
    :type max_sep: float
    :param max_sep: Maximum seperation between event pairs in km
    :type min_link: int
    :param min_link: Minimum links for an event to be paired
    :type coherence_weight: bool
    :param coherence_weight: Use coherence to weight the dt.cc file, or the \
        raw cross-correlation value, defaults to false which uses the cross-\
        correlation value.
    :type plotvar: bool
    :param plotvar: To show the pick-correction plots, defualts to False.

    .. warning:: This is not a fast routine!

    .. warning:: In contrast to seisan's \
        corr routine, but in accordance with the hypoDD manual, this outputs \
        corrected differential time.

    .. note:: Currently we have not implemented a method for taking \
        unassociated event objects and wavefiles.  As such if you have events \
        with associated wavefiles you are advised to generate Sfiles for each \
        event using the sfile_util module prior to this step.
    """
    import obspy
    if int(obspy.__version__.split('.')[0]) > 0:
        from obspy.signal.cross_correlation import xcorr_pick_correction
    else:
        from obspy.signal.cross_correlation import xcorrPickCorrection \
            as xcorr_pick_correction
    import matplotlib.pyplot as plt
    from obspy import read
    from eqcorrscan.utils.mag_calc import dist_calc
    import glob
    import warnings

    corr_list = []
    f = open('dt.cc', 'w')
    f2 = open('dt.cc2', 'w')
    for i, master in enumerate(event_list):
        master_sfile = master[1]
        master_event_id = master[0]
        master_picks = sfile_util.readpicks(master_sfile).picks
        master_event = sfile_util.readheader(master_sfile)
        master_ori_time = master_event.origins[0].time
        master_location = (master_event.origins[0].latitude,
                           master_event.origins[0].longitude,
                           master_event.origins[0].depth)
        master_wavefiles = sfile_util.readwavename(master_sfile)
        masterpath = glob.glob(wavbase + os.sep + master_wavefiles[0])
        if masterpath:
            masterstream = read(masterpath[0])
        if len(master_wavefiles) > 1:
            for wavefile in master_wavefiles:
                try:
                    masterstream += read(os.join(wavbase, wavefile))
                except:
                    continue
                    raise IOError("Couldn't find wavefile")
        for j in range(i+1, len(event_list)):
            # Use this tactic to only output unique event pairings
            slave_sfile = event_list[j][1]
            slave_event_id = event_list[j][0]
            slave_wavefiles = sfile_util.readwavename(slave_sfile)
            try:
                # slavestream=read(wavbase+'/*/*/'+slave_wavefiles[0])
                slavestream = read(wavbase + os.sep + slave_wavefiles[0])
            except:
                # print(slavestream)
                raise IOError('No wavefile found: '+slave_wavefiles[0]+' ' +
                              slave_sfile)
            if len(slave_wavefiles) > 1:
                for wavefile in slave_wavefiles:
                    # slavestream+=read(wavbase+'/*/*/'+wavefile)
                    try:
                        slavestream += read(wavbase+'/'+wavefile)
                    except:
                        continue
            # Write out the header line
            event_text = '#'+str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10)+' 0.0   \n'
            event_text2 = '#'+str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10)+' 0.0   \n'
            slave_picks = sfile_util.readpicks(slave_sfile).picks
            slave_event = sfile_util.readheader(slave_sfile)
            slave_ori_time = slave_event.origins[0].time
            slave_location = (slave_event.origins[0].latitude,
                              slave_event.origins[0].longitude,
                              slave_event.origins[0].depth)
            if dist_calc(master_location, slave_location) > max_sep:
                continue
            links = 0
            phases = 0
            for pick in master_picks:
                if pick.phase_hint[0].upper() not in ['P', 'S']:
                    continue
                    # Only use P and S picks, not amplitude or 'other'
                # Find station, phase pairs
                # Added by Carolin
                slave_matches = [p for p in slave_picks
                                 if p.phase_hint == pick.phase_hint
                                 and p.waveform_id.station_code ==
                                 pick.waveform_id.station_code]

                if masterstream.select(station=pick.waveform_id.station_code,
                                       channel='*' +
                                       pick.waveform_id.channel_code[-1]):
                    mastertr = masterstream.\
                        select(station=pick.waveform_id.station_code,
                               channel='*' +
                               pick.waveform_id.channel_code[-1])[0]
                else:
                    print('No waveform data for ' +
                          pick.waveform_id.station_code + '.' +
                          pick.waveform_id.channel_code)
                    print(pick.waveform_id.station_code +
                          '.' + pick.waveform_id.channel_code +
                          ' ' + slave_sfile+' ' + master_sfile)
                    break
                # Loop through the matches
                for slave_pick in slave_matches:
                    if slavestream.select(station=slave_pick.waveform_id.
                                          station_code,
                                          channel='*'+slave_pick.waveform_id.
                                          channel_code[-1]):
                        slavetr = slavestream.\
                            select(station=slave_pick.waveform_id.station_code,
                                   channel='*'+slave_pick.waveform_id.
                                   channel_code[-1])[0]
                    else:
                        print('No slave data for ' +
                              slave_pick.waveform_id.station_code + '.' +
                              slave_pick.waveform_id.channel_code)
                        print(pick.waveform_id.station_code +
                              '.' + pick.waveform_id.channel_code +
                              ' ' + slave_sfile + ' ' + master_sfile)
                        break
                    # Correct the picks
                    try:
                        correction, cc =\
                            xcorr_pick_correction(pick.time, mastertr,
                                                  slave_pick.time,
                                                  slavetr, pre_pick,
                                                  extract_len - pre_pick,
                                                  shift_len, filter="bandpass",
                                                  filter_options={'freqmin':
                                                                  lowcut,
                                                                  'freqmax':
                                                                  highcut},
                                                  plot=plotvar)
                        # Get the differntial travel time using the
                        # corrected time.
                        # Check that the correction is within the allowed shift
                        # This can occur in the obspy routine when the
                        # correlation function is increasing at the end of the
                        # window.
                        if abs(correction) > shift_len:
                            warnings.warn('Shift correction too large, ' +
                                          'will not use')
                            continue
                        correction = (pick.time - master_ori_time) -\
                            (slave_pick.time + correction - slave_ori_time)
                        links += 1
                        if cc * cc >= coh_thresh:
                            if coherence_weight:
                                weight = cc * cc
                            else:
                                weight = cc
                            phases += 1
                            # added by Caro
                            event_text += pick.waveform_id.station_code.\
                                ljust(5) + _cc_round(correction, 3).\
                                rjust(11) + _cc_round(weight, 3).rjust(8) +\
                                ' '+pick.phase_hint+'\n'
                            event_text2 += pick.waveform_id.station_code\
                                .ljust(5).upper() +\
                                _cc_round(correction, 3).rjust(11) +\
                                _cc_round(weight, 3).rjust(8) +\
                                ' '+pick.phase_hint+'\n'

                            # links+=1
                        corr_list.append(cc*cc)
                    except:
                        # Should warn here
                        msg = "Couldn't compute correlation correction"
                        warnings.warn(msg)
                        continue
            if links >= min_link and phases > 0:
                f.write(event_text)
                f2.write(event_text2)
    if plotvar:
        plt.hist(corr_list, 150)
        plt.show()
    # f.write('\n')
    f.close()
    f2.close()
    return
Example #20
0
def write_catalog(event_list, max_sep=1, min_link=8):
    """
    Function to write the dt.ct file needed by hypoDD - takes input event list
    from write_event as a list of tuples of event id and sfile.  It will read
    the pick information from the seisan formated s-file using the sfile_util
    utilities.

    :type event_list: list of tuple
    :param event_list: List of tuples of event_id (int) and sfile (String)
    :type max_sep: float
    :param max_sep: Maximum seperation between event pairs in km
    :type min_link: int
    :param min_link: Minimum links for an event to be paired

    :returns: list stations

    .. note:: Currently we have not implemented a method for taking \
        unassociated event objects and wavefiles.  As such if you have events \
        with associated wavefiles you are advised to generate Sfiles for each \
        event using the sfile_util module prior to this step.
    """
    from eqcorrscan.utils.mag_calc import dist_calc
    f = open('dt.ct', 'w')
    f2 = open('dt.ct2', 'w')
    fphase = open('phase.dat', 'w')
    stations = []
    evcount = 0
    for i, master in enumerate(event_list):
        master_sfile = master[1]
        master_event_id = master[0]
        master_event = sfile_util.readpicks(master_sfile)
        master_ori_time = master_event.origins[0].time
        master_location = (master_event.origins[0].latitude,
                           master_event.origins[0].longitude,
                           master_event.origins[0].depth / 1000)
        if len(master_event.magnitudes) > 0:
            master_magnitude = master_event.magnitudes[0].mag or ' '
        else:
            master_magnitude = ' '
        header = '# '+master_ori_time.strftime('%Y  %m  %d  %H  %M  %S.%f') +\
            ' '+str(master_location[0]).ljust(8)+' ' +\
            str(master_location[1]).ljust(8)+' ' +\
            str(master_location[2]).ljust(4)+' ' +\
            str(master_magnitude).ljust(4)+' 0.0 0.0 0.0' +\
            str(master_event_id).rjust(4)
        fphase.write(header+'\n')
        for pick in master_event.picks:
            if pick.phase_hint[0].upper() in ['P', 'S']:
                weight = [arrival.time_weight
                          for arrival in master_event.origins[0].arrivals
                          if arrival.pick_id == pick.resource_id][0]
                # Convert seisan weight to hypoDD 0-1 weights
                if weight == 0:
                    weight = 1.0
                elif weight == 9:
                    weight = 0.0
                else:
                    weight = 1 - weight / 4.0
                fphase.write(pick.waveform_id.station_code+'  ' +
                             _cc_round(pick.time -
                                       master_ori_time, 3).rjust(6) +
                             '   '+str(weight).ljust(5)+pick.phase_hint+'\n')
        for j in range(i+1, len(event_list)):
            # Use this tactic to only output unique event pairings
            slave_sfile = event_list[j][1]
            slave_event_id = event_list[j][0]
            # Write out the header line
            event_text = '#'+str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10)+'\n'
            event_text2 = '#'+str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10)+'\n'
            slave_event = sfile_util.readpicks(slave_sfile)
            slave_ori_time = slave_event.origins[0].time
            slave_location = (slave_event.origins[0].latitude,
                              slave_event.origins[0].longitude,
                              slave_event.origins[0].depth / 1000)
            if dist_calc(master_location, slave_location) > max_sep:
                continue
            links = 0  # Count the number of linkages
            for pick in master_event.picks:
                if pick.phase_hint[0].upper() not in ['P', 'S']:
                    continue
                    # Only use P and S picks, not amplitude or 'other'
                # Added by Carolin
                slave_matches = [p for p in slave_event.picks
                                 if p.phase_hint == pick.phase_hint
                                 and p.waveform_id.station_code.upper() ==
                                 pick.waveform_id.station_code.upper()]
                # Loop through the matches
                for slave_pick in slave_matches:
                    links += 1
                    master_weight = [arrival.time_weight
                                     for arrival in master_event.
                                     origins[0].arrivals
                                     if arrival.pick_id == pick.resource_id][0]
                    slave_weight = [arrival.time_weight
                                    for arrival in slave_event.
                                    origins[0].arrivals
                                    if arrival.pick_id ==
                                    slave_pick.resource_id][0]
                    event_text += pick.waveform_id.station_code.ljust(5) +\
                        _cc_round(pick.time-master_ori_time, 3).rjust(11) +\
                        _cc_round(slave_pick.time-slave_ori_time, 3).rjust(8) +\
                        _av_weight(master_weight, slave_weight).rjust(7) +\
                        ' '+pick.phase_hint+'\n'
                    # Added by Carolin
                    event_text2 += pick.waveform_id.station_code.ljust(5) +\
                        _cc_round(pick.time-master_ori_time, 3).rjust(11) +\
                        _cc_round(slave_pick.time-slave_ori_time, 3).rjust(8) +\
                        _av_weight(master_weight, slave_weight).rjust(7) +\
                        ' '+pick.phase_hint+'\n'
                    stations.append(pick.waveform_id.station_code)
            if links >= min_link:
                f.write(event_text)
                f2.write(event_text2)
                evcount += 1
    print('You have '+str(evcount)+' links')
    # f.write('\n')
    f.close()
    f2.close()
    fphase.close()
    return list(set(stations))
Example #21
0
def write_correlations(event_list,
                       wavbase,
                       extract_len,
                       pre_pick,
                       shift_len,
                       lowcut=1.0,
                       highcut=10.0,
                       max_sep=4,
                       min_link=8,
                       coh_thresh=0.0,
                       coherence_weight=True,
                       plotvar=False):
    """
    Function to write a dt.cc file for hypoDD input - takes an input list of
    events and computes pick refienements by correlation.

    :type event_list: list of tuple
    :param event_list: List of tuples of event_id (int) and sfile (String)
    :type wavbase: str
    :param wavbase: Path to the seisan wave directory that the wavefiles in the
                    S-files are stored
    :type extract_len: float
    :param extract_len: Length in seconds to extract around the pick
    :type pre_pick: float
    :param pre_pick: Time before the pick to start the correlation window
    :type shift_len: float
    :param shift_len: Time to allow pick to vary
    :type lowcut: float
    :param lowcut: Lowcut in Hz - default=1.0
    :type highcut: float
    :param highcut: Highcut in Hz - deafult=10.0
    :type max_sep: float
    :param max_sep: Maximum seperation between event pairs in km
    :type min_link: int
    :param min_link: Minimum links for an event to be paired
    :type coherence_weight: bool
    :param coherence_weight: Use coherence to weight the dt.cc file, or the \
        raw cross-correlation value, defaults to false which uses the cross-\
        correlation value.
    :type plotvar: bool
    :param plotvar: To show the pick-correction plots, defualts to False.

    .. warning:: This is not a fast routine!

    .. warning:: In contrast to seisan's \
        corr routine, but in accordance with the hypoDD manual, this outputs \
        corrected differential time.

    .. note:: Currently we have not implemented a method for taking \
        unassociated event objects and wavefiles.  As such if you have events \
        with associated wavefiles you are advised to generate Sfiles for each \
        event using the sfile_util module prior to this step.
    """
    import obspy
    if int(obspy.__version__.split('.')[0]) > 0:
        from obspy.signal.cross_correlation import xcorr_pick_correction
    else:
        from obspy.signal.cross_correlation import xcorrPickCorrection \
            as xcorr_pick_correction
    import matplotlib.pyplot as plt
    from obspy import read
    from eqcorrscan.utils.mag_calc import dist_calc
    import glob
    import warnings

    corr_list = []
    f = open('dt.cc', 'w')
    f2 = open('dt.cc2', 'w')
    for i, master in enumerate(event_list):
        master_sfile = master[1]
        master_event_id = master[0]
        master_picks = sfile_util.readpicks(master_sfile).picks
        master_event = sfile_util.readheader(master_sfile)
        master_ori_time = master_event.origins[0].time
        master_location = (master_event.origins[0].latitude,
                           master_event.origins[0].longitude,
                           master_event.origins[0].depth)
        master_wavefiles = sfile_util.readwavename(master_sfile)
        masterpath = glob.glob(wavbase + os.sep + master_wavefiles[0])
        if masterpath:
            masterstream = read(masterpath[0])
        if len(master_wavefiles) > 1:
            for wavefile in master_wavefiles:
                try:
                    masterstream += read(os.join(wavbase, wavefile))
                except:
                    continue
                    raise IOError("Couldn't find wavefile")
        for j in range(i + 1, len(event_list)):
            # Use this tactic to only output unique event pairings
            slave_sfile = event_list[j][1]
            slave_event_id = event_list[j][0]
            slave_wavefiles = sfile_util.readwavename(slave_sfile)
            try:
                # slavestream=read(wavbase+'/*/*/'+slave_wavefiles[0])
                slavestream = read(wavbase + os.sep + slave_wavefiles[0])
            except:
                # print(slavestream)
                raise IOError('No wavefile found: ' + slave_wavefiles[0] +
                              ' ' + slave_sfile)
            if len(slave_wavefiles) > 1:
                for wavefile in slave_wavefiles:
                    # slavestream+=read(wavbase+'/*/*/'+wavefile)
                    try:
                        slavestream += read(wavbase + '/' + wavefile)
                    except:
                        continue
            # Write out the header line
            event_text = '#'+str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10)+' 0.0   \n'
            event_text2 = '#'+str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10)+' 0.0   \n'
            slave_picks = sfile_util.readpicks(slave_sfile).picks
            slave_event = sfile_util.readheader(slave_sfile)
            slave_ori_time = slave_event.origins[0].time
            slave_location = (slave_event.origins[0].latitude,
                              slave_event.origins[0].longitude,
                              slave_event.origins[0].depth)
            if dist_calc(master_location, slave_location) > max_sep:
                continue
            links = 0
            phases = 0
            for pick in master_picks:
                if pick.phase_hint[0].upper() not in ['P', 'S']:
                    continue
                    # Only use P and S picks, not amplitude or 'other'
                # Find station, phase pairs
                # Added by Carolin
                slave_matches = [
                    p for p in slave_picks
                    if p.phase_hint == pick.phase_hint and
                    p.waveform_id.station_code == pick.waveform_id.station_code
                ]

                if masterstream.select(station=pick.waveform_id.station_code,
                                       channel='*' +
                                       pick.waveform_id.channel_code[-1]):
                    mastertr = masterstream.\
                        select(station=pick.waveform_id.station_code,
                               channel='*' +
                               pick.waveform_id.channel_code[-1])[0]
                else:
                    print('No waveform data for ' +
                          pick.waveform_id.station_code + '.' +
                          pick.waveform_id.channel_code)
                    print(pick.waveform_id.station_code + '.' +
                          pick.waveform_id.channel_code + ' ' + slave_sfile +
                          ' ' + master_sfile)
                    break
                # Loop through the matches
                for slave_pick in slave_matches:
                    if slavestream.select(
                            station=slave_pick.waveform_id.station_code,
                            channel='*' +
                            slave_pick.waveform_id.channel_code[-1]):
                        slavetr = slavestream.\
                            select(station=slave_pick.waveform_id.station_code,
                                   channel='*'+slave_pick.waveform_id.
                                   channel_code[-1])[0]
                    else:
                        print('No slave data for ' +
                              slave_pick.waveform_id.station_code + '.' +
                              slave_pick.waveform_id.channel_code)
                        print(pick.waveform_id.station_code + '.' +
                              pick.waveform_id.channel_code + ' ' +
                              slave_sfile + ' ' + master_sfile)
                        break
                    # Correct the picks
                    try:
                        correction, cc =\
                            xcorr_pick_correction(pick.time, mastertr,
                                                  slave_pick.time,
                                                  slavetr, pre_pick,
                                                  extract_len - pre_pick,
                                                  shift_len, filter="bandpass",
                                                  filter_options={'freqmin':
                                                                  lowcut,
                                                                  'freqmax':
                                                                  highcut},
                                                  plot=plotvar)
                        # Get the differntial travel time using the
                        # corrected time.
                        # Check that the correction is within the allowed shift
                        # This can occur in the obspy routine when the
                        # correlation function is increasing at the end of the
                        # window.
                        if abs(correction) > shift_len:
                            warnings.warn('Shift correction too large, ' +
                                          'will not use')
                            continue
                        correction = (pick.time - master_ori_time) -\
                            (slave_pick.time + correction - slave_ori_time)
                        links += 1
                        if cc * cc >= coh_thresh:
                            if coherence_weight:
                                weight = cc * cc
                            else:
                                weight = cc
                            phases += 1
                            # added by Caro
                            event_text += pick.waveform_id.station_code.\
                                ljust(5) + _cc_round(correction, 3).\
                                rjust(11) + _cc_round(weight, 3).rjust(8) +\
                                ' '+pick.phase_hint+'\n'
                            event_text2 += pick.waveform_id.station_code\
                                .ljust(5).upper() +\
                                _cc_round(correction, 3).rjust(11) +\
                                _cc_round(weight, 3).rjust(8) +\
                                ' '+pick.phase_hint+'\n'

                            # links+=1
                        corr_list.append(cc * cc)
                    except:
                        # Should warn here
                        msg = "Couldn't compute correlation correction"
                        warnings.warn(msg)
                        continue
            if links >= min_link and phases > 0:
                f.write(event_text)
                f2.write(event_text2)
    if plotvar:
        plt.hist(corr_list, 150)
        plt.show()
    # f.write('\n')
    f.close()
    f2.close()
    return
Example #22
0
def write_catalog(event_list, max_sep=1, min_link=8):
    """
    Function to write the dt.ct file needed by hypoDD - takes input event list
    from write_event as a list of tuples of event id and sfile.  It will read
    the pick information from the seisan formated s-file using the sfile_util
    utilities.

    :type event_list: list of tuple
    :param event_list: List of tuples of event_id (int) and sfile (String)
    :type max_sep: float
    :param max_sep: Maximum seperation between event pairs in km
    :type min_link: int
    :param min_link: Minimum links for an event to be paired

    :returns: list stations

    .. note:: Currently we have not implemented a method for taking \
        unassociated event objects and wavefiles.  As such if you have events \
        with associated wavefiles you are advised to generate Sfiles for each \
        event using the sfile_util module prior to this step.
    """
    from eqcorrscan.utils.mag_calc import dist_calc
    f = open('dt.ct', 'w')
    f2 = open('dt.ct2', 'w')
    fphase = open('phase.dat', 'w')
    stations = []
    evcount = 0
    for i, master in enumerate(event_list):
        master_sfile = master[1]
        master_event_id = master[0]
        master_event = sfile_util.readpicks(master_sfile)
        master_ori_time = master_event.origins[0].time
        master_location = (master_event.origins[0].latitude,
                           master_event.origins[0].longitude,
                           master_event.origins[0].depth / 1000)
        if len(master_event.magnitudes) > 0:
            master_magnitude = master_event.magnitudes[0].mag or ' '
        else:
            master_magnitude = ' '
        header = '# '+master_ori_time.strftime('%Y  %m  %d  %H  %M  %S.%f') +\
            ' '+str(master_location[0]).ljust(8)+' ' +\
            str(master_location[1]).ljust(8)+' ' +\
            str(master_location[2]).ljust(4)+' ' +\
            str(master_magnitude).ljust(4)+' 0.0 0.0 0.0' +\
            str(master_event_id).rjust(4)
        fphase.write(header + '\n')
        for pick in master_event.picks:
            if pick.phase_hint[0].upper() in ['P', 'S']:
                weight = [
                    arrival.time_weight
                    for arrival in master_event.origins[0].arrivals
                    if arrival.pick_id == pick.resource_id
                ][0]
                # Convert seisan weight to hypoDD 0-1 weights
                if weight == 0:
                    weight = 1.0
                elif weight == 9:
                    weight = 0.0
                else:
                    weight = 1 - weight / 4.0
                fphase.write(pick.waveform_id.station_code + '  ' +
                             _cc_round(pick.time -
                                       master_ori_time, 3).rjust(6) + '   ' +
                             str(weight).ljust(5) + pick.phase_hint + '\n')
        for j in range(i + 1, len(event_list)):
            # Use this tactic to only output unique event pairings
            slave_sfile = event_list[j][1]
            slave_event_id = event_list[j][0]
            # Write out the header line
            event_text = '#'+str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10)+'\n'
            event_text2 = '#'+str(master_event_id).rjust(10) +\
                str(slave_event_id).rjust(10)+'\n'
            slave_event = sfile_util.readpicks(slave_sfile)
            slave_ori_time = slave_event.origins[0].time
            slave_location = (slave_event.origins[0].latitude,
                              slave_event.origins[0].longitude,
                              slave_event.origins[0].depth / 1000)
            if dist_calc(master_location, slave_location) > max_sep:
                continue
            links = 0  # Count the number of linkages
            for pick in master_event.picks:
                if pick.phase_hint[0].upper() not in ['P', 'S']:
                    continue
                    # Only use P and S picks, not amplitude or 'other'
                # Added by Carolin
                slave_matches = [
                    p for p in slave_event.picks
                    if p.phase_hint == pick.phase_hint
                    and p.waveform_id.station_code.upper() ==
                    pick.waveform_id.station_code.upper()
                ]
                # Loop through the matches
                for slave_pick in slave_matches:
                    links += 1
                    master_weight = [
                        arrival.time_weight
                        for arrival in master_event.origins[0].arrivals
                        if arrival.pick_id == pick.resource_id
                    ][0]
                    slave_weight = [
                        arrival.time_weight
                        for arrival in slave_event.origins[0].arrivals
                        if arrival.pick_id == slave_pick.resource_id
                    ][0]
                    event_text += pick.waveform_id.station_code.ljust(5) +\
                        _cc_round(pick.time-master_ori_time, 3).rjust(11) +\
                        _cc_round(slave_pick.time-slave_ori_time, 3).rjust(8) +\
                        _av_weight(master_weight, slave_weight).rjust(7) +\
                        ' '+pick.phase_hint+'\n'
                    # Added by Carolin
                    event_text2 += pick.waveform_id.station_code.ljust(5) +\
                        _cc_round(pick.time-master_ori_time, 3).rjust(11) +\
                        _cc_round(slave_pick.time-slave_ori_time, 3).rjust(8) +\
                        _av_weight(master_weight, slave_weight).rjust(7) +\
                        ' '+pick.phase_hint+'\n'
                    stations.append(pick.waveform_id.station_code)
            if links >= min_link:
                f.write(event_text)
                f2.write(event_text2)
                evcount += 1
    print('You have ' + str(evcount) + ' links')
    # f.write('\n')
    f.close()
    f2.close()
    fphase.close()
    return list(set(stations))
Example #23
0
def plot_event_well_dist(catalog, well_file, flow_start, diffs,
                         temp_list='all', method='scatter', starttime=None,
                         endtime=None, title=None, show=True):
    """
    Function to plot events with distance from well as a function of time.
    :param cat: catalog of events
    :param well_file: text file of xyz feedzone pts
    :param flow_start: Start UTCdt of well flow to model
    :param diffs: list of diffusion values to plot
    :param temp_list: list of templates for which we'll plot detections
    :param method: plot the 'scatter' or daily 'average' distance or both
    :return: matplotlib.pyplot.Axes
    """
    well_pts = format_well_data(well_file)
    # Grab only templates in the list
    cat = Catalog()
    filt_cat = Catalog()
    if starttime and endtime:
        filt_cat.events = [ev for ev in catalog if ev.origins[-1].time
                           < endtime and ev.origins[-1].time >= starttime]
    else:
        filt_cat = catalog
    cat.events = [ev for ev in filt_cat if
                  str(ev.resource_id).split('/')[-1].split('_')[0] in
                  temp_list or temp_list == 'all']
    time_dist_tups = []
    cat_start = min([ev.origins[-1].time.datetime for ev in cat])
    cat_end = max([ev.origins[-1].time.datetime for ev in cat])
    for ev in cat:
        if ev.origins[-1]:
            dist = min([dist_calc((ev.origins[-1].latitude,
                                   ev.origins[-1].longitude,
                                   ev.origins[-1].depth / 1000.),
                                  pt) for pt in well_pts])
            time_dist_tups.append((ev.origins[-1].time.datetime,
                                  dist))
    times, dists = zip(*time_dist_tups)
    # Make DataFrame for boxplotting
    dist_df = pd.DataFrame()
    dist_df['dists'] = pd.Series(dists, index=times)
    # Add daily grouping column to df (this is crap, but can't find better)
    dist_df['day_num'] =  [date2num(dto.replace(hour=12, minute=0, second=0,
                                                microsecond=0).to_pydatetime())
                           for dto in dist_df.index]
    dist_df['dto_num'] =  [date2num(dt) for dt in dist_df.index]
    # Now create the pressure envelopes
    # Creating hourly datetime increments
    start = pd.Timestamp(flow_start.datetime)
    end = pd.Timestamp(cat_end)
    t = pd.to_datetime(pd.date_range(start, end, freq='H'))
    t = [date2num(d) for d in t]
    # Now diffusion y vals
    diff_ys = []
    for d in diffs:
        diff_ys.append([np.sqrt(60 * d * i / 4000 * np.pi) # account for kms
                        for i in range(len(t))])
    # Plot 'em up
    fig, ax = plt.subplots(figsize=(7, 6))
    # First boxplots
    u_days = list(set(dist_df.day_num))
    bins = [dist_df.loc[dist_df['day_num'] == d]['dists'].values
            for d in u_days]
    positions = [d for d in u_days]
    bplots = ax.boxplot(bins, positions=positions, patch_artist=True,
                        flierprops={'markersize': 0}, manage_xticks=False)
    for patch in bplots['boxes']:
        patch.set_facecolor('lightblue')
        patch.set_alpha(0.5)
    # First diffusions
    for i, diff_y in enumerate(diff_ys):
        ax.plot(t, diff_y,
                label='Diffusion envelope, D={} $m^2/s$'.format(str(diffs[i])))
    # Now events
    if method != 'scatter':
        dates = []
        day_avg_dist = []
        for date in date_generator(cat_start, cat_end):
            dates.append(date)
            tdds = [tdd[1] for tdd in time_dist_tups if tdd[0] > date
                    and tdd[0] < date + timedelta(days=1)]
            day_avg_dist.append(np.mean(tdds))
    if method == 'scatter':
        ax.scatter(times, dists, color='gray', label='Event', s=10, alpha=0.5)
    elif method == 'average':
        ax.plot(dates, day_avg_dist)
    elif method == 'both':
        ax.scatter(times, dists)
        ax.plot(dates, day_avg_dist, color='r')
    # Plot formatting
    fig.autofmt_xdate()
    ax.legend()
    ax.set_ylim([0, 6])
    if title:
        ax.set_title(title, fontsize=19)
    else:
        ax.set_title('Fluid diffusion envelopes and earthquake distance')
    if starttime:
        ax.set_xlim([date2num(starttime.datetime), max(t)])
    else:
        ax.set_xlim([min(t), max(t)])
    ax.set_xlabel('Date')
    ax.set_ylabel('Distance (km)')
    fig.tight_layout()
    if show:
        fig.show()
    return ax
 def test_write_catalog(self):
     """
     Simple testing function for the write_catalogue function in \
     catalog_to_dd.
     """
     self.assertTrue(os.path.isfile('dt.ct'))
     # Check dt.ct file, should contain only a few linked events
     dt_file_out = open('dt.ct', 'r')
     event_pairs = []
     event_links = []
     event_pair = ''
     for i, line in enumerate(dt_file_out):
         if line[0] == '#':
             if i != 0:
                 # Check the number of links
                 self.assertTrue(len(event_links) >= self.minimum_links)
                 # Check the distance between events
                 event_1_name = [
                     event[1] for event in self.event_list
                     if event[0] == int(event_pair.split()[1])
                 ][0]
                 event_2_name = [
                     event[1] for event in self.event_list
                     if event[0] == int(event_pair.split()[2])
                 ][0]
                 event_1 = sfile_util.readheader(event_1_name)
                 event_2 = sfile_util.readheader(event_2_name)
                 event_1_location = (event_1.origins[0].latitude,
                                     event_1.origins[0].longitude,
                                     event_1.origins[0].depth / 1000)
                 event_2_location = (event_2.origins[0].latitude,
                                     event_2.origins[0].longitude,
                                     event_2.origins[0].depth / 1000)
                 hypocentral_seperation = dist_calc(event_1_location,
                                                    event_2_location)
                 self.assertTrue(
                     hypocentral_seperation < self.maximum_separation)
                 # Check that the differential times are accurate
                 event_1_picks = sfile_util.readpicks(event_1_name).picks
                 event_2_picks = sfile_util.readpicks(event_2_name).picks
                 for pick_pair in event_links:
                     station = pick_pair.split()[0]
                     event_1_travel_time_output = pick_pair.split()[1]
                     event_2_travel_time_output = pick_pair.split()[2]
                     # weight = pick_pair.split()[3]
                     phase = pick_pair.split()[4]
                     # Extract the relevant pick information from the
                     # two sfiles
                     for pick in event_1_picks:
                         if pick.waveform_id.station_code == station:
                             if pick.phase_hint[0].upper() == phase:
                                 event_1_pick = pick
                     for pick in event_2_picks:
                         if pick.waveform_id.station_code == station:
                             if pick.phase_hint[0].upper() == phase:
                                 event_2_pick = pick
                     # Calculate the travel-time
                     event_1_travel_time_input = event_1_pick.time -\
                         event_1.origins[0].time
                     event_2_travel_time_input = event_2_pick.time -\
                         event_2.origins[0].time
                     self.assertEqual(event_1_travel_time_input,
                                      float(event_1_travel_time_output))
                     self.assertEqual(event_2_travel_time_input,
                                      float(event_2_travel_time_output))
             event_pair = line
             event_pairs.append(line)
             event_links = []
         else:
             event_links.append(line)
     self.assertTrue(os.path.isfile('phase.dat'))
     dt_file_out.close()
Example #25
0
def write_hybridMT_input(cat,
                         sac_dir,
                         inv,
                         self_files,
                         outfile,
                         prepick,
                         postpick,
                         file_type='raw',
                         plot=False):
    """
    Umbrella function to handle writing input files for focimt and hybridMT

    :param cat: Catalog of events to write files for
    :param sac_dir: Root directory for detection SAC files
    :param inv: Inventory object containing all necessary station responses
    :param selfs: List containing directory names for template self detections
    :param prefilt: List of 4 corners for preconvolution bandpass
        For details see obspy.core.trace.Trace.remove_response() docs
    :return:
    """
    selfs = []
    for self_file in self_files:
        with open(self_file, 'r') as f:
            rdr = csv.reader(f)
            for row in rdr:
                selfs.append(str(row[0]))
    ev_dict = {}
    # Build prefilt dict (Assuming all wavs have been downsampled to 100 Hz)
    pf_dict = {
        'MERC': [0.001, 1.0, 35., 45.],
        'WPRZ': [0.001, 0.5, 35., 45.],
        'GEONET': [0.001, 0.01, 40., 48.]
    }
    # Loop through events
    for ev in cat:
        ev_id = str(ev.resource_id).split('/')[-1]
        print('Working on {}'.format(ev_id))
        self = [self for self in selfs if self.split('_')[0] == ev_id]
        orig = ev.origins[-1]
        if len(self
               ) == 0:  # Skip those with no self detection (outside fields)
            print('No self detection for %s' % ev_id)
            continue
        wavs = glob('%s/%s/*' % (sac_dir, self[0]))
        # Loop through arrivals and populate ev_dict with TOA, Backaz, etc...
        ev_dict[ev_id] = {}  # Allocate subdictionary
        ev_dict[ev_id]['phases'] = []
        ev_dict[ev_id]['header'] = None
        for arr in orig.arrivals:
            pick = arr.pick_id.get_referred_object()
            sta = pick.waveform_id.station_code
            chan = pick.waveform_id.channel_code
            print('{}.{}'.format(sta, chan))
            if chan[-1] != 'Z':
                continue
            sta_inv = inv.select(station=sta, channel=chan)
            # Do a rough incidence angle calculation based on dist and depth
            dist = dist_calc(
                (orig.latitude, orig.longitude, orig.depth / 1000.),
                (sta_inv[0][0].latitude, sta_inv[0][0].longitude,
                 (sta_inv[0][0].elevation - sta_inv[0][0][0].depth) / 1000.))
            aoi = 90. - np.degrees(np.arcsin(orig.depth / 1000. / dist))
            if np.isnan(aoi):
                aoi = 180. - arr.takeoff_angle
            # Establish which station we're working with
            if sta.endswith('Z'):
                if sta == 'WPRZ':
                    prefilt = pf_dict['WPRZ']
                else:
                    prefilt = pf_dict['GEONET']
            else:
                prefilt = pf_dict['MERC']
            wav_file = [
                wav for wav in wavs if wav.split('_')[-1].split('.')[0] == chan
                and wav.split('_')[-2] == sta
            ]
            if len(wav_file) == 0:
                print('Waveform directory not found.')
                continue
            # Read in the corresponding trace
            # Cosine taper and demeaning applied by default
            raw = read(wav_file[0])[0]
            tr = read(wav_file[0])[0].remove_response(inventory=inv,
                                                      pre_filt=prefilt,
                                                      output='DISP')
            # Invert polarity of SP instruments
            if not sta.endswith('Z'):
                tr.data *= -1
            # Trim around P pulse
            raw_sliced = raw.slice(starttime=pick.time - 0.2,
                                   endtime=pick.time + 1).copy()
            whole_tr = tr.slice(starttime=pick.time - 0.2,
                                endtime=pick.time + 1).copy()
            tr.trim(starttime=pick.time - prepick,
                    endtime=pick.time + postpick)
            pick_sample = int(prepick * tr.stats.sampling_rate)
            # Find the next index where trace crosses the 'zero' value
            # which we assume is the value at time of pick.
            # Take last 'zero' crossing of the trimmed wav, assuming we've
            # trimmed only half a cycle. Then integrate from pick time to
            # first sample with a swapped sign (+/- or -/+)
            # Make pick value zero
            leveled = tr.data - tr.data[pick_sample]
            # Determine some polarity info
            rel_min_max = argrelmax(np.abs(leveled))  #Relative peaks
            print(rel_min_max)
            try:
                if rel_min_max[0].shape[0] > 1:
                    print(leveled[rel_min_max])
                    rel_pk = np.argmax(np.abs(leveled[rel_min_max]))
                    print(rel_pk)
                    print(rel_min_max[0][rel_pk])
                    peak = leveled[rel_min_max[0][rel_pk]]  # Largest peak
                else:
                    try:
                        peak = leveled[rel_min_max][0]
                    except IndexError:
                        print('No relative maxima or minima')
                        continue
            except ValueError:
                print('No relative maxima or minima')
                continue
            print('Peak value: {!s}'.format(peak))
            polarity = np.sign(peak)  # Sign of largest peak
            print('Zero crossings at: {!s}'.format(
                np.where(np.diff(np.sign(leveled[pick_sample + 1:])) != 0)[0]))
            try:
                pulse = leveled[pick_sample:pick_sample + 1 + np.where(
                    np.diff(np.sign(leveled[pick_sample + 1:])) != 0)[0][-1] +
                                2]  # 2-sample fudge factor over crossing
            except IndexError as i:
                print('IndexError: {}'.format(i))
                if polarity == 1:
                    try:
                        pulse = leveled[pick_sample:argrelmin(leveled)[0][-1] +
                                        1]
                    except IndexError:
                        print('No zero crossing OR relative minimum.')
                        continue
                elif polarity == -1:
                    try:
                        pulse = leveled[pick_sample:argrelmax(leveled)[0][-1] +
                                        1]
                    except IndexError:
                        print('No zero crossing OR relative maximum.')
                        continue
            # Try to catch case where small min/max just post pick
            if len(pulse) < 6:
                print('{}'.format(
                    'Pulse is too short: likely due to small rel peak'))
                pulse = leveled[pick_sample:]
            omega = np.trapz(pulse)
            if plot:
                fig, (ax1, ax2, ax3) = plt.subplots(nrows=3, ncols=1)
                fig.suptitle('{}.{}'.format(sta, chan))
                ax1.plot(raw_sliced.data, label='raw')
                ax2.plot(whole_tr.data, label='Displacement')
                ax3.plot(leveled, color='k', label='Pulse')
                ax3.plot(np.arange(pick_sample,
                                   pick_sample + len(pulse),
                                   step=1),
                         pulse,
                         color='r')
                ax3.axvline(pick_sample,
                            linestyle='--',
                            color='grey',
                            label='Pick')
                plt.legend()
                plt.show()
                plt.close()
            # Now we can populate the strings in ev_dict
            if file_type == 'raw':
                ev_dict[ev_id]['phases'].append(
                    "  {} {} {} {!s} {!s} {!s} {!s} {!s} {!s} {!s}\n".format(
                        sta, chan[-1], pick.phase_hint, omega * polarity,
                        arr.azimuth, aoi, arr.takeoff_angle, 5000, dist * 1000,
                        2600))
            elif file_type == 'vel1d':
                x, y, z = dec_2_merc_meters(
                    sta_inv[0][0].longitude, sta_inv[0][0].latitude,
                    sta_inv[0][0].elevation - sta_inv[0][0][0].depth)
                ev_dict[ev_id]['phases'].append(
                    "  {} {} {} {!s} {!s} {!s} {!s}\n".format(
                        sta, chan[-1], pick.phase_hint, omega * polarity, y, x,
                        z))
        if len(ev_dict[ev_id]['phases']) > 0:
            if file_type == 'raw':
                ev_dict[ev_id]['header'] = "{} {!s}\n".format(
                    ev_id, len(ev_dict[ev_id]['phases']))
            elif file_type == 'vel1d':
                ex, ey, ez = dec_2_merc_meters(orig.longitude, orig.latitude,
                                               -1 * orig.depth)
                ev_dict[ev_id][
                    'header'] = "{} {!s} {!s} {!s} {!s} {!s}\n".format(
                        ev_id, len(ev_dict[ev_id]['phases']), ey, ex, ez, 2600)
    with open(outfile, 'w') as fo:
        for eid, dict in iteritems(ev_dict):
            if dict['header'] is not None:
                print('Writing event %s' % eid)
                fo.write(dict['header'])
                fo.writelines(dict['phases'])
    return