class EnhancedSacPzResponse(Object): codes = Tuple.T(4, String.T()) tmin = Timestamp.T(optional=True) tmax = Timestamp.T(optional=True) lat = Float.T() lon = Float.T() elevation = Float.T() depth = Float.T() dip = Float.T() azimuth = Float.T() input_unit = String.T() output_unit = String.T() response = trace.PoleZeroResponse.T() def spans(self, *args): if len(args) == 0: return True elif len(args) == 1: return ((self.tmin is None or self.tmin <= args[0]) and (self.tmax is None or args[0] <= self.tmax)) elif len(args) == 2: return ((self.tmin is None or args[1] >= self.tmin) and (self.tmax is None or self.tmax >= args[0]))
class SourceGenerator(LocationGenerator): nevents = Int.T(default=2) avoid_water = Bool.T( default=False, help='Avoid sources offshore under the ocean / lakes.') radius = Float.T(default=10 * km) time_min = Timestamp.T(default=util.str_to_time('2017-01-01 00:00:00')) time_max = Timestamp.T(default=util.str_to_time('2017-01-03 00:00:00')) magnitude_min = Float.T(default=4.0) magnitude_max = Float.T(default=0.0) b_value = Float.T(optional=True, help='Gutenberg Richter magnitude distribution.') def __init__(self, *args, **kwargs): super(SourceGenerator, self).__init__(*args, **kwargs) if self.b_value and self.magnitude_max: raise Exception('b_value and magnitude_max are mutually exclusive') def draw_magnitude(self, rstate): if self.b_value is None: return rstate.uniform(self.magnitude_min, self.magnitude_max) else: return moment_tensor.rand_to_gutenberg_richter( rstate.rand(), self.b_value, magnitude_min=self.magnitude_min) def get_sources(self): sources = [] for ievent in range(self.nevents): src = self.get_source(ievent) src.name = 'scenario_ev%03d' % (ievent + 1) sources.append(src) return sources def dump_data(self, path): fn_sources = op.join(path, 'sources.yml') with open(fn_sources, 'w') as f: for src in self.get_sources(): f.write(src.dump()) fn_events = op.join(path, 'events.txt') with open(fn_events, 'w') as f: for isrc, src in enumerate(self.get_sources()): f.write(src.pyrocko_event().dump()) return [fn_events, fn_sources] def add_map_artists(self, automap): pass
class Equipment(Object): resource_id = String.T(optional=True, xmlstyle='attribute') type = String.T(optional=True, xmltagname='Type') description = Unicode.T(optional=True, xmltagname='Description') manufacturer = Unicode.T(optional=True, xmltagname='Manufacturer') vendor = Unicode.T(optional=True, xmltagname='Vendor') model = Unicode.T(optional=True, xmltagname='Model') serial_number = String.T(optional=True, xmltagname='SerialNumber') installation_date = Timestamp.T(optional=True, xmltagname='InstallationDate') removal_date = DummyAwareOptionalTimestamp.T(optional=True, xmltagname='RemovalDate') calibration_date_list = List.T(Timestamp.T(xmltagname='CalibrationDate'))
class StaticTarget(meta.MultiLocation): ''' A computation request for a spatial multi-location target of static/geodetic quantities. ''' quantity = meta.QuantityType.T( optional=True, default='displacement', help='Measurement quantity type, for now only `displacement` is' 'supported.') interpolation = InterpolationMethod.T( default='nearest_neighbor', help='Interpolation method between Green\'s functions. Supported are' ' ``nearest_neighbor`` and ``multilinear``') tsnapshot = Timestamp.T( optional=True, help='time of the desired snapshot in [s], ' 'If not given, the first sample is taken. If the desired sample' ' exceeds the length of the Green\'s function store,' ' the last sample is taken.') store_id = meta.StringID.T( optional=True, help='ID of Green\'s function store to use for the computation. ' 'If not given, the processor may use a system default.') def base_key(self): return (self.store_id, self.coords5.shape, self.quantity, self.tsnapshot, self.interpolation) @property def ntargets(self): ''' Number of targets held by instance. ''' return self.ncoords def get_targets(self): ''' Discretizes the multilocation target into a list of :class:`Target:` :returns: :class:`Target` :rtype: list ''' targets = [] for i in xrange(self.ntargets): targets.append( Target( lat=self.coords5[i, 0], lon=self.coords5[i, 1], north_shift=self.coords5[i, 2], east_shift=self.coords5[i, 3], elevation=self.coords5[i, 4])) return targets def post_process(self, engine, source, statics): return meta.StaticResult(result=statics)
class Station(BaseNode): '''This type represents a Station epoch. It is common to only have a single station epoch with the station's creation and termination dates as the epoch start and end dates.''' latitude = Latitude.T(xmltagname='Latitude') longitude = Longitude.T(xmltagname='Longitude') elevation = Distance.T(xmltagname='Elevation') site = Site.T(optional=True, xmltagname='Site') vault = Unicode.T(optional=True, xmltagname='Vault') geology = Unicode.T(optional=True, xmltagname='Geology') equipment_list = List.T(Equipment.T(xmltagname='Equipment')) operator_list = List.T(Operator.T(xmltagname='Operator')) creation_date = Timestamp.T(optional=True, xmltagname='CreationDate') termination_date = DummyAwareOptionalTimestamp.T( optional=True, xmltagname='TerminationDate') total_number_channels = Counter.T(optional=True, xmltagname='TotalNumberChannels') selected_number_channels = Counter.T(optional=True, xmltagname='SelectedNumberChannels') external_reference_list = List.T( ExternalReference.T(xmltagname='ExternalReference')) channel_list = List.T(Channel.T(xmltagname='Channel')) @property def position_values(self): lat = self.latitude.value lon = self.longitude.value elevation = value_or_none(self.elevation) return lat, lon, elevation
class DirContext(Object): path = String.T() mtime = Timestamp.T() entries = DirContextEntry.T() def get_entry(self, fn): path = os.path.abspath(fn) for entry in self.entries: if entry.path == path: return entry raise Exception('entry not found') def iter_entries(self, fn, step=1): current = self.get_entry(fn) by_ifile = dict( (entry.ifile, entry) for entry in self.entries if entry.tstart == current.tstart) icurrent = current.ifile while True: icurrent += step try: yield by_ifile[icurrent] except KeyError: break
class CreationInfo(Object): agency_id = AgencyID.T(optional=True, xmltagname='agencyID') agency_uri = ResourceReference.T(optional=True, xmltagname='agencyURI') author = Author.T(optional=True) author_uri = ResourceReference.T(optional=True, xmltagname='authorURI') creation_time = Timestamp.T(optional=True) version = Version.T(optional=True)
class Meta(Object): """Meta configuration for ``Scene``.""" scene_title = String.T(default="Unnamed Scene", help="Scene title") scene_id = String.T(default="None", help="Scene identification") satellite_name = String.T( default="Undefined Mission", help="Satellite mission name" ) wavelength = Float.T(optional=True, help="Wavelength in [m]") orbital_node = StringChoice.T( choices=["Ascending", "Descending", "Undefined"], default="Undefined", help="Orbital direction, ascending/descending", ) time_master = Timestamp.T( default=1481116161.930574, help="Timestamp for master acquisition" ) time_slave = Timestamp.T( default=1482239325.482, help="Timestamp for slave acquisition" ) extra = Dict.T(default={}, help="Extra header information") filename = String.T(optional=True) def __init__(self, *args, **kwargs): self.old_import = False mapping = {"orbit_direction": "orbital_node"} for old, new in mapping.items(): if old in kwargs.keys(): kwargs[new] = kwargs.pop(old, None) self.old_import = True Object.__init__(self, *args, **kwargs) @property def time_separation(self): """ :getter: Absolute time difference between ``time_master`` and ``time_slave`` :type: timedelta """ return dt.fromtimestamp(self.time_slave) - dt.fromtimestamp(self.time_master)
class Comment(Object): '''Container for a comment or log entry. Corresponds to SEED blockettes 31, 51 and 59.''' id = Counter.T(optional=True, xmlstyle='attribute') value = Unicode.T(xmltagname='Value') begin_effective_time = Timestamp.T(optional=True, xmltagname='BeginEffectiveTime') end_effective_time = DummyAwareOptionalTimestamp.T( optional=True, xmltagname='EndEffectiveTime') author_list = List.T(Person.T(xmltagname='Author'))
class SourceGenerator(LocationGenerator): nevents = Int.T(default=2) avoid_water = Bool.T( default=False, help='Avoid sources offshore under the ocean / lakes.') radius = Float.T( default=10*km) time_min = Timestamp.T(default=util.str_to_time('2017-01-01 00:00:00')) time_max = Timestamp.T(default=util.str_to_time('2017-01-03 00:00:00')) magnitude_min = Float.T(default=4.0) magnitude_max = Float.T(default=7.0) def get_sources(self): sources = [] for ievent in range(self.nevents): sources.append(self.get_source(ievent)) return sources def dump_data(self, path): fn_sources = op.join(path, 'sources.yml') with open(fn_sources, 'w') as f: for src in self.get_sources(): f.write(src.dump()) fn_events = op.join(path, 'events.txt') with open(fn_events, 'w') as f: for src in self.get_sources(): f.write(src.pyrocko_event().dump()) return [fn_events, fn_sources] def add_map_artists(self, automap): pass
class Detection(Object): id = String.T() time = Timestamp.T() location = geo.Point.T() ifm = Float.T() def get_event(self): lat, lon = geo.point_coords(self.location, system='latlon') event = model.Event(lat=lat, lon=lon, depth=self.location.z, time=self.time, name='%s (%g)' % (self.id, self.ifm)) return event
class WaveformMisfitResult(gf.Result, MisfitResult): '''Carries the observations for a target and corresponding synthetics. A number of different waveform or phase representations are possible. ''' processed_obs = Trace.T(optional=True) processed_syn = Trace.T(optional=True) filtered_obs = Trace.T(optional=True) filtered_syn = Trace.T(optional=True) spectrum_obs = TraceSpectrum.T(optional=True) spectrum_syn = TraceSpectrum.T(optional=True) taper = trace.Taper.T(optional=True) tobs_shift = Float.T(optional=True) tsyn_pick = Timestamp.T(optional=True) tshift = Float.T(optional=True) cc = Trace.T(optional=True) piggyback_subresults = List.T(WaveformPiggybackSubresult.T())
class BaseNode(Object): '''A base node type for derivation from: Network, Station and Channel types.''' code = String.T(xmlstyle='attribute') start_date = Timestamp.T(optional=True, xmlstyle='attribute') end_date = DummyAwareOptionalTimestamp.T(optional=True, xmlstyle='attribute') restricted_status = RestrictedStatus.T(optional=True, xmlstyle='attribute') alternate_code = String.T(optional=True, xmlstyle='attribute') historical_code = String.T(optional=True, xmlstyle='attribute') description = Unicode.T(optional=True, xmltagname='Description') comment_list = List.T(Comment.T(xmltagname='Comment')) def spans(self, *args): if len(args) == 0: return True elif len(args) == 1: return ((self.start_date is None or self.start_date <= args[0]) and (self.end_date is None or args[0] <= self.end_date)) elif len(args) == 2: return ((self.start_date is None or args[1] >= self.start_date) and (self.end_date is None or self.end_date >= args[0]))
class Target(meta.Receiver): ''' A seismogram computation request for a single component, including its post-processing parmeters. ''' quantity = meta.QuantityType.T( optional=True, help='Measurement quantity type (e.g. "displacement", "pressure", ...)' 'If not given, it is guessed from the channel code.' 'Beware: If velocity is requested, the velocity is not directly' 'retrieved. Instead a numpy.diff is run on the retrieved' 'displacements, with lower accuracy. For high accuracy we' 'recommend using the Pyrocko object DifferentiationResponse.') codes = Tuple.T( 4, String.T(), default=('', 'STA', '', 'Z'), help='network, station, location and channel codes to be set on ' 'the response trace.') elevation = Float.T( default=0.0, help='station surface elevation in [m]') store_id = meta.StringID.T( optional=True, help='ID of Green\'s function store to use for the computation. ' 'If not given, the processor may use a system default.') sample_rate = Float.T( optional=True, help='sample rate to produce. ' 'If not given the GF store\'s default sample rate is used. ' 'GF store specific restrictions may apply.') interpolation = meta.InterpolationMethod.T( default='nearest_neighbor', help='Interpolation method between Green\'s functions. Supported are' ' ``nearest_neighbor`` and ``multilinear``') optimization = OptimizationMethod.T( default='enable', optional=True, help='disable/enable optimizations in weight-delay-and-sum operation') tmin = Timestamp.T( optional=True, help='time of first sample to request in [s]. ' 'If not given, it is determined from the Green\'s functions.') tmax = Timestamp.T( optional=True, help='time of last sample to request in [s]. ' 'If not given, it is determined from the Green\'s functions.') azimuth = Float.T( optional=True, help='azimuth of sensor component in [deg], clockwise from north. ' 'If not given, it is guessed from the channel code.') dip = Float.T( optional=True, help='dip of sensor component in [deg], ' 'measured downward from horizontal. ' 'If not given, it is guessed from the channel code.') filter = Filter.T( optional=True, help='frequency response filter.') def base_key(self): return (self.store_id, self.sample_rate, self.interpolation, self.optimization, self.tmin, self.tmax, self.elevation, self.depth, self.north_shift, self.east_shift, self.lat, self.lon) def effective_quantity(self): if self.quantity is not None: return self.quantity # guess from channel code cha = self.codes[-1].upper() if len(cha) == 3: # use most common SEED conventions here, however units have to be # guessed, because they are not uniquely defined by the conventions if cha[-2] in 'HL': # high gain, low gain seismometer return 'velocity' if cha[-2] == 'N': # accelerometer return 'acceleration' if cha[-2] == 'D': # hydrophone, barometer, ... return 'pressure' if cha[-2] == 'A': # tiltmeter return 'tilt' elif len(cha) == 2: if cha[-2] == 'U': return 'displacement' if cha[-2] == 'V': return 'velocity' elif len(cha) == 1: if cha in 'NEZ': return 'displacement' if cha == 'P': return 'pressure' raise BadTarget('cannot guess measurement quantity type from channel ' 'code "%s"' % cha) def receiver(self, store): rec = meta.Receiver(**dict(meta.Receiver.T.inamevals(self))) return rec def component_code(self): cha = self.codes[-1].upper() if cha: return cha[-1] else: return ' ' def effective_azimuth(self): if self.azimuth is not None: return self.azimuth elif self.component_code() in 'NEZ': return {'N': 0., 'E': 90., 'Z': 0.}[self.component_code()] raise BadTarget('cannot determine sensor component azimuth for ' '%s.%s.%s.%s' % self.codes) def effective_dip(self): if self.dip is not None: return self.dip elif self.component_code() in 'NEZ': return {'N': 0., 'E': 0., 'Z': -90.}[self.component_code()] raise BadTarget('cannot determine sensor component dip') def get_sin_cos_factors(self): azi = self.effective_azimuth() dip = self.effective_dip() sa = math.sin(azi*d2r) ca = math.cos(azi*d2r) sd = math.sin(dip*d2r) cd = math.cos(dip*d2r) return sa, ca, sd, cd def get_factor(self): return 1.0 def post_process(self, engine, source, tr): return meta.Result(trace=tr)
class FDSNStationXML(Object): '''Top-level type for Station XML. Required field are Source (network ID of the institution sending the message) and one or more Network containers or one or more Station containers.''' schema_version = Float.T(default=1.0, xmlstyle='attribute') source = String.T(xmltagname='Source') sender = String.T(optional=True, xmltagname='Sender') module = String.T(optional=True, xmltagname='Module') module_uri = String.T(optional=True, xmltagname='ModuleURI') created = Timestamp.T(xmltagname='Created') network_list = List.T(Network.T(xmltagname='Network')) xmltagname = 'FDSNStationXML' def get_pyrocko_stations(self, nslcs=None, nsls=None, time=None, timespan=None, inconsistencies='warn'): assert inconsistencies in ('raise', 'warn') if nslcs is not None: nslcs = set(nslcs) if nsls is not None: nsls = set(nsls) tt = () if time is not None: tt = (time, ) elif timespan is not None: tt = timespan pstations = [] for network in self.network_list: if not network.spans(*tt): continue for station in network.station_list: if not station.spans(*tt): continue if station.channel_list: loc_to_channels = {} for channel in station.channel_list: if not channel.spans(*tt): continue loc = channel.location_code.strip() if loc not in loc_to_channels: loc_to_channels[loc] = [] loc_to_channels[loc].append(channel) for loc in sorted(loc_to_channels.keys()): channels = loc_to_channels[loc] if nslcs is not None: channels = [ channel for channel in channels if (network.code, station.code, loc, channel.code) in nslcs ] if not channels: continue nsl = network.code, station.code, loc if nsls is not None and nsl not in nsls: continue pstations.append( pyrocko_station_from_channels( nsl, channels, inconsistencies=inconsistencies)) else: pstations.append( model.Station(network.code, station.code, '*', lat=station.latitude.value, lon=station.longitude.value, elevation=value_or_none( station.elevation), name=station.description or '')) return pstations @classmethod def from_pyrocko_stations(cls, pyrocko_stations): ''' Generate :py:class:`FDSNStationXML` from list of :py:class;`pyrocko.model.Station` instances. :param pyrocko_stations: list of :py:class;`pyrocko.model.Station` instances. ''' from collections import defaultdict network_dict = defaultdict(list) for s in pyrocko_stations: network, station, location = s.nsl() channel_list = [] for c in s.channels: channel_list.append( Channel(location_code=location, code=c.name, latitude=Latitude(value=s.lat), longitude=Longitude(value=s.lon), elevation=Distance(value=s.elevation), depth=Distance(value=s.depth), azimuth=Azimuth(value=c.azimuth), dip=Dip(value=c.dip))) network_dict[network].append( Station(code=station, latitude=Latitude(value=s.lat), longitude=Longitude(value=s.lon), elevation=Distance(value=s.elevation), channel_list=channel_list)) timestamp = time.time() network_list = [] for k, station_list in network_dict.items(): network_list.append( Network(code=k, station_list=station_list, total_number_stations=len(station_list))) sxml = FDSNStationXML(source='from pyrocko stations list', created=timestamp, network_list=network_list) sxml.validate() return sxml def iter_network_stations(self, net=None, sta=None, time=None, timespan=None): tt = () if time is not None: tt = (time, ) elif timespan is not None: tt = timespan for network in self.network_list: if not network.spans(*tt) or (net is not None and network.code != net): continue for station in network.station_list: if not station.spans(*tt) or (sta is not None and station.code != sta): continue yield (network, station) def iter_network_station_channels(self, net=None, sta=None, loc=None, cha=None, time=None, timespan=None): if loc is not None: loc = loc.strip() tt = () if time is not None: tt = (time, ) elif timespan is not None: tt = timespan for network in self.network_list: if not network.spans(*tt) or (net is not None and network.code != net): continue for station in network.station_list: if not station.spans(*tt) or (sta is not None and station.code != sta): continue if station.channel_list: for channel in station.channel_list: if (not channel.spans(*tt) or (cha is not None and channel.code != cha) or (loc is not None and channel.location_code.strip() != loc)): continue yield (network, station, channel) def get_channel_groups(self, net=None, sta=None, loc=None, cha=None, time=None, timespan=None): groups = {} for network, station, channel in self.iter_network_station_channels( net, sta, loc, cha, time=time, timespan=timespan): net = network.code sta = station.code cha = channel.code loc = channel.location_code.strip() if len(cha) == 3: bic = cha[:2] # band and intrument code according to SEED elif len(cha) == 1: bic = '' else: bic = cha if channel.response and \ channel.response.instrument_sensitivity and \ channel.response.instrument_sensitivity.input_units: unit = channel.response.instrument_sensitivity.input_units.name else: unit = None bic = (bic, unit) k = net, sta, loc if k not in groups: groups[k] = {} if bic not in groups[k]: groups[k][bic] = [] groups[k][bic].append(channel) for nsl, bic_to_channels in groups.iteritems(): bad_bics = [] for bic, channels in bic_to_channels.iteritems(): sample_rates = [] for channel in channels: sample_rates.append(channel.sample_rate.value) if not same(sample_rates): scs = ','.join(channel.code for channel in channels) srs = ', '.join('%e' % x for x in sample_rates) err = 'ignoring channels with inconsistent sampling ' + \ 'rates (%s.%s.%s.%s: %s)' % (nsl + (scs, srs)) logger.warn(err) bad_bics.append(bic) for bic in bad_bics: del bic_to_channels[bic] return groups def choose_channels(self, target_sample_rate=None, priority_band_code=['H', 'B', 'M', 'L', 'V', 'E', 'S'], priority_units=['M/S', 'M/S**2'], priority_instrument_code=['H', 'L'], time=None, timespan=None): nslcs = {} for nsl, bic_to_channels in self.get_channel_groups( time=time, timespan=timespan).iteritems(): useful_bics = [] for bic, channels in bic_to_channels.iteritems(): rate = channels[0].sample_rate.value if target_sample_rate is not None and \ rate < target_sample_rate*0.99999: continue if len(bic[0]) == 2: if bic[0][0] not in priority_band_code: continue if bic[0][1] not in priority_instrument_code: continue unit = bic[1] prio_unit = len(priority_units) try: prio_unit = priority_units.index(unit) except ValueError: pass prio_inst = len(priority_instrument_code) prio_band = len(priority_band_code) if len(channels[0].code) == 3: try: prio_inst = priority_instrument_code.index( channels[0].code[1]) except ValueError: pass try: prio_band = priority_band_code.index( channels[0].code[0]) except ValueError: pass if target_sample_rate is None: rate = -rate useful_bics.append((-len(channels), prio_band, rate, prio_unit, prio_inst, bic)) useful_bics.sort() for _, _, rate, _, _, bic in useful_bics: channels = sorted(bic_to_channels[bic]) if channels: for channel in channels: nslcs[nsl + (channel.code, )] = channel break return nslcs def get_pyrocko_response(self, nslc, time=None, timespan=None, fake_input_units=None): net, sta, loc, cha = nslc resps = [] for _, _, channel in self.iter_network_station_channels( net, sta, loc, cha, time=time, timespan=timespan): resp = channel.response if resp: resps.append( resp.get_pyrocko_response( nslc, fake_input_units=fake_input_units)) if not resps: raise NoResponseInformation('%s.%s.%s.%s' % nslc) elif len(resps) > 1: raise MultipleResponseInformation('%s.%s.%s.%s' % nslc) return resps[0] @property def n_code_list(self): return sorted(set(x.code for x in self.network_list)) @property def ns_code_list(self): nss = set() for network in self.network_list: for station in network.station_list: nss.add((network.code, station.code)) return sorted(nss) @property def nsl_code_list(self): nsls = set() for network in self.network_list: for station in network.station_list: for channel in station.channel_list: nsls.add( (network.code, station.code, channel.location_code)) return sorted(nsls) @property def nslc_code_list(self): nslcs = set() for network in self.network_list: for station in network.station_list: for channel in station.channel_list: nslcs.add((network.code, station.code, channel.location_code, channel.code)) return sorted(nslcs) def summary(self): l = [ 'number of n codes: %i' % len(self.n_code_list), 'number of ns codes: %i' % len(self.ns_code_list), 'number of nsl codes: %i' % len(self.nsl_code_list), 'number of nslc codes: %i' % len(self.nslc_code_list) ] return '\n'.join(l)
class ScenarioCollectionItem(Object): scenario_id = gf.StringID.T() time_created = Timestamp.T() def __init__(self, **kwargs): Object.__init__(self, **kwargs) self._path = None self._pile = None self._engine = None self._scenes = None def set_base_path(self, path): self._path = path def get_base_path(self): if self._path is None: raise EnvironmentError('Base path not set!') return self._path def init_modelling(self, engine): self._engine = engine def get_path(self, *entry): return op.join(*((self._path, ) + entry)) def get_generator(self): generator = guts.load(filename=self.get_path('generator.yaml')) generator.init_modelling(self._engine) return generator def get_time_range(self): return self.get_generator().get_time_range() def have_waveforms(self, tmin, tmax): p = self.get_waveform_pile() trs_have = p.all(tmin=tmin, tmax=tmax, load_data=False, degap=False) return any(tr.data_len() > 0 for tr in trs_have) def get_waveform_pile(self): self.ensure_data() if self._pile is None: path_waveforms = self.get_path('waveforms') util.ensuredir(path_waveforms) fns = util.select_files([path_waveforms], show_progress=False) self._pile = pile.Pile() if fns: self._pile.load_files(fns, fileformat='mseed', show_progress=False) return self._pile def get_insar_scenes(self): from kite import Scene if self._scenes is None: self._scenes = [] path_insar = self.get_path('insar') util.ensuredir(path_insar) fns = util.select_files([path_insar], regex='\\.(npz)$', show_progress=False) for f in fns: self._scenes.append(Scene.load(f)) return self._scenes def get_gnss_campaigns(self): return self.get_generator().get_gnss_campaigns() def make_map(self, path_pdf): draw_scenario_gmt(self.get_generator(), path_pdf) def get_map(self, format='pdf'): path_pdf = self.get_path('map.pdf') if not op.exists(path_pdf): self.make_map(path_pdf) path = self.get_path('map.%s' % format) outdated = op.exists(path) and mtime(path) < mtime(path_pdf) if not op.exists(path) or outdated: gmtpy.convert_graph(path_pdf, path) return path def ensure_data(self, tmin=None, tmax=None, overwrite=False): return self.get_generator().dump_data(self.get_path(), tmin, tmax, overwrite) def ensure_waveforms(self, tmin=None, tmax=None, overwrite=False): self.ensure_data(tmin, tmax, overwrite) return self.get_waveform_pile() def ensure_insar_scenes(self, tmin=None, tmax=None, overwrite=False): self.ensure_data(tmin, tmax, overwrite) return self.get_insar_scenes() def get_archive(self): self.ensure_data() path_tar = self.get_path('archive.tar') if not op.exists(path_tar): path_base = self.get_path() path_waveforms = self.get_path('waveforms') self.ensure_data() fns = util.select_files([path_waveforms], show_progress=False) f = tarfile.TarFile(path_tar, 'w') for fn in fns: fna = fn[len(path_base) + 1:] f.add(fn, fna) f.close() return path_tar
class Event(Object): '''Seismic event representation :param lat: latitude of hypocenter (default 0.0) :param lon: longitude of hypocenter (default 0.0) :param time: origin time as float in seconds after '1970-01-01 00:00:00 :param name: event identifier as string (optional) :param depth: source depth (optional) :param magnitude: magnitude of event (optional) :param region: source region (optional) :param catalog: name of catalog that lists this event (optional) :param moment_tensor: moment tensor as :py:class:`moment_tensor.MomentTensor` instance (optional) :param duration: source duration as float (optional) ''' lat = Float.T(default=0.0) lon = Float.T(default=0.0) time = Timestamp.T(default=util.str_to_time('1970-01-01 00:00:00')) name = String.T(default='', optional=True) depth = Float.T(optional=True) magnitude = Float.T(optional=True) magnitude_type = String.T(optional=True) region = Unicode.T(optional=True) catalog = String.T(optional=True) moment_tensor = moment_tensor.MomentTensor.T(optional=True) duration = Float.T(optional=True) def __init__(self, lat=0., lon=0., time=0., name='', depth=None, magnitude=None, magnitude_type=None, region=None, load=None, loadf=None, catalog=None, moment_tensor=None, duration=None): vals = None if load is not None: vals = Event.oldload(load) elif loadf is not None: vals = Event.oldloadf(loadf) if vals: lat, lon, time, name, depth, magnitude, magnitude_type, region, \ catalog, moment_tensor, duration = vals Object.__init__(self, lat=lat, lon=lon, time=time, name=name, depth=depth, magnitude=magnitude, magnitude_type=magnitude_type, region=region, catalog=catalog, moment_tensor=moment_tensor, duration=duration) def time_as_string(self): return util.time_to_str(self.time) def set_name(self, name): self.name = name def olddump(self, filename): file = open(filename, 'w') self.olddumpf(file) file.close() def olddumpf(self, file): file.write('name = %s\n' % self.name) file.write('time = %s\n' % util.time_to_str(self.time)) if self.lat is not None: file.write('latitude = %.12g\n' % self.lat) if self.lon is not None: file.write('longitude = %.12g\n' % self.lon) if self.magnitude is not None: file.write('magnitude = %g\n' % self.magnitude) file.write('moment = %g\n' % moment_tensor.magnitude_to_moment(self.magnitude)) if self.magnitude_type is not None: file.write('magnitude_type = %s\n' % self.magnitude_type) if self.depth is not None: file.write('depth = %.10g\n' % self.depth) if self.region is not None: file.write('region = %s\n' % self.region) if self.catalog is not None: file.write('catalog = %s\n' % self.catalog) if self.moment_tensor is not None: m = self.moment_tensor.m() sdr1, sdr2 = self.moment_tensor.both_strike_dip_rake() file.write( ('mnn = %g\nmee = %g\nmdd = %g\nmne = %g\nmnd = %g\nmed = %g\n' 'strike1 = %g\ndip1 = %g\nrake1 = %g\n' 'strike2 = %g\ndip2 = %g\nrake2 = %g\n') % ((m[0, 0], m[1, 1], m[2, 2], m[0, 1], m[0, 2], m[1, 2]) + sdr1 + sdr2)) if self.duration is not None: file.write('duration = %g\n' % self.duration) @staticmethod def unique(events, deltat=10., group_cmp=(lambda a, b: cmp(a.catalog, b.catalog))): groups = Event.grouped(events, deltat) events = [] for group in groups: if group: group.sort(group_cmp) events.append(group[-1]) return events @staticmethod def grouped(events, deltat=10.): events = list(events) groups = [] for ia, a in enumerate(events): groups.append([]) haveit = False for ib, b in enumerate(events[:ia]): if abs(b.time - a.time) < deltat: groups[ib].append(a) haveit = True break if not haveit: groups[ia].append(a) groups = [g for g in groups if g] groups.sort(key=lambda g: sum(e.time for e in g) // len(g)) return groups @staticmethod def dump_catalog(events, filename=None, stream=None): if filename is not None: file = open(filename, 'w') else: file = stream try: i = 0 for ev in events: ev.olddumpf(file) file.write('--------------------------------------------\n') i += 1 finally: if filename is not None: file.close() @staticmethod def oldload(filename): with open(filename, 'r') as file: return Event.oldloadf(file) @staticmethod def oldloadf(file): d = {} try: for line in file: if line.lstrip().startswith('#'): continue toks = line.split(' = ', 1) if len(toks) == 2: k, v = toks[0].strip(), toks[1].strip() if k in ('name', 'region', 'catalog', 'magnitude_type'): d[k] = v if k in (('latitude longitude magnitude depth duration ' 'mnn mee mdd mne mnd med strike1 dip1 rake1 ' 'strike2 dip2 rake2 duration').split()): d[k] = float(v) if k == 'time': d[k] = util.str_to_time(v) if line.startswith('---'): d['have_separator'] = True break except Exception as e: raise FileParseError(e) if not d: raise EOF() if 'have_separator' in d and len(d) == 1: raise EmptyEvent() mt = None m6 = [d[x] for x in 'mnn mee mdd mne mnd med'.split() if x in d] if len(m6) == 6: mt = moment_tensor.MomentTensor(m=moment_tensor.symmat6(*m6)) else: sdr = [d[x] for x in 'strike1 dip1 rake1'.split() if x in d] if len(sdr) == 3: moment = 1.0 if 'moment' in d: moment = d['moment'] elif 'magnitude' in d: moment = moment_tensor.magnitude_to_moment(d['magnitude']) mt = moment_tensor.MomentTensor(strike=sdr[0], dip=sdr[1], rake=sdr[2], scalar_moment=moment) return (d.get('latitude', 0.0), d.get('longitude', 0.0), d.get('time', 0.0), d.get('name', ''), d.get('depth', None), d.get('magnitude', None), d.get('magnitude_type', None), d.get('region', None), d.get('catalog', None), mt, d.get('duration', None)) @staticmethod def load_catalog(filename): file = open(filename, 'r') try: while True: try: ev = Event(loadf=file) yield ev except EmptyEvent: pass except EOF: pass file.close() def get_hash(self): e = self if isinstance(e.time, util.hpfloat): stime = util.time_to_str(e.time, format='%Y-%m-%d %H:%M:%S.6FRAC') else: stime = util.time_to_str(e.time, format='%Y-%m-%d %H:%M:%S.3FRAC') s = float_or_none_to_str return ehash(', '.join( (stime, s(e.lat), s(e.lon), s(e.depth), s(e.magnitude), str(e.catalog), str(e.name), str(e.region)))) def human_str(self): s = [ 'Latitude [deg]: %g' % self.lat, 'Longitude [deg]: %g' % self.lon, 'Time [UTC]: %s' % util.time_to_str(self.time) ] if self.name: s.append('Name: %s' % self.name) if self.depth is not None: s.append('Depth [km]: %g' % (self.depth / 1000.)) if self.magnitude is not None: s.append('Magnitude [%s]: %3.1f' % (self.magnitude_type or 'M?', self.magnitude)) if self.region: s.append('Region: %s' % self.region) if self.catalog: s.append('Catalog: %s' % self.catalog) if self.moment_tensor: s.append(str(self.moment_tensor)) return '\n'.join(s)
class Config(HasPaths): stations_path = Path.T(optional=True, help='stations file in Pyrocko format') stations_stationxml_path = Path.T( optional=True, help='stations file in StationXML format') receivers = List.T(receiver.Receiver.T(), help='receiver coordinates if not read from file') data_paths = List.T(Path.T(), help='list of directories paths to search for data') events_path = Path.T( optional=True, help='limit processing to time windows around given events') event_time_window_factor = Float.T( default=2., help='controls length of time windows for event-wise processing') blacklist = List.T( String.T(), help='codes in the form NET.STA.LOC of receivers to be excluded') whitelist = List.T( String.T(), help='codes in the form NET.STA.LOC of receivers to be included') distance_max = Float.T(optional=True, help='receiver maximum distance from grid') tmin = Timestamp.T(optional=True, help='beginning of time interval to be processed') tmax = Timestamp.T(optional=True, help='end of time interval to be processed') run_path = Path.T(optional=True, help='output is saved to this directory') save_figures = Bool.T(default=False, help='flag to activate saving of detection figures') grid = grid.Grid.T( optional=True, help='definition of search grid, if not given, a default grid is ' 'chosen') autogrid_radius_factor = Float.T( default=1.5, help='size factor to use when automatically choosing a grid size') autogrid_density_factor = Float.T( default=10.0, help='grid density factor used when automatically choosing a grid ' 'spacing') image_function_contributions = List.T(ifc.IFC.T(), help='image function contributions') sharpness_normalization = Bool.T( default=True, help='whether to divide image function frames by their mean value') ifc_count_normalization = Bool.T( default=False, help='whether to divide image function by number of contributors') detector_threshold = Float.T(default=70., help='threshold on detector function') detector_tpeaksearch = Float.T(optional=True, help='search time span for peak detection') fill_incomplete_with_zeros = Bool.T( default=True, help='fill incomplete trace time windows with zeros ' '(and let edge effects ruin your day)') earthmodels = List.T(Earthmodel.T(), help='list of earthmodels usable in shifters') tabulated_phases = List.T( TPDef.T(), help='list of tabulated phase definitions usable shifters') cache_path = Path.T( default='lassie_phases.cache', help='directory where lassie stores tabulated phases etc.') stacking_blocksize = Int.T( optional=True, help='enable chunked stacking to reduce memory usage. Setting this to ' 'e.g. 64 will use ngridpoints * 64 * 8 bytes of memory to hold ' 'the stacking results, instead of computing the whole processing ' 'time window in one shot. Setting this to a very small number ' 'may lead to bad performance. If this is enabled together with ' 'plotting, the cutout of the image function seen in the map ' 'image must be stacked again just for plotting (redundantly and ' 'memory greedy) because it may intersect more than one ' 'processing chunk.') def __init__(self, *args, **kwargs): HasPaths.__init__(self, *args, **kwargs) self._receivers = None self._grid = None self._events = None self._config_name = 'untitled' def setup_image_function_contributions(self): ''' Post-init setup of image function contributors. ''' for ifc_ in self.image_function_contributions: ifc_.setup(self) def set_config_name(self, config_name): self._config_name = config_name def expand_path(self, path): def extra(path): return expand_template(path, dict(config_name=self._config_name)) return HasPaths.expand_path(self, path, extra=extra) def get_events_path(self): run_path = self.expand_path(self.run_path) return op.join(run_path, 'events.list') def get_ifm_dir_path(self): run_path = self.expand_path(self.run_path) return op.join(run_path, 'ifm') def get_ifm_path_template(self): return op.join(self.get_ifm_dir_path(), '%(station)s_%(tmin_ms)s.mseed') def get_detections_path(self): run_path = self.expand_path(self.run_path) return op.join(run_path, 'detections.list') def get_figures_path_template(self): run_path = self.expand_path(self.run_path) return op.join(run_path, 'figures', '%(id)s.%(format)s') def get_receivers(self): '''Aggregate receivers from different sources.''' fp = self.expand_path if self._receivers is None: self._receivers = list(self.receivers) if self.stations_path: for station in model.load_stations(fp(self.stations_path)): self._receivers.append( receiver.Receiver(codes=station.nsl(), lat=station.lat, lon=station.lon, z=station.depth)) if self.stations_stationxml_path: sx = stationxml.load_xml( filename=fp(self.stations_stationxml_path)) for station in sx.get_pyrocko_stations(): self._receivers.append( receiver.Receiver(codes=station.nsl(), lat=station.lat, lon=station.lon, z=station.depth)) return self._receivers def get_events(self): if self.events_path is None: return None if self._events is None: self._events = model.load_events(self.expand_path( self.events_path)) return self._events def get_grid(self): '''Get grid or make default grid.''' self.setup_image_function_contributions() if self._grid is None: if not self.grid: receivers = self.get_receivers() fsmooth_max = max(ifc.get_fsmooth() for ifc in self.image_function_contributions) vmin = min(ifc.shifter.get_vmin() for ifc in self.image_function_contributions) spacing = vmin / fsmooth_max / self.autogrid_density_factor lat0, lon0, north, east, depth = geo.bounding_box_square( *geo.points_coords(receivers), scale=self.autogrid_radius_factor) self._grid = grid.Carthesian3DGrid(lat=lat0, lon=lon0, xmin=north[0], xmax=north[1], dx=spacing, ymin=east[0], ymax=east[1], dy=spacing, zmin=depth[0], zmax=depth[1], dz=spacing) logger.info('automatic grid:\n%s' % self._grid) else: self._grid = self.grid self._grid.update() return self._grid
class DirContextEntry(Object): path = String.T() tstart = Timestamp.T() ifile = Int.T()
class SourceGenerator(LocationGenerator): nevents = Int.T(default=2) avoid_water = Bool.T( default=False, help='Avoid sources offshore under the ocean / lakes.') time_min = Timestamp.T(default=util.str_to_time('2017-01-01 00:00:00')) time_max = Timestamp.T(default=util.str_to_time('2017-01-03 00:00:00')) magnitude_min = Float.T(default=4.0, help='minimum moment magnitude') magnitude_max = Float.T( optional=True, help='if set, maximum moment magnitude for a uniform distribution. ' 'If set to ``None``, magnitudes are drawn using a ' 'Gutenberg-Richter distribution, see :gattr:`b_value`.') b_value = Float.T( optional=True, help='b-value for Gutenberg-Richter magnitude distribution. If unset, ' 'a value of 1 is assumed.') def __init__(self, *args, **kwargs): super(SourceGenerator, self).__init__(*args, **kwargs) if self.b_value is not None and self.magnitude_max is not None: raise ScenarioError( '%s: b_value and magnitude_max are mutually exclusive.' % self.__class__.__name__) def draw_magnitude(self, rstate): if self.b_value is None and self.magnitude_max is None: b_value = 1.0 else: b_value = self.b_value if b_value is None: return rstate.uniform(self.magnitude_min, self.magnitude_max) else: return moment_tensor.rand_to_gutenberg_richter( rstate.rand(), b_value, magnitude_min=self.magnitude_min) def get_sources(self): sources = [] for ievent in range(self.nevents): src = self.get_source(ievent) src.name = 'scenario_ev%03d' % (ievent + 1) sources.append(src) return sources def ensure_data(self, path): fn_sources = op.join(path, 'sources.yml') if not op.exists(fn_sources): with open(fn_sources, 'w') as f: for src in self.get_sources(): f.write(src.dump()) fn_events = op.join(path, 'events.txt') if not op.exists(fn_events): with open(fn_events, 'w') as f: for isrc, src in enumerate(self.get_sources()): f.write(src.pyrocko_event().dump()) def add_map_artists(self, automap): pass
class ScenePatch(Object): lat_center = Float.T(help='Center latitude anchor.') lon_center = Float.T(help='center longitude anchor.') time_master = Timestamp.T(help='Timestamp of the master.') time_slave = Timestamp.T(help='Timestamp of the slave.') inclination = Float.T( help='Orbital inclination towards the equatorial plane [deg].') apogee = Float.T(help='Apogee of the satellite in [m].') swath_width = Float.T(default=250 * km, help='Swath width in [m].') track_length = Float.T(help='Track length in [m].') incident_angle = Float.T(help='Ground incident angle in [deg].') resolution = Tuple.T(help='Resolution of raster in east x north [px].') orbital_node = StringChoice.T(['Ascending', 'Descending'], help='Orbit heading.') mask_water = Bool.T(default=True, help='Mask water bodies.') class SatelliteGeneratorTarget(gf.SatelliteTarget): def __init__(self, scene_patch, *args, **kwargs): gf.SatelliteTarget.__init__(self, *args, **kwargs) self.scene_patch = scene_patch def post_process(self, *args, **kwargs): resp = gf.SatelliteTarget.post_process(self, *args, **kwargs) from kite import Scene from kite.scene import SceneConfig, FrameConfig, Meta patch = self.scene_patch grid, _ = patch.get_grid() displacement = num.empty_like(grid) displacement.fill(num.nan) displacement[patch.get_mask()] = resp.result['displacement.los'] theta, phi = patch.get_incident_angles() llLat, llLon = patch.get_ll_anchor() urLat, urLon = patch.get_ur_anchor() dLon = num.abs(llLon - urLon) / patch.resolution[0] dLat = num.abs(llLat - urLat) / patch.resolution[1] scene_config = SceneConfig(meta=Meta( scene_title='Pyrocko Scenario Generator - {orbit} ({time})'. format(orbit=self.scene_patch.orbital_node, time=datetime.now()), orbital_node=patch.orbital_node, scene_id='pyrocko_scenario_%s' % self.scene_patch.orbital_node, satellite_name='Sentinel-1 (Scenario)'), frame=FrameConfig(llLat=float(llLat), llLon=float(llLon), dN=float(dLat), dE=float(dLon), spacing='degree')) scene = Scene(displacement=displacement, theta=theta, phi=phi, config=scene_config) resp.scene = scene return resp def __init__(self, *args, **kwargs): Object.__init__(self, *args, **kwargs) self._mask_water = None @property def width(self): track_shift = num.abs( num.cos(self.inclination * d2r) * self.track_length) return self.swath_width + track_shift def get_ll_anchor(self): return od.ne_to_latlon(self.lat_center, self.lon_center, -self.track_length / 2, -self.width / 2) def get_ur_anchor(self): return od.ne_to_latlon(self.lat_center, self.lon_center, self.track_length / 2, self.width / 2) def get_ul_anchor(self): return od.ne_to_latlon(self.lat_center, self.lon_center, self.track_length / 2, -self.width / 2) def get_lr_anchor(self): return od.ne_to_latlon(self.lat_center, self.lon_center, -self.track_length / 2, self.width / 2) def get_corner_coordinates(self): inc = self.inclination llLat, llLon = self.get_ll_anchor() urLat, urLon = self.get_ur_anchor() if self.orbital_node == 'Ascending': ulLat, ulLon = od.ne_to_latlon( self.lat_center, self.lon_center, self.track_length / 2, -num.tan(inc * d2r) * self.width / 2) lrLat, lrLon = od.ne_to_latlon(self.lat_center, self.lon_center, -self.track_length / 2, num.tan(inc * d2r) * self.width / 2) elif self.orbital_node == 'Descending': urLat, urLon = od.ne_to_latlon(self.lat_center, self.lon_center, self.track_length / 2, num.tan(inc * d2r) * self.width / 2) llLat, llLon = od.ne_to_latlon( self.lat_center, self.lon_center, -self.track_length / 2, -num.tan(inc * d2r) * self.width / 2) return ((llLat, llLon), (ulLat, ulLon), (urLat, urLon), (lrLat, lrLon)) def get_grid(self): '''Return relative positions of scatterer. :param track: Acquisition track, from `'asc'` or `'dsc'`. :type track: string ''' easts = num.linspace(0, self.width, self.resolution[0]) norths = num.linspace(0, self.track_length, self.resolution[1]) return num.meshgrid(easts, norths) def get_mask_track(self): east_shifts, north_shifts = self.get_grid() norths = north_shifts[:, 0] track = num.abs(num.cos(self.inclination * d2r)) * norths track_mask = num.logical_and( east_shifts > track[:, num.newaxis], east_shifts < (track + self.swath_width)[:, num.newaxis]) if self.orbital_node == 'Ascending': track_mask = num.fliplr(track_mask) return track_mask def get_mask_water(self): if self._mask_water is None: east_shifts, north_shifts = self.get_grid() east_shifts -= east_shifts[0, -1] / 2 north_shifts -= north_shifts[-1, -1] / 2 latlon = od.ne_to_latlon(self.lat_center, self.lon_center, north_shifts.ravel(), east_shifts.ravel()) points = num.array(latlon).T self._mask_water = get_gsshg().get_land_mask(points)\ .reshape(*east_shifts.shape) return self._mask_water def get_mask(self): mask_track = self.get_mask_track() if self.mask_water: mask_water = self.get_mask_water() return num.logical_and(mask_track, mask_water) return mask_track def get_incident_angles(self): # theta: elevation angle towards satellite from horizon in radians. # phi: Horizontal angle towards satellite' :abbr:`line of sight (LOS)` # in [rad] from East. east_shifts, _ = self.get_grid() phi = num.empty_like(east_shifts) theta = num.empty_like(east_shifts) east_shifts += num.tan(self.incident_angle * d2r) * self.apogee theta = num.arctan(east_shifts / self.apogee) if self.orbital_node == 'Ascending': phi.fill(self.inclination * d2r + num.pi / 2) elif self.orbital_node == 'Descending': phi.fill(2 * num.pi - (self.inclination * d2r + 3 / 2 * num.pi)) theta = num.fliplr(theta) else: raise AttributeError('Orbital node %s not defined!' % self.orbital_node) return theta, phi def get_target(self): gE, gN = self.get_grid() mask = self.get_mask() east_shifts = gE[mask].ravel() north_shifts = gN[mask].ravel() llLat, llLon = self.get_ll_anchor() ncoords = east_shifts.size theta, phi = self.get_incident_angles() theta = theta[mask].ravel() phi = phi[mask].ravel() if ncoords == 0: logger.warning('InSAR taget has no valid points,' ' maybe it\'s all water?') return return self.SatelliteGeneratorTarget(scene_patch=self, lats=num_full(ncoords, fill_value=llLat), lons=num_full(ncoords, fill_value=llLon), east_shifts=east_shifts, north_shifts=north_shifts, theta=theta, phi=phi)
class TimeQuantity(Object): value = Timestamp.T() uncertainty = Float.T(optional=True) lower_uncertainty = Float.T(optional=True) upper_uncertainty = Float.T(optional=True) confidence_level = Float.T(optional=True)
class A(Object): x = Choice.T(optional=True, choices=[Timestamp.T(), Int.T()])
class TimeWindow(Object): begin = Float.T() end = Float.T() reference = Timestamp.T()