def test_dist_mat_km(self): """Test spacial clustering.""" from eqcorrscan.utils.clustering import dist_mat_km from obspy.clients.fdsn import Client from obspy import UTCDateTime client = Client("IRIS") starttime = UTCDateTime("2002-01-01") endtime = UTCDateTime("2002-01-02") cat = client.get_events(starttime=starttime, endtime=endtime, minmagnitude=6, catalog="ISC") dist_mat = dist_mat_km(cat) self.assertEqual(len(dist_mat), len(cat))
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)
def compute_differential_times(catalog, correlation, stream_dict=None, event_id_mapper=None, max_sep=8., min_link=8, min_cc=None, extract_len=None, pre_pick=None, shift_len=None, interpolate=False, max_workers=None, *args, **kwargs): """ Generate groups of differential times for a catalog. :type catalog: `obspy.core.event.Catalog` :param catalog: Catalog of events to get differential times for :type correlation: bool :param correlation: If True will generate cross-correlation derived differential-times for a dt.cc file. If false, will generate catalog times for a dt.ct file. :type stream_dict: dict :param stream_dict: Dictionary of streams keyed by event-id (the event.resource_id.id, NOT the hypoDD event-id) :type event_id_mapper: dict :param event_id_mapper: Dictionary mapping event resource id to an integer event id for hypoDD. If this is None, or missing events then the dictionary will be updated to include appropriate event-ids. This should be of the form {event.resource_id.id: integer_id} :type max_sep: float :param max_sep: Maximum hypocentral separation in km to link events :type min_link: int :param min_link: Minimum shared phase observations to link events :type min_cc: float :param min_cc: Threshold to include cross-correlation results. :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 in seconds. e.g. if shift_len is set to 1s, the pick will be allowed to shift between pick_time - 1 and pick_time + 1. :type interpolate: bool :param interpolate: Whether to interpolate correlations or not. Allows subsample accuracy :type max_workers: int :param max_workers: Maximum number of workers for parallel processing. If None then all threads will be used - only used if correlation = True :rtype: dict :return: Dictionary of differential times keyed by event id. :rtype: dict :return: Dictionary of event_id_mapper .. note:: The arguments min_cc, stream_dict, extract_len, pre_pick, shift_len and interpolate are only required if correlation=True. """ include_master = kwargs.get("include_master", False) correlation_kwargs = dict(min_cc=min_cc, stream_dict=stream_dict, extract_len=extract_len, pre_pick=pre_pick, shift_len=shift_len, interpolate=interpolate, max_workers=max_workers) if correlation: for arg, name in correlation_kwargs.items(): assert arg is not None, "{0} is required for correlation".format( name) # Ensure all events have locations and picks. event_id_mapper = _generate_event_id_mapper( catalog=catalog, event_id_mapper=event_id_mapper) distances = dist_mat_km(catalog) distance_filter = distances <= max_sep if not include_master: np.fill_diagonal(distance_filter, 0) # Do not match events to themselves - this is the default, # only included for testing additional_args = dict(min_link=min_link, event_id_mapper=event_id_mapper) if correlation: differential_times = {} additional_args.update(correlation_kwargs) n = len(catalog) for i, master in enumerate(catalog): master_id = master.resource_id.id sub_catalog = [ ev for j, ev in enumerate(catalog) if distance_filter[i][j] ] if master_id not in additional_args["stream_dict"].keys(): Logger.warning(f"{master_id} not in waveforms, skipping") continue differential_times.update({ master_id: _compute_dt_correlations(sub_catalog, master, **additional_args) }) Logger.info(f"Completed correlations for core event {i} of {n}") else: # Reformat catalog to sparse catalog sparse_catalog = [_make_sparse_event(ev) for ev in catalog] sub_catalogs = ([ ev for i, ev in enumerate(sparse_catalog) if master_filter[i] ] for master_filter in distance_filter) differential_times = { master.resource_id: _compute_dt(sub_catalog, master, **additional_args) for master, sub_catalog in zip(sparse_catalog, sub_catalogs) } # Remove Nones for key, value in differential_times.items(): differential_times.update({key: [v for v in value if v is not None]}) return differential_times, event_id_mapper
def test_dist_mat_km(self): """Test spacial clustering.""" dist_mat = dist_mat_km(self.cat) self.assertEqual(len(dist_mat), len(self.cat))
def decluster_distance_time(peaks, index, trig_int, catalog, hypocentral_separation, threshold=0): """ Decluster based on time between peaks, and distance between events. Peaks, index and catalog must all be sorted the same way, e.g. peak[i] corresponds to index[i] and catalog[i]. Peaks that are within the time threshold of one-another, but correspond to events separated by more than the hypocentral_separation threshold will not be removed. :type peaks: np.array :param peaks: array of peak values :type index: np.ndarray :param index: locations of peaks :type trig_int: int :param trig_int: Minimum trigger interval in samples :type catalog: obspy.core.event.Catalog :param catalog: Catalog of events with origins to use to measure inter-event distances :type hypocentral_separation: float :param hypocentral_separation: Maximum inter-event distance to decluster over in km :type threshold: float :param threshold: Minimum absolute peak value to retain it :return: list of tuples of (value, sample) """ utilslib = _load_cdll('libutils') length = peaks.shape[0] trig_int = int(trig_int) for var in [index.max(), trig_int]: if var == ctypes.c_long(var).value: long_type = ctypes.c_long func = utilslib.decluster_dist_time elif var == ctypes.c_longlong(var).value: long_type = ctypes.c_longlong func = utilslib.decluster_dist_time_ll else: raise OverflowError("Maximum index larger than internal long long") func.argtypes = [ np.ctypeslib.ndpointer(dtype=np.float32, shape=(length, ), flags=native_str('C_CONTIGUOUS')), np.ctypeslib.ndpointer(dtype=long_type, shape=(length, ), flags=native_str('C_CONTIGUOUS')), np.ctypeslib.ndpointer(dtype=np.float32, shape=(length * length, ), flags=native_str('C_CONTIGUOUS')), long_type, ctypes.c_float, long_type, ctypes.c_float, np.ctypeslib.ndpointer(dtype=np.uint32, shape=(length, ), flags=native_str('C_CONTIGUOUS')) ] func.restype = ctypes.c_int sorted_inds = np.abs(peaks).argsort() # Sort everything in the same way. arr = peaks[sorted_inds[::-1]] inds = index[sorted_inds[::-1]] sorted_events = [catalog[i] for i in sorted_inds[::-1]] distance_matrix = dist_mat_km(catalog=sorted_events) arr = np.ascontiguousarray(arr, dtype=np.float32) inds = np.ascontiguousarray(inds, dtype=long_type) distance_matrix = np.ascontiguousarray(distance_matrix.flatten(order="C"), dtype=np.float32) out = np.zeros(len(arr), dtype=np.uint32) ret = func(arr, inds, distance_matrix, long_type(length), np.float32(threshold), long_type(trig_int), hypocentral_separation, out) if ret != 0: raise MemoryError("Issue with c-routine, returned %i" % ret) peaks_out = list(zip(arr[out.astype(bool)], inds[out.astype(bool)])) return peaks_out