def test_get_vel(): data_files, origin = read_data_dir("geonet", "us1000778i", "*.V1A") data_files.sort() streams = [] for f in data_files: streams += read_data(f) sc = StreamCollection(streams) config = get_config() config["integration"]["frequency"] = True final_vel = [] for st in sc: for tr in st: tmp_tr = get_vel(tr, config=config) final_vel.append(tmp_tr.data[-1]) target_final_vel = np.array([ -2.182293e-03, -1.417545e-03, 2.111492e-03, -9.395322e-04, 1.662219e-03, -2.690978e-04, 1.376186e-04, -7.358185e-05, 1.741465e-05, ]) np.testing.assert_allclose(final_vel, target_final_vel, atol=1e-6)
def _get_person_agent(pr, config=None): '''Get the seis-prov entity for the user software. Args: pr (prov.model.ProvDocument): Existing ProvDocument. config (dict): Configuration options. Returns: prov.model.ProvDocument: Provenance document updated with gmprocess software name/version. ''' username = getpass.getuser() if config is None: config = get_config() fullname = '' email = '' if 'user' in config: if 'name' in config['user']: fullname = config['user']['name'] if 'email' in config['user']: email = config['user']['email'] hashstr = '0000001' person_id = "seis_prov:sp001_pp_%s" % hashstr pr.agent(person_id, other_attributes=((("prov:label", username), ("prov:type", prov.identifier.QualifiedName( prov.constants.PROV, "Person")), ("seis_prov:name", fullname), ("seis_prov:email", email)))) return pr
def read_data(filename, config=None, read_format=None, **kwargs): """ Read strong motion data from a file. Args: filename (str): Path to file read_format (str): Format of file Returns: list: Sequence of obspy.core.stream.Streams read from file """ _, file_ext = os.path.splitext(filename) if file_ext in EXCLUDED_EXTS: raise ValueError(f"Excluded extension: {filename}") # Check if file exists if not os.path.exists(filename): raise OSError(f"Not a file {filename!r}") # Get and validate format if config is None: config = get_config() if read_format is None: read_format = _get_format(filename, config) else: read_format = _validate_format(filename, config, read_format.lower()) # Load reader and read file reader = "gmprocess.io." + read_format + ".core" reader_module = importlib.import_module(reader) read_name = "read_" + read_format read_method = getattr(reader_module, read_name) streams = read_method(filename, config, **kwargs) return streams
def test_correct_baseline(): data_files, origin = read_data_dir("geonet", "us1000778i", "*.V1A") data_files.sort() streams = [] for f in data_files: streams += read_data(f) sc = StreamCollection(streams) final_acc = [] config = get_config() config["integration"]["frequency"] = True for st in sc: for tr in st: tmp_tr = correct_baseline(tr, config=config) final_acc.append(tmp_tr.data[-1]) target_final_acc = np.array([ 0.599829, 0.717284, -1.548017, 0.377616, -0.685688, 0.112147, 0.024594, 0.004697, -0.013296, ]) np.testing.assert_allclose(final_acc, target_final_acc, atol=1e-6)
def test_fit_spectra(): config = get_config() datapath = os.path.join("data", "testdata", "demo", "ci38457511", "raw") datadir = pkg_resources.resource_filename("gmprocess", datapath) event = get_event_object("ci38457511") sc = StreamCollection.from_directory(datadir) for st in sc: st = signal_split(st, event) end_conf = config["windows"]["signal_end"] st = signal_end(st, event_time=event.time, event_lon=event.longitude, event_lat=event.latitude, event_mag=event.magnitude, **end_conf) st = compute_snr(st, 30) st = get_corner_frequencies(st, event, method="constant", constant={ "highpass": 0.08, "lowpass": 20.0 }) for st in sc: spectrum.fit_spectra(st, event)
def _load_config(self): if not os.path.isfile(self.PROJECTS_FILE): # If projects.conf file doesn't exist then we need to run the # initial setup. print('No project config file detected.') print('Please select a project setup option:') print('(1) Initialize the current directory as a gmrecords') print(' project, which will contain data and conf') print(' subdirectories.') print('(2) Setup a project with data and conf locations that') print(' are independent of the current directory.') response = int(input('> ')) if response not in [1, 2]: print('Not a valid response. Exiting.') sys.exit(0) elif response == 1: InitModule().main(self) sys.exit(0) else: self._initial_setup() self.projects_conf = ConfigObj(self.PROJECTS_FILE, encoding='utf-8') self.project = self.projects_conf['project'] self.current_project = self.projects_conf['projects'][self.project] self.conf_path = self.current_project['conf_path'] self.data_path = self.current_project['data_path'] self.conf_file = os.path.join(self.conf_path, 'config.yml') if not os.path.isfile(self.conf_file): print('Config file does not exist: %s' % self.conf_file) print('Exiting.') sys.exit(1) self.conf = get_config(self.conf_file)
def test_fit_spectra(): config = get_config() datapath = os.path.join('data', 'testdata', 'demo', 'ci38457511', 'raw') datadir = pkg_resources.resource_filename('gmprocess', datapath) event = get_event_object('ci38457511') sc = StreamCollection.from_directory(datadir) for st in sc: st = signal_split(st, event) end_conf = config['windows']['signal_end'] st = signal_end(st, event_time=event.time, event_lon=event.longitude, event_lat=event.latitude, event_mag=event.magnitude, **end_conf) st = compute_snr(st, 30) st = get_corner_frequencies(st, method='constant', constant={ 'highpass': 0.08, 'lowpass': 20.0 }) for st in sc: spectrum.fit_spectra(st, event)
def test_zero_crossings(): datapath = os.path.join("data", "testdata", "zero_crossings") datadir = pkg_resources.resource_filename("gmprocess", datapath) sc = StreamCollection.from_directory(datadir) sc.describe() conf = get_config() update = { "processing": [ {"detrend": {"detrending_method": "demean"}}, {"check_zero_crossings": {"min_crossings": 1}}, ] } update_dict(conf, update) edict = { "id": "ak20419010", "time": UTCDateTime("2018-11-30T17:29:29"), "lat": 61.346, "lon": -149.955, "depth": 46.7, "magnitude": 7.1, } event = get_event_object(edict) test = process_streams(sc, event, conf) for st in test: for tr in st: assert tr.hasParameter("ZeroCrossingRate") np.testing.assert_allclose( test[0][0].getParameter("ZeroCrossingRate")["crossing_rate"], 0.008888888888888889, atol=1e-5, )
def test_integrate_taper(): data_files, origin = read_data_dir("geonet", "us1000778i", "*.V1A") data_files.sort() streams = [] for f in data_files: streams += read_data(f) sc = StreamCollection(streams) config = get_config() config["integration"]["taper"]["taper"] = True final_vel = [] for st in sc: for tr in st: tmp_tr = tr.integrate(config=config) final_vel.append(tmp_tr.data[-1]) target_final_vel = np.array([ 3.896186e00, -4.901823e00, -5.722080e-01, 1.621672e-01, -1.654317e-01, -8.242356e-04, -1.482590e-02, 1.504334e-01, 1.021050e-01, ]) np.testing.assert_allclose(final_vel, target_final_vel, atol=1e-6)
def is_obspy(filename, config=None): """Check to see if file is a format supported by Obspy (not KNET). Note: Currently only SAC and Miniseed are supported. Args: filename (str): Path to possible Obspy format. config (dict): Dictionary containing configuration. Returns: bool: True if obspy supported, otherwise False. """ logging.debug("Checking if format is supported by obspy.") metadir = config["read"]["metadata_directory"] if config is None: config = get_config() if not os.path.isfile(filename): return False try: stream = read(filename) if stream[0].stats._format in IGNORE_FORMATS: return False if stream[0].stats._format in REQUIRES_XML: xmlfile = _get_station_file(filename, stream, metadir) if not os.path.isfile(xmlfile): return False return True else: return True except BaseException: return False return False
def test_all_pickers(): streams = get_streams() picker_config = get_config(section='pickers') methods = ['ar', 'baer', 'power', 'kalkan'] columns = ['Stream', 'Method', 'Pick_Time', 'Mean_SNR'] df = pd.DataFrame(columns=columns) for stream in streams: print(stream.get_id()) for method in methods: try: if method == 'ar': loc, mean_snr = pick_ar(stream, picker_config=picker_config) elif method == 'baer': loc, mean_snr = pick_baer(stream, picker_config=picker_config) elif method == 'power': loc, mean_snr = pick_power(stream, picker_config=picker_config) elif method == 'kalkan': loc, mean_snr = pick_kalkan(stream, picker_config=picker_config) elif method == 'yeck': loc, mean_snr = pick_yeck(stream) except BaseException: loc = -1 mean_snr = np.nan row = { 'Stream': stream.get_id(), 'Method': method, 'Pick_Time': loc, 'Mean_SNR': mean_snr } df = df.append(row, ignore_index=True) stations = df['Stream'].unique() cmpdict = { 'TW.ECU.BN': 'kalkan', 'TW.ELD.BN': 'power', 'TW.EGF.BN': 'ar', 'TW.EAS.BN': 'ar', 'TW.EDH.BN': 'ar', 'TK.4304.HN': 'ar', 'TK.0921.HN': 'ar', 'TK.5405.HN': 'ar', 'NZ.HSES.HN': 'baer', 'NZ.WTMC.HN': 'baer', 'NZ.THZ.HN': 'power' } for station in stations: station_df = df[df['Stream'] == station] max_snr = station_df['Mean_SNR'].max() maxrow = station_df[station_df['Mean_SNR'] == max_snr].iloc[0] method = maxrow['Method'] try: assert cmpdict[station] == method except Exception as e: pass
def test_all_pickers(): streams = get_streams() picker_config = get_config(section="pickers") methods = ["ar", "baer", "power", "kalkan"] columns = ["Stream", "Method", "Pick_Time", "Mean_SNR"] df = pd.DataFrame(columns=columns) for stream in streams: print(stream.get_id()) for method in methods: try: if method == "ar": loc, mean_snr = pick_ar(stream, picker_config=picker_config) elif method == "baer": loc, mean_snr = pick_baer(stream, picker_config=picker_config) elif method == "power": loc, mean_snr = pick_power(stream, picker_config=picker_config) elif method == "kalkan": loc, mean_snr = pick_kalkan(stream, picker_config=picker_config) elif method == "yeck": loc, mean_snr = pick_yeck(stream) except BaseException: loc = -1 mean_snr = np.nan row = { "Stream": stream.get_id(), "Method": method, "Pick_Time": loc, "Mean_SNR": mean_snr, } df = df.append(row, ignore_index=True) stations = df["Stream"].unique() cmpdict = { "TW.ECU.BN": "kalkan", "TW.ELD.BN": "power", "TW.EGF.BN": "ar", "TW.EAS.BN": "ar", "TW.EDH.BN": "ar", "TK.4304.HN": "ar", "TK.0921.HN": "ar", "TK.5405.HN": "ar", "NZ.HSES.HN": "baer", "NZ.WTMC.HN": "baer", "NZ.THZ.HN": "power", } for station in stations: station_df = df[df["Stream"] == station] max_snr = station_df["Mean_SNR"].max() maxrow = station_df[station_df["Mean_SNR"] == max_snr].iloc[0] method = maxrow["Method"] try: assert cmpdict[station] == method except Exception as e: pass
def pick_ar(stream, picker_config=None, config=None): """Wrapper around the AR P-phase picker. Args: stream (StationStream): Stream containing waveforms that need to be picked. picker_config (dict): Dictionary with parameters for AR P-phase picker. See picker.yml. config (dict): Configuration dictionary. Key value here is: windows: window_checks: min_noise_duration Returns: tuple: - Best estimate for p-wave arrival time (s since start of trace). - Mean signal to noise ratio based on the pick. """ if picker_config is None: picker_config = get_config(section='pickers') if config is None: config = get_config() min_noise_dur = config['windows']['window_checks']['min_noise_duration'] params = picker_config['ar'] # Get the east, north, and vertical components from the stream st_e = stream.select(channel='??[E1]') st_n = stream.select(channel='??[N2]') st_z = stream.select(channel='??[Z3]') # Check if we found one of each component # If not, use the next picker in the order of preference if len(st_e) != 1 or len(st_n) != 1 or len(st_z) != 1: raise BaseException('Unable to perform AR picker.') minloc = ar_pick(st_z[0].data, st_n[0].data, st_e[0].data, st_z[0].stats.sampling_rate, **params)[0] if minloc < min_noise_dur: fmt = 'Noise window (%.1f s) less than minimum (%.1f)' tpl = (minloc, min_noise_dur) raise ValueError(fmt % tpl) mean_snr = calc_snr(stream, minloc) return (minloc, mean_snr)
def test_get_disp(): data_files, origin = read_data_dir("geonet", "us1000778i", "*.V1A") data_files.sort() streams = [] for f in data_files: streams += read_data(f) sc = StreamCollection(streams) config = get_config() config["integration"]["frequency"] = True final_disp = [] for st in sc: for tr in st: tmp_tr = get_disp(tr, config=config) final_disp.append(tmp_tr.data[-1]) target_final_disp = np.array([ -0.07689, 0.082552, -0.024509, -0.00047, -0.000257, -0.000152, -0.003425, 0.000671, 0.000178, ]) np.testing.assert_allclose(final_disp, target_final_disp, atol=1e-6) config["integration"]["frequency"] = False config["integration"]["initial"] = 0.0 config["integration"]["demean"] = True final_disp = [] for st in sc: for tr in st: tmp_tr = get_disp(tr, config=config) final_disp.append(tmp_tr.data[-1]) target_final_disp = np.array([ -0.076882, 0.082549, -0.024512, -0.000469, -0.000259, -0.000152, -0.003425, 0.000672, 0.000178, ]) np.testing.assert_allclose(final_disp, target_final_disp, atol=1e-6)
def pick_baer(stream, picker_config=None, config=None): """Wrapper around the Baer P-phase picker. Args: stream (StationStream): Stream containing waveforms that need to be picked. picker_config (dict): Dictionary with parameters for Baer P-phase picker. See picker.yml. config (dict): Configuration dictionary. Key value here is: windows: window_checks: min_noise_duration Returns: tuple: - Best estimate for p-wave arrival time (s since start of trace). - Mean signal to noise ratio based on the pick. """ if picker_config is None: picker_config = get_config(section='pickers') if config is None: config = get_config() min_noise_dur = config['windows']['window_checks']['min_noise_duration'] params = picker_config['baer'] locs = [] for trace in stream: pick_sample = pk_baer(trace.data, trace.stats.sampling_rate, **params)[0] loc = pick_sample * trace.stats.delta locs.append(loc) locs = np.array(locs) if np.any(locs >= 0): minloc = np.min(locs[locs >= 0]) else: minloc = -1 if minloc < min_noise_dur: fmt = 'Noise window (%.1f s) less than minimum (%.1f)' tpl = (minloc, min_noise_dur) raise ValueError(fmt % tpl) mean_snr = calc_snr(stream, minloc) return (minloc, mean_snr)
def check_instrument(st, n_max=3, n_min=1, require_two_horiz=False, config=None): """ Test the channels of the station. The purpose of the maximum limit is to skip over stations with muliple strong motion instruments, which can occur with downhole or structural arrays since our code currently is not able to reliably group by location within an array. The purpose of the minimum and require_two_horiz checks are to ensure the channels are required for subsequent intensity measures such as ROTD. Args: st (StationStream): Stream of data. n_max (int): Maximum allowed number of streams; default to 3. n_min (int): Minimum allowed number of streams; default to 1. require_two_horiz (bool): Require two horizontal components; default to `False`. config (dict): Configuration dictionary (or None). See get_config(). Returns: Stream with adjusted failed fields. """ if not st.passed: return st if config is None: config = get_config() logging.debug("Starting check_instrument") logging.debug(f"len(st) = {len(st)}") for failed_test, message in [ (len(st) > n_max, f"More than {n_max} traces in stream."), (len(st) < n_min, f"Less than {n_min} traces in stream."), ( require_two_horiz and (st.num_horizontal != 2), "Not two horizontal components", ), ]: if failed_test: for tr in st: tr.fail(message) # Stop at first failed test break return st
def __init__( self, time, lat, lon, depth, magnitude, config=None, rawdir=None, drop_non_free=True, stream_collection=True, ): """Create an FDSNFetcher instance. Download waveform data from the all available FDSN sites using the Obspy mass downloader functionality. Args: time (datetime): Origin time. lat (float): Origin latitude. lon (float): Origin longitude. depth (float): Origin depth. magnitude (float): Origin magnitude. config (dict): Dictionary containing configuration. If None, retrieve global config. rawdir (str): Path to location where raw data will be stored. If not specified, raw data will be deleted. drop_non_free (bool): Option to ignore non-free-field (borehole, sensors on structures, etc.) stream_collection (bool): Construct and return a StreamCollection instance? """ if config is None: config = get_config() tz = pytz.UTC if isinstance(time, UTCDateTime): time = time.datetime self.time = tz.localize(time) self.lat = lat self.lon = lon self.depth = depth self.magnitude = magnitude self.config = config self.rawdir = rawdir self.drop_non_free = drop_non_free self.stream_collection = stream_collection
def pick_kalkan(stream, picker_config=None, config=None): """Wrapper around the Kalkan P-phase picker. Args: stream (StationStream): Stream containing waveforms that need to be picked. picker_config (dict): Dictionary with parameters for Kalkan P-phase picker. See picker.yml. config (dict): Configuration dictionary. Key value here is: windows: window_checks: min_noise_duration Returns: tuple: - Best estimate for p-wave arrival time (s since start of trace). - Mean signal to noise ratio based on the pick. """ if picker_config is None: picker_config = get_config(section="pickers") if config is None: config = get_config() min_noise_dur = config["windows"]["window_checks"]["min_noise_duration"] params = picker_config["kalkan"] locs = [] for trace in stream: loc = pphase_pick(trace, **params) if loc >= 0: locs.append(loc) locs = np.array(locs) if np.any(locs >= 0): minloc = np.min(locs[locs >= 0]) else: minloc = -1 if minloc < min_noise_dur: fmt = "Noise window (%.1f s) less than minimum (%.1f)" tpl = (minloc, min_noise_dur) raise ValueError(fmt % tpl) mean_snr = calc_snr(stream, minloc) return (minloc, mean_snr)
def pick_travel(stream, origin, model=None, picker_config=None): '''Use TauP travel time model to find P-Phase arrival time. Args: stream (StationStream): StationStream containing 1 or more channels of waveforms. origin (ScalarEvent): Event origin/magnitude information. model (TauPyModel): TauPyModel object for computing travel times. Returns: tuple: - Best estimate for p-wave arrival time (s since start of trace). - Mean signal to noise ratio based on the pick. ''' if model is None: if picker_config is None: picker_config = get_config(section='pickers') model = TauPyModel(picker_config['travel_time']['model']) if stream[0].stats.starttime == NAN_TIME: return (-1, 0) lat = origin.latitude lon = origin.longitude depth = origin.depth_km if depth < 0: depth = 0 etime = origin.time slat = stream[0].stats.coordinates.latitude slon = stream[0].stats.coordinates.longitude dist_deg = locations2degrees(lat, lon, slat, slon) try: arrivals = model.get_travel_times( source_depth_in_km=depth, distance_in_degree=dist_deg, phase_list=['P', 'p', 'Pn']) except BaseException as e: fmt = ('Exception "%s" generated by get_travel_times() ' 'dist=%.3f depth=%.1f') logging.warning(fmt % (str(e), dist_deg, depth)) arrivals = [] if not len(arrivals): return (-1, 0) # arrival time is time since origin arrival = arrivals[0] # we need time since start of the record minloc = arrival.time + (etime - stream[0].stats.starttime) mean_snr = calc_snr(stream, minloc) return (minloc, mean_snr)
def test_nnet(): conf = get_config() update = { "processing": [ { "detrend": { "detrending_method": "demean" } }, { "detrend": { "detrending_method": "linear" } }, { "compute_snr": { "bandwidth": 20.0, "check": { "max_freq": 5.0, "min_freq": 0.2, "threshold": 3.0 }, } }, { "NNet_QA": { "acceptance_threshold": 0.5, "model_name": "CantWell" } }, ] } update_dict(conf, update) data_files, origin = read_data_dir("geonet", "us1000778i", "*.V1A") streams = [] for f in data_files: streams += read_data(f) sc = StreamCollection(streams) test = process_streams(sc, origin, conf) tstream = test.select(station="HSES")[0] nnet_dict = tstream.getStreamParam("nnet_qa") np.testing.assert_allclose(nnet_dict["score_HQ"], 0.99321798811740059, rtol=1e-3)
def test_read(): config = get_config() cosmos_files, _ = read_data_dir("cosmos", "ci14155260", "Cosmos12TimeSeriesTest.v1") cwb_files, _ = read_data_dir("cwb", "us1000chhc", "1-EAS.dat") dmg_files, _ = read_data_dir("dmg", "nc71734741", "CE89146.V2") geonet_files, _ = read_data_dir( "geonet", "us1000778i", "20161113_110259_WTMC_20.V1A" ) knet_files, _ = read_data_dir("knet", "us2000cnnl", "AOM0011801241951.EW") smc_files, _ = read_data_dir("smc", "nc216859", "0111a.smc") file_dict = {} file_dict["cosmos"] = cosmos_files[0] file_dict["cwb"] = cwb_files[0] file_dict["dmg"] = dmg_files[0] file_dict["geonet"] = geonet_files[0] file_dict["knet"] = knet_files[0] file_dict["smc"] = smc_files[0] for file_format in file_dict: file_path = file_dict[file_format] assert _get_format(file_path, config) == file_format assert _validate_format(file_path, config, file_format) == file_format assert _validate_format(file_dict["knet"], config, "smc") == "knet" assert _validate_format(file_dict["dmg"], config, "cosmos") == "dmg" assert _validate_format(file_dict["cosmos"], config, "invalid") == "cosmos" for file_format in file_dict: try: stream = read_data(file_dict[file_format], config, file_format)[0] except Exception as e: pass assert stream[0].stats.standard["source_format"] == file_format stream = read_data(file_dict[file_format])[0] assert stream[0].stats.standard["source_format"] == file_format # test exception try: file_path = smc_files[0].replace("0111a.smc", "not_a_file.smc") read_data(file_path)[0] success = True except BaseException: success = False assert success == False
def test_nnet(): conf = get_config() update = { 'processing': [{ 'detrend': { 'detrending_method': 'demean' } }, { 'detrend': { 'detrending_method': 'linear' } }, { 'compute_snr': { 'bandwidth': 20.0, 'check': { 'max_freq': 5.0, 'min_freq': 0.2, 'threshold': 3.0 } } }, { 'NNet_QA': { 'acceptance_threshold': 0.5, 'model_name': 'CantWell' } }] } update_dict(conf, update) data_files, origin = read_data_dir('geonet', 'us1000778i', '*.V1A') streams = [] for f in data_files: streams += read_data(f) sc = StreamCollection(streams) test = process_streams(sc, origin, conf) tstream = test.select(station='HSES')[0] nnet_dict = tstream.getStreamParam('nnet_qa') np.testing.assert_allclose(nnet_dict['score_HQ'], 0.99321798811740059, rtol=1e-3)
def test_auto_fchp(): data_files, origin = read_data_dir("geonet", "us1000778i", "*.V1A") data_files.sort() streams = [] for f in data_files: streams += read_data(f) sc = StreamCollection(streams) output_fchp = [] config = get_config() config["integration"]["frequency"] = True for st in sc: for tr in st: tr.setParameter( "corner_frequencies", { "type": "constant", "highpass": 0.001, "lowpass": 20 }, ) tmp_st = ridder_fchp(st, config=config) for tr in tmp_st: initial_corners = tr.getParameter("corner_frequencies") output_fchp.append(initial_corners["highpass"]) target_fchp = np.array([ 0.021345158261480087, 0.022839239726168643, 0.02482398434993213, 0.01399481102242619, 0.026850167635921275, 0.004817661513765862, 0.008204101694236587, 0.006429246474225982, 0.004237087327289796, ]) np.testing.assert_allclose(output_fchp, target_fchp, atol=1e-7)
def __disp_checks( tr, max_final_displacement=0.025, max_displacment_ratio=0.2, config=None ): # Need to find the high/low pass filtering steps in the config # to ensure that filtering here is done with the same options if config is None: config = get_config() processing_steps = config["processing"] ps_names = [list(ps.keys())[0] for ps in processing_steps] ind = int(np.where(np.array(ps_names) == "highpass_filter")[0][0]) hp_args = processing_steps[ind]["highpass_filter"] ind = int(np.where(np.array(ps_names) == "lowpass_filter")[0][0]) lp_args = processing_steps[ind]["lowpass_filter"] # Make a copy of the trace so we don't modify it in place with # filtering or integration trdis = tr.copy() # Filter trdis = lowpass_filter_trace(trdis, **lp_args) trdis = highpass_filter_trace(trdis, **hp_args) # Apply baseline correction trdis = correct_baseline(trdis, config) # Integrate to displacment trdis = get_disp(trdis, config=config) # Checks ok = True max_displacment = np.max(np.abs(trdis.data)) final_displacement = np.abs(trdis.data[-1]) disp_ratio = final_displacement / max_displacment if final_displacement > max_final_displacement: ok = False if disp_ratio > max_displacment_ratio: ok = False return ok
def pick_yeck(stream): """IN DEVELOPMENT! SNR based P-phase picker. Args: stream (StationStream): Stream containing waveforms that need to be picked. Returns: tuple: - Best estimate for p-wave arrival time (s since start of trace). - Mean signal to noise ratio based on the pick. """ min_window = 5.0 # put into config config = get_config() min_noise_dur = config['windows']['window_checks']['min_noise_duration'] locs = [] for trace in stream: data = trace.data sr = trace.stats.sampling_rate pidx_start = int(min_window * sr) snr = np.zeros(len(data)) for pidx in range(pidx_start, len(data) - pidx_start): snr_i = sub_calc_snr(data, pidx) snr[pidx] = snr_i snr = np.array(snr) pidx = snr.argmax() loc = pidx / sr locs.append(loc) locs = np.array(locs) if np.any(locs >= 0): minloc = np.min(locs[locs >= 0]) else: minloc = -1 if minloc < min_noise_dur: fmt = 'Noise window (%.1f s) less than minimum (%.1f)' tpl = (minloc, min_noise_dur) raise ValueError(fmt % tpl) mean_snr = calc_snr(stream, minloc) return (minloc, mean_snr)
def correct_baseline(trace, config=None): """ Performs a baseline correction following the method of Ancheta et al. (2013). This removes low-frequency, non-physical trends that remain in the time series following filtering. Args: trace (obspy.core.trace.Trace): Trace of strong motion data. config (dict): Configuration dictionary (or None). See get_config(). Returns: trace: Baseline-corrected trace. """ if config is None: config = get_config() # Integrate twice to get the displacement time series disp = get_disp(trace, config=config) # Fit a sixth order polynomial to displacement time series, requiring # that the 1st and 0th order coefficients are zero time_values = ( np.linspace(0, trace.stats.npts - 1, trace.stats.npts) * trace.stats.delta ) poly_cofs = list(curve_fit(_poly_func, time_values, disp.data)[0]) poly_cofs += [0, 0] # Construct a polynomial from the coefficients and compute # the second derivative polynomial = np.poly1d(poly_cofs) polynomial_second_derivative = np.polyder(polynomial, 2) # Subtract the second derivative of the polynomial from the # acceleration trace trace.data -= polynomial_second_derivative(time_values) trace.setParameter("baseline", {"polynomial_coefs": poly_cofs}) return trace
def from_directory(cls, directory, use_default_config=False): """Create a StreamCollection instance from a directory of data. Args: directory (str): Directory of ground motion files (streams) to be read. use_default_config (bool): Use default ("production") config. Returns: StreamCollection instance. """ if use_default_config: config = get_config(use_default=True) else: config = None streams, missed_files, errors = directory_to_streams(directory, config=config) # Might eventually want to include some of the missed files and # error info but don't have a sensible place to put it currently. return cls(streams, config=config)
def test_zero_crossings(): datapath = os.path.join('data', 'testdata', 'zero_crossings') datadir = pkg_resources.resource_filename('gmprocess', datapath) sc = StreamCollection.from_directory(datadir) sc.describe() conf = get_config() update = { 'processing': [{ 'detrend': { 'detrending_method': 'demean' } }, { 'check_zero_crossings': { 'min_crossings': 1 } }] } update_dict(conf, update) edict = { 'id': 'ak20419010', 'time': UTCDateTime('2018-11-30T17:29:29'), 'lat': 61.346, 'lon': -149.955, 'depth': 46.7, 'magnitude': 7.1 } event = get_event_object(edict) test = process_streams(sc, event, conf) for st in test: for tr in st: assert tr.hasParameter('ZeroCrossingRate') np.testing.assert_allclose( test[0][0].getParameter('ZeroCrossingRate')['crossing_rate'], 0.008888888888888889, atol=1e-5)
def _get_person_agent(pr, config=None): """Get the seis-prov entity for the user software. Args: pr (prov.model.ProvDocument): Existing ProvDocument. config (dict): Configuration options. Returns: prov.model.ProvDocument: Provenance document updated with gmprocess software name/version. """ username = getpass.getuser() if config is None: config = get_config() fullname = "" email = "" if "user" in config: if "name" in config["user"]: fullname = config["user"]["name"] if "email" in config["user"]: email = config["user"]["email"] hashstr = "0000001" person_id = f"seis_prov:sp001_pp_{hashstr}" pr.agent( person_id, other_attributes=(( ("prov:label", username), ( "prov:type", prov.identifier.QualifiedName(prov.constants.PROV, "Person"), ), ("seis_prov:name", fullname), ("seis_prov:email", email), )), ) return pr
def update_config(custom_cfg_file): """Merge custom config with default. Args: custom_cfg_file (str): Path to custom config. Returns: dict: Merged config dictionary. """ config = get_config() if not os.path.isfile(custom_cfg_file): return config try: with open(custom_cfg_file, 'rt', encoding='utf-8') as f: custom_cfg = yaml.load(f, Loader=yaml.FullLoader) update_dict(config, custom_cfg) except yaml.parser.ParserError: return None return config