def read_bhrc(filename): """Read the Iran BHRC strong motion data format. Args: filename (str): path to BHRC data file. Returns: list: Sequence of one StationStream object containing 3 StationTrace objects. """ header1, offset = _read_header_lines(filename, 0) data1, offset = _read_data(filename, offset, header1) header2, offset = _read_header_lines(filename, offset) data2, offset = _read_data(filename, offset, header2) header3, offset = _read_header_lines(filename, offset) data3, offset = _read_data(filename, offset, header3) trace1 = StationTrace(data1, header1) trace2 = StationTrace(data2, header2) trace3 = StationTrace(data3, header3) stream = StationStream([trace1, trace2, trace3]) for tr in stream: if tr.stats.standard.process_level != PROCESS_LEVELS['V0']: response = {'input_units': 'counts', 'output_units': 'cm/s^2'} tr.setProvenance('remove_response', response) return [stream]
def read_bhrc(filename, config=None, **kwargs): """Read the Iran BHRC strong motion data format. Args: filename (str): Path to BHRC data file. config (dict): Dictionary containing configuration. kwargs (ref): Other arguments will be ignored. Returns: list: Sequence of one StationStream object containing 3 StationTrace objects. """ header1, offset = _read_header_lines(filename, 0) data1, offset = _read_data(filename, offset, header1) header2, offset = _read_header_lines(filename, offset) data2, offset = _read_data(filename, offset, header2) header3, offset = _read_header_lines(filename, offset) data3, offset = _read_data(filename, offset, header3) trace1 = StationTrace(data1, header1) trace2 = StationTrace(data2, header2) trace3 = StationTrace(data3, header3) stream = StationStream([trace1, trace2, trace3]) for tr in stream: if tr.stats.standard.process_level != PROCESS_LEVELS["V0"]: response = {"input_units": "counts", "output_units": "cm/s^2"} tr.setProvenance("remove_response", response) return [stream]
def get_arias(self): """ Performs calculation of arias intensity. Returns: arias_intensities: Dictionary of arias intensity for each channel. """ arias_intensities = {} arias_stream = StationStream([]) for trace in self.reduction_data: dt = trace.stats["delta"] # convert from cm/s/s to m/s/s acc = trace.data * 0.01 # Calculate Arias Intensity integrated_acc2 = integrate.cumtrapz(acc * acc, dx=dt) arias_intensity = integrated_acc2 * np.pi * GAL_TO_PCTG / 2 # Create a copy of stats so we don't modify original data stats = trace.stats.copy() channel = stats.channel stats.standard.units_type = "vel" stats.npts = len(arias_intensity) arias_stream.append(StationTrace(arias_intensity, stats)) arias_intensities[channel] = np.abs(np.max(arias_intensity)) self.arias_stream = arias_stream return arias_intensities
def read_unam(filename, config=None, **kwargs): """Read the Mexican UNAM strong motion data format. Args: filename (str): Path to UNAM data file. config (dict): Dictionary containing configuration. kwargs (ref): Other arguments will be ignored. Returns: list: Sequence of one StationStream object containing 3 StationTrace objects. """ channels = _read_header(filename) npts = channels[0]["npts"] all_data = np.genfromtxt(filename, skip_header=ALL_HEADERS, max_rows=npts) trace1 = StationTrace(data=all_data[:, 0], header=channels[0]) trace2 = StationTrace(data=all_data[:, 1], header=channels[1]) trace3 = StationTrace(data=all_data[:, 2], header=channels[2]) # tell the trace that data has already been converted to physical units response = {"input_units": "counts", "output_units": "cm/s^2"} trace1.setProvenance("remove_response", response) trace2.setProvenance("remove_response", response) trace3.setProvenance("remove_response", response) stream = StationStream(traces=[trace1, trace2, trace3]) return [stream]
def _read_volume_one(filename, line_offset, location='', units='acc'): """Read channel data from DMG Volume 1 text file. Args: filename (str): Input DMG V1 filename. line_offset (int): Line offset to beginning of channel text block. units (str): units to get. Returns: tuple: (list of obspy Trace, int line offset) """ # Parse the header portion of the file try: with open(filename, 'rt', encoding='utf-8') as f: for _ in range(line_offset): next(f) lines = [next(f) for x in range(V1_TEXT_HDR_ROWS)] # Accounts for blank lines at end of files except StopIteration: return (None, 1 + line_offset) unit = _get_units(lines[11]) # read in lines of integer data skip_rows = V1_TEXT_HDR_ROWS + line_offset int_data = _read_lines(skip_rows, V1_INT_HDR_ROWS, V2_INT_FMT, filename) int_data = int_data[0:100].astype(np.int32) # read in lines of float data skip_rows += V1_INT_HDR_ROWS flt_data = _read_lines(skip_rows, V1_REAL_HDR_ROWS, V2_REAL_FMT, filename) skip_rows += V1_REAL_HDR_ROWS # according to the powers that defined the Network.Station.Channel.Location # "standard", Location is a two character field. Most data providers, # including csmip/dmg here, don't always provide this. We'll flag it as # "--". hdr = _get_header_info_v1( int_data, flt_data, lines, 'V1', location=location) head, tail = os.path.split(filename) hdr['standard']['source_file'] = tail or os.path.basename(head) # sometimes (??) a line of text is inserted in between the float header and # the beginning of the data. Let's check for this... with open(filename, 'rt', encoding='utf-8') as f: for _ in range(skip_rows): next(f) test_line = f.readline() has_text = re.search('[A-Z]+|[a-z]+', test_line) is not None if has_text: skip_rows += 1 widths = [9] * 8 max_rows = int(np.ceil(hdr['npts'] / 8)) data = _read_lines(skip_rows, max_rows, widths, filename) acc_data = data[:hdr['npts']] evenly_spaced = True # Sometimes, npts is incrrectly specified, leading to nans # in the resulting data. Fix that here if np.any(np.isnan(acc_data)): while np.isnan(acc_data[-1]): acc_data = acc_data[:-1] hdr['npts'] = len(acc_data) else: # acceleration data is interleaved between time data max_rows = int(np.ceil(hdr['npts'] / 5)) widths = [7] * 10 data = _read_lines(skip_rows, max_rows, widths, filename) acc_data = data[1::2][:hdr['npts']] times = data[0::2][:hdr['npts']] evenly_spaced = is_evenly_spaced(times) if unit in UNIT_CONVERSIONS: acc_data *= UNIT_CONVERSIONS[unit] logging.debug('Data converted from %s to cm/s/s' % (unit)) else: raise ValueError('DMG: %s is not a supported unit.' % unit) acc_trace = StationTrace(acc_data.copy(), Stats(hdr.copy())) # Check if the times were included in the file but were not evenly spaced if not evenly_spaced: acc_trace = resample_uneven_trace(acc_trace, times, acc_data) response = {'input_units': 'counts', 'output_units': 'cm/s^2'} acc_trace.setProvenance('remove_response', response) traces = [acc_trace] new_offset = skip_rows + max_rows + 1 # there is an end of record line return (traces, new_offset)
def test_fas(): """ Testing based upon the work provided in https://github.com/arkottke/notebooks/blob/master/effective_amp_spectrum.ipynb """ ddir = os.path.join("data", "testdata") datadir = pkg_resources.resource_filename("gmprocess", ddir) fas_file = os.path.join(datadir, "fas_geometric_mean.pkl") p1 = os.path.join(datadir, "peer", "RSN763_LOMAP_GIL067.AT2") p2 = os.path.join(datadir, "peer", "RSN763_LOMAP_GIL337.AT2") stream = StationStream([]) for idx, fpath in enumerate([p1, p2]): with open(fpath, encoding="utf-8") as file_obj: for _ in range(3): next(file_obj) meta = re.findall(r"[.0-9]+", next(file_obj)) dt = float(meta[1]) accels = np.array( [col for line in file_obj for col in line.split()], dtype=float) trace = StationTrace( data=accels, header={ "channel": "H" + str(idx), "delta": dt, "units": "acc", "standard": { "corner_frequency": np.nan, "station_name": "", "source": "json", "instrument": "", "instrument_period": np.nan, "source_format": "json", "comments": "", "structure_type": "", "sensor_serial_number": "", "source_file": "", "process_level": "raw counts", "process_time": "", "horizontal_orientation": np.nan, "vertical_orientation": np.nan, "units": "acc", "units_type": "acc", "instrument_sensitivity": np.nan, "instrument_damping": np.nan, }, }, ) stream.append(trace) for tr in stream: response = {"input_units": "counts", "output_units": "cm/s^2"} tr.setProvenance("remove_response", response) target_df = pd.read_pickle(fas_file) ind_vals = target_df.index.values per = np.unique( [float(i[0].split(")")[0].split("(")[1]) for i in ind_vals]) freqs = 1 / per imts = ["fas" + str(p) for p in per] summary = StationSummary.from_stream(stream, ["geometric_mean"], imts, bandwidth=30) pgms = summary.pgms # pgms.to_pickle(fas_file) for idx, f in enumerate(freqs): fstr = f"FAS({1 / f:.3f})" fval1 = pgms.loc[fstr, "GEOMETRIC_MEAN"].Result fval2 = target_df.loc[fstr, "GEOMETRIC_MEAN"].Result np.testing.assert_allclose(fval1, fval2, rtol=1e-5, atol=1e-5)
def test_fas(): """ Testing based upon the work provided in https://github.com/arkottke/notebooks/blob/master/effective_amp_spectrum.ipynb """ ddir = os.path.join('data', 'testdata') datadir = pkg_resources.resource_filename('gmprocess', ddir) fas_file = os.path.join(datadir, 'fas_arithmetic_mean.pkl') p1 = os.path.join(datadir, 'peer', 'RSN763_LOMAP_GIL067.AT2') p2 = os.path.join(datadir, 'peer', 'RSN763_LOMAP_GIL337.AT2') stream = StationStream([]) for idx, fpath in enumerate([p1, p2]): with open(fpath, encoding='utf-8') as file_obj: for _ in range(3): next(file_obj) meta = re.findall(r'[.0-9]+', next(file_obj)) dt = float(meta[1]) accels = np.array( [col for line in file_obj for col in line.split()]) trace = StationTrace(data=accels, header={ 'channel': 'H' + str(idx), 'delta': dt, 'units': 'acc', 'standard': { 'corner_frequency': np.nan, 'station_name': '', 'source': 'json', 'instrument': '', 'instrument_period': np.nan, 'source_format': 'json', 'comments': '', 'structure_type': '', 'sensor_serial_number': '', 'source_file': '', 'process_level': 'raw counts', 'process_time': '', 'horizontal_orientation': np.nan, 'vertical_orientation': np.nan, 'units': 'acc', 'units_type': 'acc', 'instrument_sensitivity': np.nan, 'instrument_damping': np.nan } }) stream.append(trace) for tr in stream: response = {'input_units': 'counts', 'output_units': 'cm/s^2'} tr.setProvenance('remove_response', response) target_df = pd.read_pickle(fas_file) ind_vals = target_df.index.values per = np.unique( [float(i[0].split(')')[0].split('(')[1]) for i in ind_vals]) freqs = 1 / per imts = ['fas' + str(p) for p in per] summary = StationSummary.from_stream(stream, ['arithmetic_mean'], imts, bandwidth=30) pgms = summary.pgms # pgms.to_pickle(fas_file) for idx, f in enumerate(freqs): fstr = 'FAS(%.3f)' % (1 / f) fval1 = pgms.loc[fstr, 'ARITHMETIC_MEAN'].Result fval2 = target_df.loc[fstr, 'ARITHMETIC_MEAN'].Result np.testing.assert_allclose(fval1, fval2, rtol=1e-5, atol=1e-5)
def getStreams(self, eventid, stations=None, labels=None, config=None): """Get Stream from ASDF file given event id and input tags. Args: eventid (str): Event ID corresponding to an Event in the workspace. stations (list): List of stations to search for. labels (list): List of processing labels to search for. config (dict): Configuration options. Returns: StreamCollection: Object containing list of organized StationStreams. """ trace_auxholder = [] stream_auxholder = [] auxdata = self.dataset.auxiliary_data if 'TraceProcessingParameters' in auxdata: trace_auxholder = auxdata.TraceProcessingParameters if 'StreamProcessingParameters' in auxdata: stream_auxholder = auxdata.StreamProcessingParameters streams = [] if stations is None: stations = self.getStations(eventid) if labels is None: labels = self.getLabels() for waveform in self.dataset.ifilter( self.dataset.q.station == stations, self.dataset.q.tag == ['%s_%s' % (eventid, label) for label in labels]): tags = waveform.get_waveform_tags() for tag in tags: tstream = waveform[tag] inventory = waveform['StationXML'] for ttrace in tstream: if isinstance(ttrace.data[0], np.floating): if ttrace.data[0].nbytes == 4: ttrace.data = ttrace.data.astype('float32') else: ttrace.data = ttrace.data.astype('float64') else: if ttrace.data[0].nbytes == 2: ttrace.data = ttrace.data.astype('int16') elif ttrace.data[0].nbytes == 4: ttrace.data = ttrace.data.astype('int32') else: ttrace.data = ttrace.data.astype('int64') trace = StationTrace(data=ttrace.data, header=ttrace.stats, inventory=inventory, config=config) # get the provenance information provname = format_nslct(trace.stats, tag) if provname in self.dataset.provenance.list(): provdoc = self.dataset.provenance[provname] trace.setProvenanceDocument(provdoc) # get the trace processing parameters top = format_netsta(trace.stats) trace_path = format_nslct(trace.stats, tag) if top in trace_auxholder: root_auxholder = trace_auxholder[top] if trace_path in root_auxholder: bytelist = root_auxholder[ trace_path].data[:].tolist() jsonstr = ''.join([chr(b) for b in bytelist]) jdict = json.loads(jsonstr) for key, value in jdict.items(): trace.setParameter(key, value) # get the trace spectra arrays from auxiliary, # repack into stationtrace object spectra = {} if 'Cache' in auxdata: for aux in auxdata['Cache'].list(): auxarray = auxdata['Cache'][aux] if top not in auxarray.list(): continue auxarray_top = auxarray[top] if trace_path in auxarray_top: specparts = camel_case_split(aux) array_name = specparts[-1].lower() specname = '_'.join(specparts[:-1]).lower() specarray = auxarray_top[trace_path].data[()] if specname in spectra: spectra[specname][array_name] = specarray else: spectra[specname] = {array_name: specarray} for key, value in spectra.items(): trace.setCached(key, value) stream = StationStream(traces=[trace]) stream.tag = tag # testing this out # get the stream processing parameters stream_path = format_nslit( trace.stats, stream.get_inst(), tag) if top in stream_auxholder: top_auxholder = stream_auxholder[top] if stream_path in top_auxholder: auxarray = top_auxholder[stream_path] bytelist = auxarray.data[:].tolist() jsonstr = ''.join([chr(b) for b in bytelist]) jdict = json.loads(jsonstr) for key, value in jdict.items(): stream.setStreamParam(key, value) streams.append(stream) # No need to handle duplicates when retrieving stations from the # workspace file because it must have been handled before putting them # into the workspace file. streams = StreamCollection( streams, handle_duplicates=False, config=config) return streams
def _read_channel(filename, line_offset, location=''): """Read channel data from COSMOS V1/V2 text file. Args: filename (str): Input COSMOS V1/V2 filename. line_offset (int): Line offset to beginning of channel text block. Returns: tuple: (obspy Trace, int line offset) """ # read station, location, and process level from text header with open(filename, 'rt', encoding='utf-8') as f: for _ in range(line_offset): next(f) lines = [next(f) for x in range(TEXT_HDR_ROWS)] # read in lines of integer data skiprows = line_offset + TEXT_HDR_ROWS int_lines, int_data = _read_lines(skiprows, filename) int_data = int_data.astype(np.int32) # read in lines of float data skiprows += int_lines + 1 flt_lines, flt_data = _read_lines(skiprows, filename) # read in comment lines skiprows += flt_lines + 1 cmt_lines, cmt_data = _read_lines(skiprows, filename) skiprows += cmt_lines + 1 # according to the powers that defined the Network.Station.Channel.Location # "standard", Location is a two character field. Most data providers, # including cosmos here, don't provide this. We'll flag it as "--". hdr = _get_header_info(int_data, flt_data, lines, cmt_data, location=location) head, tail = os.path.split(filename) hdr['standard']['source_file'] = tail or os.path.basename(head) # read in the data nrows, data = _read_lines(skiprows, filename) # Check for "off-by-one" problem that sometimes occurs with cosmos data # Notes: # - We cannot do this check inside _get_header_info because we don't # have the data there. # - That method is written to set npts from the header as documented in # the spec ("lenght" == npts*dt) but it appears that sometimes a # different convention is used where the "length" of the record is # actually is actuation (npts-1)*dt. In this case, we need to # recompute duration and npts if hdr['npts'] == (len(data) - 1): hdr['npts'] = len(data) hdr['duration'] = (hdr['npts'] - 1) * hdr['delta'] # check units unit = hdr['format_specific']['physical_units'] if unit in UNIT_CONVERSIONS: data *= UNIT_CONVERSIONS[unit] logging.debug('Data converted from %s to cm/s/s' % (unit)) else: if unit != 'counts': raise ValueError( 'COSMOS: %s is not a supported unit.' % unit) if hdr['standard']['units'] != 'acc': raise ValueError('COSMOS: Only acceleration data accepted.') trace = StationTrace(data.copy(), Stats(hdr.copy())) # record that this data has been converted to g, if it has if hdr['standard']['process_level'] != PROCESS_LEVELS['V0']: response = {'input_units': 'counts', 'output_units': 'cm/s^2'} trace.setProvenance('remove_response', response) # set new offset new_offset = skiprows + nrows new_offset += 1 # there is an 'end of record' line after the data return (trace, new_offset)
def test_trace(): data = np.random.rand(1000) header = { 'sampling_rate': 1, 'npts': len(data), 'network': 'US', 'location': '11', 'station': 'ABCD', 'channel': 'HN1', 'starttime': UTCDateTime(2010, 1, 1, 0, 0, 0) } inventory = get_inventory() invtrace = StationTrace(data=data, header=header, inventory=inventory) invtrace.setProvenance('detrend', {'detrending_method': 'demean'}) invtrace.setParameter('failed', True) invtrace.setParameter('corner_frequencies', [1, 2, 3]) invtrace.setParameter('metadata', {'name': 'Fred'}) assert invtrace.getProvenance('detrend')[0] == { 'detrending_method': 'demean' } assert invtrace.getParameter('failed') assert invtrace.getParameter('corner_frequencies') == [1, 2, 3] assert invtrace.getParameter('metadata') == {'name': 'Fred'} prov = invtrace.getProvSeries() assert prov[0] == 'demean'
def test_trace(): data = np.random.rand(1000) header = { "sampling_rate": 1, "npts": len(data), "network": "US", "location": "11", "station": "ABCD", "channel": "HN1", "starttime": UTCDateTime(2010, 1, 1, 0, 0, 0), } inventory = get_inventory() invtrace = StationTrace(data=data, header=header, inventory=inventory) invtrace.setProvenance("detrend", {"detrending_method": "demean"}) invtrace.setParameter("failed", True) invtrace.setParameter("corner_frequencies", [1, 2, 3]) invtrace.setParameter("metadata", {"name": "Fred"}) assert invtrace.getProvenance("detrend")[0] == { "detrending_method": "demean" } assert invtrace.getParameter("failed") assert invtrace.getParameter("corner_frequencies") == [1, 2, 3] assert invtrace.getParameter("metadata") == {"name": "Fred"} prov = invtrace.getProvSeries() assert prov[0] == "demean"
def test_rotd(): ddir = os.path.join("data", "testdata", "process") datadir = pkg_resources.resource_filename("gmprocess", ddir) # Create a stream and station summary, convert from m/s^2 to cm/s^2 (GAL) osc1_data = np.genfromtxt(datadir + "/ALCTENE.UW..sac.acc.final.txt") osc2_data = np.genfromtxt(datadir + "/ALCTENN.UW..sac.acc.final.txt") osc1_data = osc1_data.T[1] * 100 osc2_data = osc2_data.T[1] * 100 tr1 = StationTrace( data=osc1_data, header={ "channel": "HN1", "delta": 0.01, "npts": 24001, "standard": { "corner_frequency": np.nan, "station_name": "", "source": "json", "instrument": "", "instrument_period": np.nan, "source_format": "json", "comments": "", "source_file": "", "structure_type": "", "horizontal_orientation": np.nan, "vertical_orientation": np.nan, "sensor_serial_number": "", "process_level": "corrected physical units", "process_time": "", "units": "acc", "units_type": "acc", "instrument_sensitivity": np.nan, "instrument_damping": np.nan, }, }, ) tr2 = StationTrace( data=osc2_data, header={ "channel": "HN2", "delta": 0.01, "npts": 24001, "standard": { "corner_frequency": np.nan, "station_name": "", "source": "json", "instrument": "", "instrument_period": np.nan, "source_format": "json", "comments": "", "structure_type": "", "source_file": "", "horizontal_orientation": np.nan, "vertical_orientation": np.nan, "sensor_serial_number": "", "process_level": "corrected physical units", "process_time": "", "units": "acc", "units_type": "acc", "instrument_sensitivity": np.nan, "instrument_damping": np.nan, }, }, ) st = StationStream([tr1, tr2]) for tr in st: response = {"input_units": "counts", "output_units": "cm/s^2"} tr.setProvenance("remove_response", response) target_pga50 = 4.1221200279448444 target_sa1050 = 10.716249471749395 target_pgv50 = 6.2243050413999645 target_sa0350 = 10.091461811808575 target_sa3050 = 1.1232860465386469 station = StationSummary.from_stream( st, ["rotd50"], ["pga", "pgv", "sa0.3", "sa1.0", "sa3.0"]) pgms = station.pgms pga = pgms.loc["PGA", "ROTD(50.0)"].Result pgv = pgms.loc["PGV", "ROTD(50.0)"].Result SA10 = pgms.loc["SA(1.000)", "ROTD(50.0)"].Result SA03 = pgms.loc["SA(0.300)", "ROTD(50.0)"].Result SA30 = pgms.loc["SA(3.000)", "ROTD(50.0)"].Result np.testing.assert_allclose(pga, target_pga50) np.testing.assert_allclose(SA10, target_sa1050) np.testing.assert_allclose(pgv, target_pgv50) np.testing.assert_allclose(SA03, target_sa0350) np.testing.assert_allclose(SA30, target_sa3050)
def test_rotd(): ddir = os.path.join('data', 'testdata', 'process') datadir = pkg_resources.resource_filename('gmprocess', ddir) # Create a stream and station summary, convert from m/s^2 to cm/s^2 (GAL) osc1_data = np.genfromtxt(datadir + '/ALCTENE.UW..sac.acc.final.txt') osc2_data = np.genfromtxt(datadir + '/ALCTENN.UW..sac.acc.final.txt') osc1_data = osc1_data.T[1] * 100 osc2_data = osc2_data.T[1] * 100 tr1 = StationTrace(data=osc1_data, header={ 'channel': 'HN1', 'delta': 0.01, 'npts': 24001, 'standard': { 'corner_frequency': np.nan, 'station_name': '', 'source': 'json', 'instrument': '', 'instrument_period': np.nan, 'source_format': 'json', 'comments': '', 'source_file': '', 'structure_type': '', 'horizontal_orientation': np.nan, 'vertical_orientation': np.nan, 'sensor_serial_number': '', 'process_level': 'corrected physical units', 'process_time': '', 'units': 'acc', 'units_type': 'acc', 'instrument_sensitivity': np.nan, 'instrument_damping': np.nan } }) tr2 = StationTrace(data=osc2_data, header={ 'channel': 'HN2', 'delta': 0.01, 'npts': 24001, 'standard': { 'corner_frequency': np.nan, 'station_name': '', 'source': 'json', 'instrument': '', 'instrument_period': np.nan, 'source_format': 'json', 'comments': '', 'structure_type': '', 'source_file': '', 'horizontal_orientation': np.nan, 'vertical_orientation': np.nan, 'sensor_serial_number': '', 'process_level': 'corrected physical units', 'process_time': '', 'units': 'acc', 'units_type': 'acc', 'instrument_sensitivity': np.nan, 'instrument_damping': np.nan } }) st = StationStream([tr1, tr2]) for tr in st: response = {'input_units': 'counts', 'output_units': 'cm/s^2'} tr.setProvenance('remove_response', response) target_pga50 = 4.12528265306 target_sa1050 = 10.7362857143 target_pgv50 = 6.239364 target_sa0350 = 10.1434159021 target_sa3050 = 1.12614169215 station = StationSummary.from_stream( st, ['rotd50'], ['pga', 'pgv', 'sa0.3', 'sa1.0', 'sa3.0'] ) pgms = station.pgms pga = pgms.loc['PGA', 'ROTD(50.0)'].Result pgv = pgms.loc['PGV', 'ROTD(50.0)'].Result SA10 = pgms.loc['SA(1.000)', 'ROTD(50.0)'].Result SA03 = pgms.loc['SA(0.300)', 'ROTD(50.0)'].Result SA30 = pgms.loc['SA(3.000)', 'ROTD(50.0)'].Result np.testing.assert_allclose(pga, target_pga50, atol=0.1) np.testing.assert_allclose(SA10, target_sa1050, atol=0.1) np.testing.assert_allclose(pgv, target_pgv50, atol=0.1) np.testing.assert_allclose(SA03, target_sa0350, atol=0.1) np.testing.assert_allclose(SA30, target_sa3050, atol=0.1)
def read_nsmn(filename, config=None): """Read the Turkish NSMN strong motion data format. Args: filename (str): path to NSMN data file. config (dict): Dictionary containing configuration. Returns: list: Sequence of one StationStream object containing 3 StationTrace objects. """ header = _read_header(filename) header1 = copy.deepcopy(header) header2 = copy.deepcopy(header) header3 = copy.deepcopy(header) header1["standard"]["horizontal_orientation"] = 0.0 header1["standard"]["vertical_orientation"] = np.nan header1["channel"] = get_channel_name(header["sampling_rate"], True, False, True) header1["standard"]["units_type"] = get_units_type(header1["channel"]) header2["standard"]["horizontal_orientation"] = 90.0 header2["standard"]["vertical_orientation"] = np.nan header2["channel"] = get_channel_name(header["sampling_rate"], True, False, False) header2["standard"]["units_type"] = get_units_type(header2["channel"]) header3["standard"]["horizontal_orientation"] = 0.0 header3["standard"]["vertical_orientation"] = np.nan header3["channel"] = get_channel_name(header["sampling_rate"], True, True, False) header3["standard"]["units_type"] = get_units_type(header3["channel"]) # three columns of NS, EW, UD # data = np.genfromtxt(filename, skip_header=TEXT_HDR_ROWS, # delimiter=[COLWIDTH] * NCOLS, encoding=ENCODING) data = np.loadtxt(filename, skiprows=TEXT_HDR_ROWS, encoding=ENCODING) data1 = data[:, 0] data2 = data[:, 1] data3 = data[:, 2] trace1 = StationTrace(data=data1, header=header1) response = {"input_units": "counts", "output_units": "cm/s^2"} trace1.setProvenance("remove_response", response) trace2 = StationTrace(data=data2, header=header2) trace2.setProvenance("remove_response", response) trace3 = StationTrace(data=data3, header=header3) trace3.setProvenance("remove_response", response) stream = StationStream(traces=[trace1, trace2, trace3]) return [stream]
def test_fas(): """ Testing based upon the work provided in https://github.com/arkottke/notebooks/blob/master/effective_amp_spectrum.ipynb """ ddir = os.path.join('data', 'testdata') datadir = pkg_resources.resource_filename('gmprocess', ddir) fas_file = os.path.join(datadir, 'fas_quadratic_mean.txt') p1 = os.path.join(datadir, 'peer', 'RSN763_LOMAP_GIL067.AT2') p2 = os.path.join(datadir, 'peer', 'RSN763_LOMAP_GIL337.AT2') stream = StationStream([]) for idx, fpath in enumerate([p1, p2]): with open(fpath, encoding='utf-8') as file_obj: for _ in range(3): next(file_obj) meta = re.findall(r'[.0-9]+', next(file_obj)) dt = float(meta[1]) accels = np.array( [col for line in file_obj for col in line.split()]) trace = StationTrace(data=accels, header={ 'channel': 'H' + str(idx), 'delta': dt, 'units': 'acc', 'standard': { 'corner_frequency': np.nan, 'station_name': '', 'source': 'json', 'instrument': '', 'instrument_period': np.nan, 'source_format': 'json', 'comments': '', 'structure_type': '', 'sensor_serial_number': '', 'source_file': '', 'process_level': 'raw counts', 'process_time': '', 'horizontal_orientation': np.nan, 'vertical_orientation': np.nan, 'units': 'acc', 'units_type': 'acc', 'instrument_sensitivity': np.nan, 'instrument_damping': np.nan } }) stream.append(trace) for tr in stream: response = {'input_units': 'counts', 'output_units': 'cm/s^2'} tr.setProvenance('remove_response', response) freqs, fas = np.loadtxt(fas_file, unpack=True, usecols=(0, 1), delimiter=',') # scaling required on the test data as it was not accounted for originally imts = ['fas' + str(1 / p) for p in freqs] summary = StationSummary.from_stream(stream, ['quadratic_mean'], imts, bandwidth=30) pgms = summary.pgms for idx, f in enumerate(freqs): fstr = 'FAS(%.3f)' % (1 / f) fval = pgms.loc[fstr, 'QUADRATIC_MEAN'].Result np.testing.assert_allclose(fval, fas[idx] * stream[0].stats.delta, rtol=1e-5, atol=1e-5)
def read_knet(filename): """Read Japanese KNET strong motion file. Args: filename (str): Path to possible KNET data file. kwargs (ref): Other arguments will be ignored. Returns: Stream: Obspy Stream containing three channels of acceleration data (cm/s**2). """ logging.debug("Starting read_knet.") if not is_knet(filename): raise Exception('%s is not a valid KNET file' % filename) # Parse the header portion of the file with open(filename, 'rt') as f: lines = [next(f) for x in range(TEXT_HDR_ROWS)] hdr = {} coordinates = {} standard = {} hdr['network'] = 'BO' hdr['station'] = lines[5].split()[2] logging.debug('station: %s' % hdr['station']) standard['station_name'] = '' # according to the powers that defined the Network.Station.Channel.Location # "standard", Location is a two character field. Most data providers, # including KNET here, don't provide this. We'll flag it as "--". hdr['location'] = '--' coordinates['latitude'] = float(lines[6].split()[2]) coordinates['longitude'] = float(lines[7].split()[2]) coordinates['elevation'] = float(lines[8].split()[2]) hdr['sampling_rate'] = float( re.search('\\d+', lines[10].split()[2]).group()) hdr['delta'] = 1 / hdr['sampling_rate'] standard['units'] = 'acc' dir_string = lines[12].split()[1].strip() # knet files have directions listed as N-S, E-W, or U-D, # whereas in kiknet those directions are '4', '5', or '6'. if dir_string in ['N-S', '1', '4']: hdr['channel'] = get_channel_name(hdr['sampling_rate'], is_acceleration=True, is_vertical=False, is_north=True) elif dir_string in ['E-W', '2', '5']: hdr['channel'] = get_channel_name(hdr['sampling_rate'], is_acceleration=True, is_vertical=False, is_north=False) elif dir_string in ['U-D', '3', '6']: hdr['channel'] = get_channel_name(hdr['sampling_rate'], is_acceleration=True, is_vertical=True, is_north=False) else: raise Exception('KNET: Could not parse direction %s' % lines[12].split()[1]) logging.debug('channel: %s' % hdr['channel']) scalestr = lines[13].split()[2] parts = scalestr.split('/') num = float(parts[0].replace('(gal)', '')) den = float(parts[1]) calib = num / den hdr['calib'] = calib duration = float(lines[11].split()[2]) hdr['npts'] = int(duration * hdr['sampling_rate']) timestr = ' '.join(lines[9].split()[2:4]) # The K-NET and KiK-Net data logger adds a 15s time delay # this is removed here sttime = datetime.strptime(timestr, TIMEFMT) - timedelta(seconds=15.0) # Shift the time to utc (Japanese time is 9 hours ahead) sttime = sttime - timedelta(seconds=9 * 3600.) hdr['starttime'] = sttime # read in the data - there is a max of 8 columns per line # the code below handles the case when last line has # less than 8 columns if hdr['npts'] % COLS_PER_LINE != 0: nrows = int(np.floor(hdr['npts'] / COLS_PER_LINE)) nrows2 = 1 else: nrows = int(np.ceil(hdr['npts'] / COLS_PER_LINE)) nrows2 = 0 data = np.genfromtxt(filename, skip_header=TEXT_HDR_ROWS, max_rows=nrows, filling_values=np.nan) data = data.flatten() if nrows2: skip_header = TEXT_HDR_ROWS + nrows data2 = np.genfromtxt(filename, skip_header=skip_header, max_rows=nrows2, filling_values=np.nan) data = np.hstack((data, data2)) nrows += nrows2 # apply the correction factor we're given in the header data *= calib # fill out the rest of the standard dictionary standard['units_type'] = get_units_type(hdr['channel']) standard['horizontal_orientation'] = np.nan standard['vertical_orientation'] = np.nan standard['instrument_period'] = np.nan standard['instrument_damping'] = np.nan standard['process_time'] = '' standard['process_level'] = PROCESS_LEVELS['V1'] standard['sensor_serial_number'] = '' standard['instrument'] = '' standard['comments'] = '' standard['structure_type'] = '' if dir_string in ['1', '2', '3']: standard['structure_type'] = 'borehole' standard['corner_frequency'] = np.nan standard['units'] = 'acc' standard['source'] = SRC standard['source_format'] = 'knet' head, tail = os.path.split(filename) standard['source_file'] = tail or os.path.basename(head) # this field can be used for instrument correction # when data is in counts standard['instrument_sensitivity'] = np.nan hdr['coordinates'] = coordinates hdr['standard'] = standard # create a Trace from the data and metadata trace = StationTrace(data.copy(), Stats(hdr.copy())) response = {'input_units': 'counts', 'output_units': 'cm/s^2'} trace.setProvenance('remove_response', response) stream = StationStream(traces=[trace]) return [stream]
def test_radial_transverse(): origin = Origin(latitude=47.149, longitude=-122.7266667) st = read(os.path.join(datadir, "resp_cor", "UW.ALCT.--.*.MSEED")) st[0].stats.standard = {} st[0].stats.standard["horizontal_orientation"] = 0.0 st[0].stats["channel"] = "HN1" st[1].stats.standard = {} st[1].stats.standard["horizontal_orientation"] = 90.0 st[1].stats["channel"] = "HN2" st[2].stats.standard = {} st[2].stats.standard["horizontal_orientation"] = np.nan st[2].stats["channel"] = "HNZ" inv = read_inventory(os.path.join(datadir, "inventory.xml")) stalat, stalon = inv[0][0][0].latitude, inv[0][0][0].longitude for i, tr in enumerate(st): tr.stats["coordinates"] = {"latitude": stalat} tr.stats["coordinates"]["longitude"] = stalon tr.stats["standard"].update({ "corner_frequency": np.nan, "station_name": "", "source": "json", "instrument": "", "instrument_period": np.nan, "vertical_orientation": np.nan, "source_format": "json", "comments": "", "structure_type": "", "source_file": "", "sensor_serial_number": "", "process_level": "raw counts", "process_time": "", "units": "cm/s/s", "units_type": "acc", "instrument_sensitivity": np.nan, "volts_to_counts": np.nan, "instrument_damping": np.nan, }) baz = gps2dist_azimuth(stalat, stalon, origin.latitude, origin.longitude)[1] st1 = st.copy() st1[0].stats.channel = st1[0].stats.channel[:-1] + "N" st1[1].stats.channel = st1[1].stats.channel[:-1] + "E" st1.rotate(method="NE->RT", back_azimuth=baz) pgms = np.abs(st1.max()) st2 = StationStream([]) for t in st: st2.append(StationTrace(t.data, t.stats)) for tr in st2: response = {"input_units": "counts", "output_units": "cm/s^2"} tr.setProvenance("remove_response", response) summary = StationSummary.from_stream(st2, ["radial_transverse"], ["pga"], origin) pgmdf = summary.pgms R = pgmdf.loc["PGA", "HNR"].Result T = pgmdf.loc["PGA", "HNT"].Result np.testing.assert_almost_equal(pgms[0], sp.g * R) np.testing.assert_almost_equal(pgms[1], sp.g * T) # Test with a station whose channels are not aligned to E-N SEW_st = read(os.path.join(datadir, "resp_cor", "GS.SEW.*.mseed")) SEW_inv = read_inventory(os.path.join(datadir, "inventory_sew.xml")) stalat, stalon = inv[0][0][0].latitude, inv[0][0][0].longitude # This needs to be checked. The target data doesn't appear to be # correct. This can be updated when a tolerance is added to the rotate # method. """traces = [] for tr in SEW_st: tr.stats.coordinates = {'latitude': stalat, 'longitude': stalon} tr.stats.standard = {'corner_frequency': np.nan, 'station_name': '', 'source': 'json', 'instrument': '', 'instrument_period': np.nan, 'source_format': 'json', 'comments': '', 'structure_type': '', 'sensor_serial_number': '', 'process_level': 'raw counts', 'process_time': '', 'horizontal_orientation': SEW_inv.get_channel_metadata(tr.get_id())['azimuth'], 'units': 'acc', 'instrument_damping': np.nan} traces += [StationTrace(tr.data, tr.stats)] baz = gps2dist_azimuth(stalat, stalon, origin.latitude, origin.longitude)[1] SEW_st_copy = StationStream(traces) SEW_st_copy.rotate(method='->NE', inventory=SEW_inv) SEW_st_copy.rotate(method='NE->RT', back_azimuth=baz) pgms = np.abs(SEW_st_copy.max()) summary = StationSummary.from_stream( SEW_st, ['radial_transverse'], ['pga'], origin) np.testing.assert_almost_equal( pgms[1], sp.g * summary.pgms['PGA']['R']) np.testing.assert_almost_equal( pgms[2], sp.g * summary.pgms['PGA']['T'])""" # Test failure case without two horizontal channels copy1 = st2.copy() copy1[0].stats.channel = copy1[0].stats.channel[:-1] + "3" pgms = StationSummary.from_stream(copy1, ["radial_transverse"], ["pga"], origin).pgms assert np.isnan(pgms.loc["PGA", "HNR"].Result) assert np.isnan(pgms.loc["PGA", "HNT"].Result) # Test failure case when channels are not orthogonal copy3 = st2.copy() copy3[0].stats.standard.horizontal_orientation = 100 pgms = StationSummary.from_stream(copy3, ["radial_transverse"], ["pga"], origin).pgms assert np.isnan(pgms.loc["PGA", "HNR"].Result) assert np.isnan(pgms.loc["PGA", "HNT"].Result)
def test_arias(): ddir = os.path.join('data', 'testdata') datadir = pkg_resources.resource_filename('gmprocess', ddir) data_file = os.path.join(datadir, 'arias_data.json') with open(data_file, 'rt', encoding='utf-8') as f: jdict = json.load(f) time = np.array(jdict['time']) # input output is m/s/s acc = np.array(jdict['acc']) / 100 target_IA = jdict['ia'] delta = time[2] - time[1] sr = 1 / delta header = { 'delta': delta, 'sampling_rate': sr, 'npts': len(acc), 'units': 'm/s/s', 'channel': 'HN1', 'standard': { 'corner_frequency': np.nan, 'station_name': '', 'source': 'json', 'source_file': '', 'instrument': '', 'instrument_period': np.nan, 'source_format': 'json', 'comments': '', 'structure_type': '', 'sensor_serial_number': '', 'process_level': 'raw counts', 'process_time': '', 'horizontal_orientation': np.nan, 'vertical_orientation': np.nan, 'units': 'acc', 'units_type': 'acc', 'instrument_sensitivity': np.nan, 'instrument_damping': np.nan } } # input is cm/s/s output is m/s/s trace = StationTrace(data=acc * 100, header=header) trace2 = trace.copy() trace2.stats.channel = 'HN2' stream = StationStream([trace, trace2]) for tr in stream: response = {'input_units': 'counts', 'output_units': 'cm/s^2'} tr.setProvenance('remove_response', response) station = StationSummary.from_stream(stream, ['ARITHMETIC_MEAN'], ['arias']) pgms = station.pgms Ia = pgms.loc['ARIAS', 'ARITHMETIC_MEAN'].Result # the target has only one decimal place and is in cm/s/s Ia = Ia * 100 np.testing.assert_almost_equal(Ia, target_IA, decimal=1) # Test other components data_files, _ = read_data_dir('cwb', 'us1000chhc', '2-ECU.dat') stream = read_data(data_files[0])[0] station = StationSummary.from_stream(stream, [ 'channels', 'gmrotd', 'rotd50', 'greater_of_two_horizontals', 'ARITHMETIC_MEAN' ], ['arias']) stream = StationSummary.from_stream(stream, ['gmrotd50'], ['arias']) assert stream.pgms.Result.tolist() == []
def read_at2(dfile, horient=0.0): # This is a conveneince method so we can read in these specific data for # testing, it is not a general purpose reader since this format does not # contain a lot of metadata that is generally required for it to be useful. skiprows = 4 datafile = open(dfile, 'r', encoding='utf-8') datareader = csv.reader(datafile) data = [] header = [] # for i in range(skiprows): # next(datareader) # header.append(datareader.readlines()) count = 0 for row in datareader: if count < skiprows: header.append(row) else: data.extend([float(e) for e in row[0].split()]) count += 1 datafile.close() hdr = {} hdr['network'] = '' hdr['station'] = '' if horient == 0: hdr['channel'] = 'BH1' else: hdr['channel'] = 'BH2' hdr['location'] = '--' dt = float(header[3][1].split('=')[1].strip().lower().replace('sec', '')) hdr['npts'] = len(data) hdr['sampling_rate'] = 1 / dt hdr['duration'] = (hdr['npts'] - 1) / hdr['sampling_rate'] hdr['starttime'] = 0 # There is no lat/lon... hdr['coordinates'] = {'latitude': 0.0, 'longitude': 0.0, 'elevation': 0.0} standard = {} standard['units'] = 'acc' standard['units_type'] = 'acc' standard['horizontal_orientation'] = horient standard['vertical_orientation'] = np.nan standard['source_file'] = dfile standard['station_name'] = '' standard['corner_frequency'] = 30.0 standard['structure_type'] = '' standard['comments'] = '' standard['instrument'] = '' standard['instrument_period'] = 1.0 standard['instrument_sensitivity'] = 1.0 standard['source'] = 'PEER' standard['instrument_damping'] = 0.1 standard['sensor_serial_number'] = '' standard['process_level'] = 'corrected physical units' standard['source_format'] = 'AT2' standard['process_time'] = '' hdr['standard'] = standard # convert data from g to cm/s^2 g_to_cmss = 980.665 tr = StationTrace(np.array(data.copy()) * g_to_cmss, Stats(hdr.copy())) response = {'input_units': 'counts', 'output_units': 'cm/s^2'} tr.setProvenance('remove_response', response) return tr
def read_cwb(filename, config=None, **kwargs): """Read Taiwan Central Weather Bureau strong motion file. Args: filename (str): Path to possible CWB data file. config (dict): Dictionary containing configuration. kwargs (ref): Other arguments will be ignored. Returns: Stream: Obspy Stream containing three channels of acceleration data (cm/s**2). """ logging.debug("Starting read_cwb.") if not is_cwb(filename, config): raise Exception(f"{filename} is not a valid CWB strong motion data file.") f = open(filename, "rt", encoding="utf-8") # according to the powers that defined the Network.Station.Channel.Location # "standard", Location is a two character field. Most data providers, # including CWB here, don't provide this. We'll flag it as "--". data = np.genfromtxt( filename, skip_header=HDR_ROWS, delimiter=[COLWIDTH] * NCOLS ) # time, Z, NS, EW hdr = _get_header_info(f, data) f.close() head, tail = os.path.split(filename) hdr["standard"]["source_file"] = tail or os.path.basename(head) hdr_z = hdr.copy() hdr_z["channel"] = get_channel_name( hdr["sampling_rate"], is_acceleration=True, is_vertical=True, is_north=False ) hdr_z["standard"]["horizontal_orientation"] = np.nan hdr_z["standard"]["vertical_orientation"] = np.nan hdr_z["standard"]["units_type"] = get_units_type(hdr_z["channel"]) hdr_h1 = hdr.copy() hdr_h1["channel"] = get_channel_name( hdr["sampling_rate"], is_acceleration=True, is_vertical=False, is_north=True ) hdr_h1["standard"]["horizontal_orientation"] = np.nan hdr_h1["standard"]["vertical_orientation"] = np.nan hdr_h1["standard"]["units_type"] = get_units_type(hdr_h1["channel"]) hdr_h2 = hdr.copy() hdr_h2["channel"] = get_channel_name( hdr["sampling_rate"], is_acceleration=True, is_vertical=False, is_north=False ) hdr_h2["standard"]["horizontal_orientation"] = np.nan hdr_h2["standard"]["vertical_orientation"] = np.nan hdr_h2["standard"]["units_type"] = get_units_type(hdr_h2["channel"]) stats_z = Stats(hdr_z) stats_h1 = Stats(hdr_h1) stats_h2 = Stats(hdr_h2) response = {"input_units": "counts", "output_units": "cm/s^2"} trace_z = StationTrace(data=data[:, 1], header=stats_z) trace_z.setProvenance("remove_response", response) trace_h1 = StationTrace(data=data[:, 2], header=stats_h1) trace_h1.setProvenance("remove_response", response) trace_h2 = StationTrace(data=data[:, 3], header=stats_h2) trace_h2.setProvenance("remove_response", response) stream = StationStream([trace_z, trace_h1, trace_h2]) return [stream]
def read_esm(filename, config=None, **kwargs): """Read European ESM strong motion file. Args: filename (str): Path to possible ESM data file. config (dict): Dictionary containing configuration. kwargs (ref): Other arguments will be ignored. Returns: Stream: Obspy Stream containing one channels of acceleration data (cm/s**2). """ logging.debug("Starting read_esm.") if not is_esm(filename, config): raise Exception(f"{filename} is not a valid ESM file") # Parse the header portion of the file header = {} with open(filename, "rt") as f: lines = [next(f) for x in range(TEXT_HDR_ROWS)] for line in lines: parts = line.split(":") key = parts[0].strip() value = ":".join(parts[1:]).strip() header[key] = value stats = {} standard = {} coordinates = {} # fill in all known stats header fields stats["network"] = header["NETWORK"] stats["station"] = header["STATION_CODE"] stats["channel"] = header["STREAM"] stats["location"] = "--" stats["delta"] = float(header["SAMPLING_INTERVAL_S"]) stats["sampling_rate"] = 1 / stats["delta"] stats["calib"] = 1.0 stats["npts"] = int(header["NDATA"]) stimestr = header["DATE_TIME_FIRST_SAMPLE_YYYYMMDD_HHMMSS"] stats["starttime"] = datetime.strptime(stimestr, TIMEFMT) # fill in standard fields head, tail = os.path.split(filename) standard["source_file"] = tail or os.path.basename(head) standard["source"] = SRC standard["source_format"] = FORMAT standard["horizontal_orientation"] = np.nan standard["vertical_orientation"] = np.nan standard["station_name"] = header["STATION_NAME"] try: standard["instrument_period"] = 1 / float( header["INSTRUMENTAL_FREQUENCY_HZ"]) except ValueError: standard["instrument_period"] = np.nan try: standard["instrument_damping"] = 1 / float( header["INSTRUMENTAL_DAMPING"]) except ValueError: standard["instrument_damping"] = np.nan ptimestr = header["DATA_TIMESTAMP_YYYYMMDD_HHMMSS"] ptime = datetime.strptime(ptimestr, TIMEFMT).strftime(TIMEFMT2) standard["process_time"] = ptime standard["process_level"] = PROCESS_LEVELS["V1"] instr_str = header["INSTRUMENT"] parts = instr_str.split("|") sensor_str = parts[0].split("=")[1].strip() standard["sensor_serial_number"] = "" standard["instrument"] = sensor_str standard["comments"] = "" standard["structure_type"] = "" standard["units"] = "cm/s^2" standard["units_type"] = "acc" standard["instrument_sensitivity"] = np.nan standard["corner_frequency"] = np.nan coordinates["latitude"] = float(header["STATION_LATITUDE_DEGREE"]) coordinates["longitude"] = float(header["STATION_LONGITUDE_DEGREE"]) coordinates["elevation"] = float(header["STATION_ELEVATION_M"]) # read in the data data = np.genfromtxt(filename, skip_header=TEXT_HDR_ROWS) # create a Trace from the data and metadata stats["standard"] = standard stats["coordinates"] = coordinates trace = StationTrace(data.copy(), Stats(stats.copy())) response = {"input_units": "counts", "output_units": "cm/s^2"} trace.setProvenance("remove_response", response) ftype = header["FILTER_TYPE"].capitalize() try: forder = int(header["FILTER_ORDER"]) except ValueError: forder = 0 try: lowfreq = float(header["LOW_CUT_FREQUENCY_HZ"]) except ValueError: lowfreq = np.nan try: highfreq = float(header["LOW_CUT_FREQUENCY_HZ"]) except ValueError: highfreq = np.nan if not np.isnan(lowfreq) and not np.isnan(lowfreq): filter_att = { "bandpass_filter": { "filter_type": ftype, "lower_corner_frequency": lowfreq, "higher_corner_frequency": highfreq, "filter_order": forder, } } trace.setProvenance("lowpass_filter", filter_att) detrend_att = {"detrend": {"detrending_method": "baseline"}} if "NOT REMOVED" not in header["BASELINE_CORRECTION"]: trace.setProvenance("detrend", detrend_att) stream = StationStream(traces=[trace]) return [stream]
def read_knet(filename, config=None, **kwargs): """Read Japanese KNET strong motion file. Args: filename (str): Path to possible KNET data file. config (dict): Dictionary containing configuration. kwargs (ref): Other arguments will be ignored. Returns: Stream: Obspy Stream containing three channels of acceleration data (cm/s**2). """ logging.debug("Starting read_knet.") if not is_knet(filename, config): raise Exception(f"{filename} is not a valid KNET file") # Parse the header portion of the file with open(filename, "rt") as f: lines = [next(f) for x in range(TEXT_HDR_ROWS)] hdr = {} coordinates = {} standard = {} hdr["network"] = "BO" hdr["station"] = lines[5].split()[2] logging.debug(f"station: {hdr['station']}") standard["station_name"] = "" # according to the powers that defined the Network.Station.Channel.Location # "standard", Location is a two character field. Most data providers, # including KNET here, don't provide this. We'll flag it as "--". hdr["location"] = "--" coordinates["latitude"] = float(lines[6].split()[2]) coordinates["longitude"] = float(lines[7].split()[2]) coordinates["elevation"] = float(lines[8].split()[2]) hdr["sampling_rate"] = float( re.search("\\d+", lines[10].split()[2]).group()) hdr["delta"] = 1 / hdr["sampling_rate"] standard["units_type"] = "acc" standard["units_type"] = "cm/s/s" dir_string = lines[12].split()[1].strip() # knet files have directions listed as N-S, E-W, or U-D, # whereas in kiknet those directions are '4', '5', or '6'. if dir_string in ["N-S", "1", "4"]: hdr["channel"] = get_channel_name(hdr["sampling_rate"], is_acceleration=True, is_vertical=False, is_north=True) elif dir_string in ["E-W", "2", "5"]: hdr["channel"] = get_channel_name( hdr["sampling_rate"], is_acceleration=True, is_vertical=False, is_north=False, ) elif dir_string in ["U-D", "3", "6"]: hdr["channel"] = get_channel_name(hdr["sampling_rate"], is_acceleration=True, is_vertical=True, is_north=False) else: raise Exception( f"KNET: Could not parse direction {lines[12].split()[1]}") logging.debug(f"channel: {hdr['channel']}") scalestr = lines[13].split()[2] parts = scalestr.split("/") num = float(parts[0].replace("(gal)", "")) den = float(parts[1]) calib = num / den hdr["calib"] = calib duration = float(lines[11].split()[2]) hdr["npts"] = int(duration * hdr["sampling_rate"]) timestr = " ".join(lines[9].split()[2:4]) # The K-NET and KiK-Net data logger adds a 15s time delay # this is removed here sttime = datetime.strptime(timestr, TIMEFMT) - timedelta(seconds=15.0) # Shift the time to utc (Japanese time is 9 hours ahead) sttime = sttime - timedelta(seconds=9 * 3600.0) hdr["starttime"] = sttime # read in the data - there is a max of 8 columns per line # the code below handles the case when last line has # less than 8 columns if hdr["npts"] % COLS_PER_LINE != 0: nrows = int(np.floor(hdr["npts"] / COLS_PER_LINE)) nrows2 = 1 else: nrows = int(np.ceil(hdr["npts"] / COLS_PER_LINE)) nrows2 = 0 data = np.genfromtxt(filename, skip_header=TEXT_HDR_ROWS, max_rows=nrows, filling_values=np.nan) data = data.flatten() if nrows2: skip_header = TEXT_HDR_ROWS + nrows data2 = np.genfromtxt(filename, skip_header=skip_header, max_rows=nrows2, filling_values=np.nan) data = np.hstack((data, data2)) nrows += nrows2 # apply the correction factor we're given in the header data *= calib # fill out the rest of the standard dictionary standard["units_type"] = get_units_type(hdr["channel"]) standard["horizontal_orientation"] = np.nan standard["vertical_orientation"] = np.nan standard["instrument_period"] = np.nan standard["instrument_damping"] = np.nan standard["process_time"] = "" standard["process_level"] = PROCESS_LEVELS["V1"] standard["sensor_serial_number"] = "" standard["instrument"] = "" standard["comments"] = "" standard["structure_type"] = "" if dir_string in ["1", "2", "3"]: standard["structure_type"] = "borehole" standard["corner_frequency"] = np.nan standard["units"] = "acc" standard["source"] = SRC standard["source_format"] = "knet" head, tail = os.path.split(filename) standard["source_file"] = tail or os.path.basename(head) # these fields can be used for instrument correction # when data is in counts standard["instrument_sensitivity"] = np.nan standard["volts_to_counts"] = np.nan hdr["coordinates"] = coordinates hdr["standard"] = standard # create a Trace from the data and metadata trace = StationTrace(data.copy(), Stats(hdr.copy())) response = {"input_units": "counts", "output_units": "cm/s^2"} trace.setProvenance("remove_response", response) stream = StationStream(traces=[trace]) return [stream]
def _read_channel(filename, line_offset, volume, location='', alternate=False): """Read channel data from USC V1 text file. Args: filename (str): Input USC V1 filename. line_offset (int): Line offset to beginning of channel text block. volume (dictionary): Dictionary of formatting information. Returns: tuple: (obspy Trace, int line offset) """ if alternate: int_rows = 5 int_fmt = 20 * [4] data_cols = 8 else: int_rows = volume['INT_HDR_ROWS'] int_fmt = volume['INT_FMT'] data_cols = 10 # Parse the header portion of the file try: with open(filename, 'rt') as f: for _ in range(line_offset): next(f) lines = [next(f) for x in range(volume['TEXT_HDR_ROWS'])] # Accounts for blank lines at end of files except StopIteration: return (None, 1 + line_offset) # read in lines of integer data skiprows = line_offset + volume['TEXT_HDR_ROWS'] int_data = np.genfromtxt(filename, skip_header=skiprows, max_rows=int_rows, dtype=np.int32, delimiter=int_fmt).flatten() # read in lines of float data skiprows += int_rows flt_data = np.genfromtxt(filename, skip_header=skiprows, max_rows=volume['FLT_HDR_ROWS'], dtype=np.float64, delimiter=volume['FLT_FMT']).flatten() hdr = _get_header_info(int_data, flt_data, lines, 'V1', location=location) skiprows += volume['FLT_HDR_ROWS'] # read in the data nrows = int(np.floor(hdr['npts'] * 2 / data_cols)) all_data = np.genfromtxt(filename, skip_header=skiprows, max_rows=nrows, dtype=np.float64, delimiter=volume['COL_FMT']) data = all_data.flatten()[1::2] times = all_data.flatten()[0::2] frac = hdr['format_specific']['fractional_unit'] if frac > 0: data *= UNIT_CONVERSIONS['g'] * frac logging.debug('Data converted from g * %s to cm/s/s' % (frac)) else: unit = _get_units(lines[11]) if unit in UNIT_CONVERSIONS: data *= UNIT_CONVERSIONS[unit] logging.debug('Data converted from %s to cm/s/s' % (unit)) else: raise ValueError('USC: %s is not a supported unit.' % unit) # Put file name into dictionary head, tail = os.path.split(filename) hdr['standard']['source_file'] = tail or os.path.basename(head) trace = StationTrace(data.copy(), Stats(hdr.copy())) if not is_evenly_spaced(times): trace = resample_uneven_trace(trace, times, data) response = {'input_units': 'counts', 'output_units': 'cm/s^2'} trace.setProvenance('remove_response', response) # set new offset new_offset = skiprows + nrows new_offset += 1 # there is an 'end of record' line after the data return (trace, new_offset)
def test_arias(): ddir = os.path.join("data", "testdata") datadir = pkg_resources.resource_filename("gmprocess", ddir) data_file = os.path.join(datadir, "arias_data.json") with open(data_file, "rt", encoding="utf-8") as f: jdict = json.load(f) time = np.array(jdict["time"]) # input output is m/s/s acc = np.array(jdict["acc"]) / 100 target_IA = jdict["ia"] delta = time[2] - time[1] sr = 1 / delta header = { "delta": delta, "sampling_rate": sr, "npts": len(acc), "channel": "HN1", "standard": { "corner_frequency": np.nan, "station_name": "", "source": "json", "source_file": "", "instrument": "", "instrument_period": np.nan, "source_format": "json", "comments": "", "structure_type": "", "sensor_serial_number": "", "process_level": "raw counts", "process_time": "", "horizontal_orientation": np.nan, "vertical_orientation": np.nan, "units": "m/s/s", "units_type": "acc", "instrument_sensitivity": np.nan, "volts_to_counts": np.nan, "instrument_damping": np.nan, }, } # input is cm/s/s output is m/s/s trace = StationTrace(data=acc * 100, header=header) trace2 = trace.copy() trace2.stats.channel = "HN2" stream = StationStream([trace, trace2]) for tr in stream: response = {"input_units": "counts", "output_units": "cm/s^2"} tr.setProvenance("remove_response", response) station = StationSummary.from_stream(stream, ["ARITHMETIC_MEAN"], ["arias"]) pgms = station.pgms Ia = pgms.loc["ARIAS", "ARITHMETIC_MEAN"].Result # the target has only one decimal place and is in cm/s/s Ia = Ia * 100 np.testing.assert_almost_equal(Ia, target_IA, decimal=1) # Test other components data_files, _ = read_data_dir("cwb", "us1000chhc", "2-ECU.dat") stream = read_data(data_files[0])[0] station = StationSummary.from_stream( stream, [ "channels", "gmrotd", "rotd50", "greater_of_two_horizontals", "ARITHMETIC_MEAN", ], ["arias"], ) stream = StationSummary.from_stream(stream, ["gmrotd50"], ["arias"]) assert stream.pgms.Result.tolist() == []
def _read_channel(filename, line_offset): """Read channel data from GNS V1 text file. Args: filename (str): Input GNS V1 filename. line_offset (int): Line offset to beginning of channel text block. Returns: tuple: (obspy Trace, int line offset) """ # read station and location strings from text header with open(filename, 'rt', encoding='utf-8') as f: for _ in range(line_offset): next(f) lines = [next(f) for x in range(TEXT_HDR_ROWS)] # this code supports V1 and V2 format files. Which one is this? data_format = 'V2' if lines[0].lower().find('uncorrected') >= 0: data_format = 'V1' # parse out the station code, name, and component string # from text header station = lines[1].split()[1] logging.debug('station: %s' % station) name = lines[2].replace(' ', '_').strip() component = lines[12].split()[1] # parse the instrument type from the text header instrument = lines[3].split()[1] # parse the sensor resolution from the text header resolution_str = lines[4].split()[1] resolution = int(re.search(r"\d+", resolution_str).group()) # read floating point header array skip_header = line_offset + TEXT_HDR_ROWS hdr_data = np.genfromtxt(filename, skip_header=skip_header, max_rows=FP_HDR_ROWS) # parse header dictionary from float header array hdr = _read_header(hdr_data, station, name, component, data_format, instrument, resolution) head, tail = os.path.split(filename) hdr['standard']['source_file'] = tail or os.path.basename(head) # according to the powers that defined the Network.Station.Channel.Location # "standard", Location is a two character field. Most data providers, # including GeoNet here, don't provide this. We'll flag it as "--". hdr['location'] = '--' skip_header2 = line_offset + TEXT_HDR_ROWS + FP_HDR_ROWS widths = [8] * COLS_PER_ROW nrows = int(np.ceil(hdr['npts'] / COLS_PER_ROW)) data = np.genfromtxt(filename, skip_header=skip_header2, max_rows=nrows, filling_values=np.nan, delimiter=widths) data = data.flatten() data = data[0:hdr['npts']] # for debugging, read in the velocity data nvel = hdr_data[3, 4] if nvel: if nvel % COLS_PER_ROW != 0: nvel_rows = int(np.floor(nvel / COLS_PER_ROW)) else: nvel_rows = int(np.ceil(nvel / COLS_PER_ROW)) skip_header_vel = line_offset + TEXT_HDR_ROWS + FP_HDR_ROWS + nrows widths = [8] * COLS_PER_ROW velocity = np.genfromtxt(filename, skip_header=skip_header_vel, max_rows=nvel_rows, filling_values=np.nan, delimiter=widths) velocity = velocity.flatten() velocity *= MMPS_TO_CMPS else: velocity = np.array([]) # for V2 files, there are extra blocks of data we need to skip containing # velocity and displacement data if data_format == 'V2': velrows = int(np.ceil(hdr_data[3, 4] / COLS_PER_ROW)) disrows = int(np.ceil(hdr_data[3, 5] / COLS_PER_ROW)) nrows = nrows + velrows + disrows data *= MMPS_TO_CMPS # convert to cm/s**2 trace = StationTrace(data, Stats(hdr)) response = {'input_units': 'counts', 'output_units': 'cm/s^2'} trace.setProvenance('remove_response', response) offset = skip_header2 + nrows return (trace, offset, velocity)
def read_smc(filename, config=None, **kwargs): """Read SMC strong motion file. Args: filename (str): Path to possible SMC data file. config (dict): Dictionary containing configuration. kwargs (ref): any_structure (bool): Read data from any type of structure, raise Exception if False and structure type is not free-field. accept_flagged (bool): accept problem flagged data. set_location (str): Two character code for location. Other arguments will be ignored. Returns: Stream: Obspy Stream containing one channel of acceleration data (cm/s**2). """ logging.debug("Starting read_smc.") any_structure = kwargs.get("any_structure", False) accept_flagged = kwargs.get("accept_flagged", False) location = kwargs.get("location", "") if not is_smc(filename, config): raise Exception(f"{filename} is not a valid SMC file") with open(filename, "rt") as f: line = f.readline().strip() if "DISPLACEMENT" in line: raise BaseException( f"SMC: Diplacement records are not supported: {filename}." ) elif "VELOCITY" in line: raise BaseException(f"SMC: Velocity records are not supported: {filename}.") elif line == "*": raise BaseException(f"SMC: No record volume specified in file: {filename}.") stats, num_comments = _get_header_info( filename, any_structure=any_structure, accept_flagged=accept_flagged, location=location, ) skip = ASCII_HEADER_LINES + INTEGER_HEADER_LINES + num_comments + FLOAT_HEADER_LINES # read float data (8 columns per line) nrows = int(np.floor(stats["npts"] / DATA_COLUMNS)) data = np.genfromtxt( filename, max_rows=nrows, skip_header=skip, delimiter=FLOAT_DATA_WIDTHS ) data = data.flatten() if stats["npts"] % DATA_COLUMNS: lastrow = np.genfromtxt( filename, max_rows=1, skip_header=skip + nrows, delimiter=FLOAT_DATA_WIDTHS ) data = np.append(data, lastrow) data = data[0 : stats["npts"]] trace = StationTrace(data, header=stats) response = {"input_units": "counts", "output_units": "cm/s^2"} trace.setProvenance("remove_response", response) stream = StationStream(traces=[trace]) return [stream]
def _read_volume_two(filename, line_offset, location='', units='acc'): """Read channel data from DMG text file. Args: filename (str): Input DMG V2 filename. line_offset (int): Line offset to beginning of channel text block. units (str): Units to get. Returns: tuple: (list of obspy Trace, int line offset) """ try: with open(filename, 'rt', encoding='utf-8') as f: for _ in range(line_offset): next(f) lines = [next(f) for x in range(V2_TEXT_HDR_ROWS)] # Accounts for blank lines at end of files except StopIteration: return (None, 1 + line_offset) # read in lines of integer data skip_rows = V2_TEXT_HDR_ROWS + line_offset int_data = _read_lines(skip_rows, V2_INT_HDR_ROWS, V2_INT_FMT, filename) int_data = int_data[0:100].astype(np.int32) # read in lines of float data skip_rows += V2_INT_HDR_ROWS flt_data = _read_lines(skip_rows, V2_REAL_HDR_ROWS, V2_REAL_FMT, filename) flt_data = flt_data[:100] skip_rows += V2_REAL_HDR_ROWS # according to the powers that defined the Network.Station.Channel.Location # "standard", Location is a two character field. Most data providers, # including csmip/dmg here, don't always provide this. We'll flag it as # "--". hdr = _get_header_info(int_data, flt_data, lines, 'V2', location=location) head, tail = os.path.split(filename) hdr['standard']['source_file'] = tail or os.path.basename(head) traces = [] # read acceleration data if hdr['npts'] > 0: acc_rows, acc_fmt, unit = _get_data_format( filename, skip_rows, hdr['npts']) acc_data = _read_lines(skip_rows + 1, acc_rows, acc_fmt, filename) acc_data = acc_data[:hdr['npts']] if unit in UNIT_CONVERSIONS: acc_data *= UNIT_CONVERSIONS[unit] logging.debug('Data converted from %s to cm/s/s' % (unit)) else: raise ValueError('DMG: %s is not a supported unit.' % unit) acc_trace = StationTrace(acc_data.copy(), Stats(hdr.copy())) response = {'input_units': 'counts', 'output_units': 'cm/s^2'} acc_trace.setProvenance('remove_response', response) if units == 'acc': traces += [acc_trace] skip_rows += int(acc_rows) + 1 # ------------------------------------------------------------------------- # NOTE: The way we were initially reading velocity and displacement data # was not correct. I'm deleting it for now since we don't need it. If/when # we revisit this we need to be more careful about how this is handled. # ------------------------------------------------------------------------- # read velocity data vel_hdr = hdr.copy() vel_hdr['standard']['units'] = 'vel' vel_hdr['npts'] = int_data[63] if vel_hdr['npts'] > 0: vel_rows, vel_fmt, unit = _get_data_format( filename, skip_rows, vel_hdr['npts']) vel_data = _read_lines(skip_rows + 1, vel_rows, vel_fmt, filename) vel_data = vel_data[:vel_hdr['npts']] skip_rows += int(vel_rows) + 1 # read displacement data disp_hdr = hdr.copy() disp_hdr['standard']['units'] = 'disp' disp_hdr['npts'] = int_data[65] if disp_hdr['npts'] > 0: disp_rows, disp_fmt, unit = _get_data_format( filename, skip_rows, disp_hdr['npts']) disp_data = _read_lines(skip_rows + 1, disp_rows, disp_fmt, filename) disp_data = disp_data[:disp_hdr['npts']] skip_rows += int(disp_rows) + 1 # there is an 'end of record' line after the data] new_offset = skip_rows + 1 return (traces, new_offset)
def test_duration(): ddir = os.path.join('data', 'testdata') datadir = pkg_resources.resource_filename('gmprocess', ddir) data_file = os.path.join(datadir, 'duration_data.json') with open(data_file, 'rt', encoding='utf-8') as f: jdict = json.load(f) time = np.array(jdict['time']) # input output is m/s/s acc = np.array(jdict['acc']) / 100 target_d595 = jdict['d595'] delta = time[2] - time[1] sr = 1 / delta header = { 'delta': delta, 'sampling_rate': sr, 'npts': len(acc), 'units': 'm/s/s', 'channel': 'HN1', 'standard': { 'corner_frequency': np.nan, 'station_name': '', 'source': 'json', 'source_file': '', 'instrument': '', 'instrument_period': np.nan, 'source_format': 'json', 'comments': '', 'structure_type': '', 'sensor_serial_number': '', 'process_level': 'raw counts', 'process_time': '', 'horizontal_orientation': np.nan, 'vertical_orientation': np.nan, 'units': 'acc', 'units_type': 'acc', 'instrument_sensitivity': np.nan, 'instrument_damping': np.nan } } # input is cm/s/s output is m/s/s trace = StationTrace(data=acc * 100, header=header) trace2 = trace.copy() trace2.stats.channel = 'HN2' stream = StationStream([trace, trace2]) for tr in stream: response = {'input_units': 'counts', 'output_units': 'cm/s^2'} tr.setProvenance('remove_response', response) station = StationSummary.from_stream( stream, ['ARITHMETIC_MEAN'], ['duration5-95']) pgms = station.pgms d595 = pgms.loc['DURATION5-95', 'ARITHMETIC_MEAN'].Result np.testing.assert_allclose(d595, target_d595, atol=1e-4, rtol=1e-4) # Test other components data_files, _ = read_data_dir('cwb', 'us1000chhc', '2-ECU.dat') stream = read_data(data_files[0])[0] station = StationSummary.from_stream( stream, ['channels', 'gmrotd', 'rotd50', 'greater_of_two_horizontals', 'ARITHMETIC_MEAN', 'geometric_mean'], ['duration5-95'] ) # Currently disallowed assert 'gmrotd' not in station.pgms.index.get_level_values(1) assert 'rotd50' not in station.pgms.index.get_level_values(1) print(station)
def test_duration(): ddir = os.path.join("data", "testdata") datadir = pkg_resources.resource_filename("gmprocess", ddir) data_file = os.path.join(datadir, "duration_data.json") with open(data_file, "rt", encoding="utf-8") as f: jdict = json.load(f) time = np.array(jdict["time"]) # input output is m/s/s acc = np.array(jdict["acc"]) / 100 target_d595 = jdict["d595"] delta = time[2] - time[1] sr = 1 / delta header = { "delta": delta, "sampling_rate": sr, "npts": len(acc), "channel": "HN1", "standard": { "corner_frequency": np.nan, "station_name": "", "source": "json", "source_file": "", "instrument": "", "instrument_period": np.nan, "source_format": "json", "comments": "", "structure_type": "", "sensor_serial_number": "", "process_level": "raw counts", "process_time": "", "horizontal_orientation": np.nan, "vertical_orientation": np.nan, "units": "m/s/s", "units_type": "acc", "instrument_sensitivity": np.nan, "volts_to_counts": np.nan, "instrument_damping": np.nan, }, } # input is cm/s/s output is m/s/s trace = StationTrace(data=acc * 100, header=header) trace2 = trace.copy() trace2.stats.channel = "HN2" stream = StationStream([trace, trace2]) for tr in stream: response = {"input_units": "counts", "output_units": "cm/s^2"} tr.setProvenance("remove_response", response) station = StationSummary.from_stream(stream, ["ARITHMETIC_MEAN"], ["duration5-95"]) pgms = station.pgms d595 = pgms.loc["DURATION5-95", "ARITHMETIC_MEAN"].Result np.testing.assert_allclose(d595, target_d595, atol=1e-4, rtol=1e-4) # Test other components data_files, _ = read_data_dir("cwb", "us1000chhc", "2-ECU.dat") stream = read_data(data_files[0])[0] station = StationSummary.from_stream( stream, [ "channels", "gmrotd", "rotd50", "greater_of_two_horizontals", "ARITHMETIC_MEAN", "geometric_mean", ], ["duration5-95"], ) # Currently disallowed assert "gmrotd" not in station.pgms.index.get_level_values(1) assert "rotd50" not in station.pgms.index.get_level_values(1) print(station)
def read_renadic(filename, **kwargs): """Read the Chilean RENADIC strong motion data format. Args: filename (str): path to RENADIC data file. kwargs (ref): Other arguments will be ignored. Returns: list: Sequence of one StationStream object containing 3 StationTrace objects. """ # This network does not include station coordinates in the data files, # but they did provide a PDF table with information about each station, # including structure type (free field or something else) and the # coordinates data_dir = pkg_resources.resource_filename('gmprocess', 'data') tablefile = os.path.join(data_dir, 'station_coordinates.xlsx') table = pd.read_excel(tablefile, engine="openpyxl") with open(filename, 'rt', encoding=ENCODING) as f: lines1 = [next(f) for x in range(TEXT_HDR_ROWS)] header1 = _read_header(lines1, filename, table) ndata_rows = int(np.ceil((header1['npts'] * 2) / NCOLS)) skip_rows = TEXT_HDR_ROWS + INT_HEADER_ROWS + FLOAT_HEADER_ROWS data1 = _read_data(filename, skip_rows, header1['npts']) skip_rows += ndata_rows + 1 with open(filename, 'rt', encoding=ENCODING) as f: [next(f) for x in range(skip_rows)] lines2 = [next(f) for x in range(TEXT_HDR_ROWS)] header2 = _read_header(lines2, filename, table) skip_rows += TEXT_HDR_ROWS + INT_HEADER_ROWS + FLOAT_HEADER_ROWS data2 = _read_data(filename, skip_rows, header1['npts']) skip_rows += ndata_rows + 1 with open(filename, 'rt', encoding=ENCODING) as f: [next(f) for x in range(skip_rows)] lines3 = [next(f) for x in range(TEXT_HDR_ROWS)] header3 = _read_header(lines3, filename, table) skip_rows += TEXT_HDR_ROWS + INT_HEADER_ROWS + FLOAT_HEADER_ROWS data3 = _read_data(filename, skip_rows, header1['npts']) trace1 = StationTrace(data=data1, header=header1) response = {'input_units': 'counts', 'output_units': 'cm/s^2'} trace1.setProvenance('remove_response', response) trace2 = StationTrace(data=data2, header=header2) trace2.setProvenance('remove_response', response) trace3 = StationTrace(data=data3, header=header3) trace3.setProvenance('remove_response', response) stream = StationStream(traces=[trace1, trace2, trace3]) return [stream]