You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
siria/src/si/ids/targets/waveform.py

306 lines
9.1 KiB
Python

# GPLv3
#
# The Developers, 21st Century
import copy
import logging
import numpy as num
from pyrocko import orthodrome as pod, gf, marker
from pyrocko.guts import Float, List, String, Tuple, StringChoice, Object
from pyrocko.io import stationxml
from ewrica import util
from .base import TargetGroupConfig, TargetGroup
logger = logging.getLogger('ewrica.si.ids.targets.waveform')
km = 1e3
quantity2unit = dict([
('displacement', 'M'),
('velocity', 'M/S'),
('acceleration', 'M/S**2')])
class WaveformQuantities(StringChoice):
choices = ['displacement', 'velocity', 'acceleration']
class ComponentWeight(Object):
north = Float.T(default=1.)
east = Float.T(default=1.)
up = Float.T(default=1.)
def get_weight(self, channel):
if channel[-1] == 'N':
return self.north
elif channel[-1] == 'E':
return self.east
elif channel[-1] == 'Z':
return self.up
else:
logger.warning(
'Channel %s is not valid. Last channel id letter '
'should be "Z", N" or "E". Weight of 0 assigned', channel)
return 0.
class RestitutionConfig(Object):
frequency_limits = Tuple.T(4, Float.T(), default=(0.001, 0.05, 100., 200.))
t_fade = Float.T(optional=True)
t_min = Float.T(
default=-60.,
help='start time of raw waveforms relative event origin time')
t_max = Float.T(
default=100.,
help='end time of raw waveforms relative event origin time')
target_quantity = WaveformQuantities.T(optional=True)
@staticmethod
def example():
return RestitutionConfig()
@property
def target_unit(self):
if self.target_quantity is not None:
return quantity2unit[self.target_quantity]
return None
class WaveformTargetGroupConfig(TargetGroupConfig):
distance_min = Float.T(
default=0.,
help='Minimum desired origin station distance in [m].')
distance_max = Float.T(
default=100. * km,
help='Maximum desired origin station distance in [m].')
ppicks_paths__ = List.T(
String.T(),
optional=True,
help='P-wave arrival picks file in snuffler marker format.')
waveform_paths__ = List.T(
String.T(),
default=['.'],
help='Paths to waveform directories.')
response_stationxml_paths__ = List.T(
String.T(),
default=['response.xml'],
help='Paths to station response files')
pphase = String.T(
optional=True,
help='Phase id of the P phase arrival of the given store (needed, if '
'no picks are provided.')
store_id = String.T(
optional=True,
help='Phase id of the P phase arrival of the given store (needed, if '
'no picks are provided.')
component_weights = ComponentWeight.T(
default=ComponentWeight(),
help='Weight of the different station channels.')
restitution_config = RestitutionConfig.T(
default=RestitutionConfig(),
help='Waveform restitution settings.')
t_prearrival = Float.T(
default=-15.,
help='data starting from time in [sec] relative to P arrivals is '
'included in dat files for baseline corrections.')
t_length = Float.T(
optional=True,
help='If given, all traces are cutted to given length in [s].')
static_weight = Float.T(
default=0.,
help='Weight of the static offsets (baseline shifts).')
def __init__(self, *args, **kwargs):
TargetGroupConfig.__init__(self, *args, **kwargs)
self._blocked = []
self._allowed = []
self._stations = []
@staticmethod
def example():
return WaveformTargetGroupConfig(
blocklist_paths=['path/to/blocklist.txt'],
blocklist=['AB.BLOCK'],
allowlist_paths=['path/to/allowlist.txt'],
allowlist=['AB.ALLOW'],
restitution_config=RestitutionConfig.example(),
waveform_paths=[
'relative/path/to/waveforms1', 'relative/path/to/waveforms1'],
response_stationxml_paths=[
'path/to/responsefile1.xml', 'path/to/responsefile2.xml'])
@property
def blocked(self):
if not self._blocked:
blocklist = copy.deepcopy(self.blocklist) or []
if self.blocklist_paths:
blocklist += [
str(s) for p in self.blocklist_paths
for s in num.loadtxt(p, dtype='<U20')]
self._blocked = list(num.unique(blocklist))
return self._blocked
@property
def allowed(self):
if not self._allowed:
allowlist = copy.deepcopy(self.allowlist) or []
if self.allowlist_paths:
allowlist += [
str(s) for p in self.allowlist_paths
for s in num.loadtxt(p, dtype='<U20')]
self._allowed = list(num.unique(allowlist))
return self._allowed
@property
def ppicks_path(self):
return self._expand_paths(self.ppicks_path__)
@ppicks_path.setter
def ppicks_path(self, ppicks_path):
self.ppicks_path__ = ppicks_path
@property
def waveform_paths(self):
return self._expand_paths(self.waveform_paths__)
@waveform_paths.setter
def waveform_paths(self, waveform_paths):
self.waveform_paths__ = waveform_paths
@property
def response_stationxml_paths(self):
return self._expand_paths(self.response_stationxml_paths__)
@response_stationxml_paths.setter
def response_stationxml_paths(self, response_stationxml_paths):
self.response_stationxml_paths__ = response_stationxml_paths
class WaveformTargetGroup(TargetGroup):
config = WaveformTargetGroupConfig.T(default=WaveformTargetGroupConfig())
def __init__(self, *args, **kwargs):
TargetGroup.__init__(self, *args, **kwargs)
self._p_arrivals = {}
self._stations = []
self._sx = None
@property
def sx(self):
if self._sx is None:
sxs = []
for f in self.config.response_stationxml_paths:
sxs.append(stationxml.load_xml(filename=f))
self._sx = stationxml.primitive_merge(sxs)
return self._sx
@property
def stations(self):
if not self._stations:
stations = self.sx.get_pyrocko_stations()
stations = util.keep_allowlisted_stations(
stations, self.config.allowed)
stations = util.remove_blocklisted_stations(
stations, self.config.blocked)
self._stations = stations
return self._stations
def _event_station_distance(self, event):
return pod.distance_accurate50m_numpy(
event.lat, event.lon,
[s.lat for s in self.stations], [s.lon for s in self.stations])
def p_arrivals(self, event):
if self._p_arrivals:
return self._p_arrivals
wf_conf = self.config
en_conf = wf_conf._parent.engine_config
arrivals = []
if (wf_conf.ppicks_paths is not None and wf_conf.pphase is None and
wf_conf.store_id is None):
for fn in wf_conf.ppicks_paths:
arrivals += marker.load_markers(fn)
elif (wf_conf.ppicks_paths is None and wf_conf.pphase is not None and
wf_conf.store_id is not None):
engine = gf.LocalEngine(
store_superdirs=en_conf.pyrocko_store_superdirs)
store = engine.get_store(wf_conf.store_id)
markers = []
for sta in self.stations:
try:
arr = store.t(wf_conf.pphase, event, sta) + event.time
markers.append(marker.Marker(
tmin=arr, tmax=arr,
nslc_ids=[
sta.nsl() + (c,) for c in ['*E', '*N', '*Z']]))
except gf.store.NoSuchPhase as e:
raise e
except gf.meta.OutOfBounds as e:
logger.warn(
'Got error \n {} \n for station {}. '
'It is excluded'.format(e, sta.nsl()))
arrivals += markers
else:
raise ValueError('Argument "ppicks_paths" is mutually exclusive '
'with "pphase" and "store_id".')
arrivals = {(ar.nslc_ids[0][:3]): ar for ar in arrivals}
self._p_arrivals = arrivals
return self._p_arrivals
def get_targets(self, event, *args, **kwargs):
dist = self._event_station_distance(event)
return [
s for s, d in zip(self.stations, dist)
if d >= self.config.distance_min and d <= self.config.distance_max]
def get_dataset(self):
raise NotImplementedError('Method not implemented')
def dump_ids_input(self, path):
pass