1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6import numpy as num
7import os
9from pyrocko import moment_tensor, model
10from pyrocko.gui.snuffling import Snuffling, Param, Choice, EventMarker
11from pyrocko import gf
13km = 1000.
16class Seismosizer(Snuffling):
17 '''
18 Generate synthetic traces on the fly
19 ====================================
21 Activate an event (press `e`) to generate synthetic waveforms for it.
22 If no stations have been loaded pripor to execution, two stations will be
23 generated at lat/lon = (5., 0) and (-5., 0.).
25 All geometrical units are kilometers (if not stated otherwise).
26 `GF Stores` will be loaded on start from `gf_store_superdirs` defined
27 in your pyrocko config file located at `$HOME/.pyrocko/config.pf`.
28 '''
30 def __init__(self):
31 Snuffling.__init__(self)
32 self.stf_types = ['half sin', 'triangular', 'boxcar', 'None']
33 self.stf_instances = [gf.HalfSinusoidSTF(), gf.TriangularSTF(),
34 gf.BoxcarSTF(), None]
36 def setup(self):
37 '''
38 Customization of the snuffling.
39 '''
41 self.set_name('Seismosizer')
42 self.add_parameter(
43 Param('Time', 'time', 0.0, -50., 50.))
44 # self.add_parameter(
45 # Param('Latitude', 'lat', 0.0, -90., 90.))
46 # self.add_parameter(
47 # Param('Longitude', 'lon', 0.0, -180., 180.))
48 self.add_parameter(
49 Param('North shift', 'north_km', 0.0, -50., 50.))
50 self.add_parameter(
51 Param('East shift', 'east_km', 0.0, -50., 50.))
52 self.add_parameter(
53 Param('Depth', 'depth_km', 10.0, 0.0, 600.0))
54 self.add_parameter(
55 Param('Magnitude', 'magnitude', 6.0, 0.0, 10.0))
56 self.add_parameter(
57 Param('Strike', 'strike', 0., -180., 180.))
58 self.add_parameter(
59 Param('Dip', 'dip', 90., 0., 90.))
60 self.add_parameter(
61 Param('Rake', 'rake', 0., -180., 180.))
62 self.add_parameter(
63 Param('Length', 'length', 0., 0., 1000*km))
64 self.add_parameter(
65 Param('Width', 'width', 0., 0., 500*km))
66 self.add_parameter(
67 Param('Nucleation X', 'nucleation_x', -1., -1., 1.))
68 self.add_parameter(
69 Param('Rupture velocity', 'velocity', 3500.0, 0.0, 5000.0))
70 self.add_parameter(
71 Param('STF duration', 'stf_duration', 0., 0., 20.))
72 self.add_parameter(
73 Choice('STF type', 'stf_type', self.stf_types[0], self.stf_types))
74 self.add_parameter(
75 Choice('GF Store', 'store_id',
76 '<not loaded yet>', ['<not loaded yet>']))
77 self.add_parameter(
78 Choice('Waveform type', 'waveform_type', 'Displacement [m]',
79 ['Displacement [m]',
80 'Displacement [nm]',
81 'Velocity [m/s]',
82 'Velocity [nm/s]',
83 'Acceleration [m/s^2]',
84 'Acceleration [nm/s^2]']))
86 self.add_trigger('Set Engine', self.set_engine)
87 self.add_trigger('Set Params from Event', self.mechanism_from_event)
88 self.add_trigger('Add Stores', self.add_store)
90 self.store_ids = None
91 self.offline_config = None
92 self._engine = None
94 def panel_visibility_changed(self, bool):
95 if bool:
96 if self._engine is None:
97 self.set_engine()
99 def set_engine(self):
100 self._engine = None
101 self.store_ids = self.get_store_ids()
102 if self.store_ids == []:
103 return
105 self.set_parameter_choices('store_id', self.store_ids)
106 self.store_id = self.store_ids[0]
108 def get_engine(self):
109 if not self._engine:
110 self._engine = gf.LocalEngine(use_config=True)
112 return self._engine
114 def get_store_ids(self):
115 return self.get_engine().get_store_ids()
117 def get_stf(self):
118 stf = dict(zip(self.stf_types, self.stf_instances))[self.stf_type]
119 if stf is not None:
120 stf.duration = self.stf_duration
122 return stf
124 def call(self):
125 '''
126 Main work routine of the snuffling.
127 '''
128 self.cleanup()
130 # get time range visible in viewer
131 viewer = self.get_viewer()
133 event = viewer.get_active_event()
134 if event:
135 event, stations = self.get_active_event_and_stations(
136 missing='warn')
137 else:
138 # event = model.Event(lat=self.lat, lon=self.lon)
139 event = model.Event(lat=0., lon=0.)
140 stations = []
142 stations = self.get_stations()
144 s2c = {}
145 for traces in self.chopper_selected_traces(fallback=True,
146 mode='visible'):
147 for tr in traces:
148 net, sta, loc, cha = tr.nslc_id
149 ns = net, sta
150 if ns not in s2c:
151 s2c[ns] = set()
153 s2c[ns].add((loc, cha))
155 if not stations:
156 stations = []
157 for (lat, lon) in [(5., 0.), (-5., 0.)]:
158 s = model.Station(station='(%g, %g)' % (lat, lon),
159 lat=lat, lon=lon)
160 stations.append(s)
161 viewer.add_stations(stations)
163 for s in stations:
164 ns = s.nsl()[:2]
165 if ns not in s2c:
166 s2c[ns] = set()
168 for cha in 'NEZ':
169 s2c[ns].add(('', cha))
171 source = gf.RectangularSource(
172 time=event.time+self.time,
173 lat=event.lat,
174 lon=event.lon,
175 north_shift=self.north_km*km,
176 east_shift=self.east_km*km,
177 depth=self.depth_km*km,
178 magnitude=self.magnitude,
179 strike=self.strike,
180 dip=self.dip,
181 rake=self.rake,
182 length=self.length,
183 width=self.width,
184 nucleation_x=self.nucleation_x,
185 velocity=self.velocity,
186 stf=self.get_stf())
188 source.regularize()
190 m = EventMarker(source.pyrocko_event())
191 self.add_marker(m)
193 targets = []
195 if self.store_id == '<not loaded yet>':
196 self.fail('Select a GF Store first')
198 for station in stations:
200 nsl = station.nsl()
201 if nsl[:2] not in s2c:
202 continue
204 for loc, cha in s2c[nsl[:2]]:
206 target = gf.Target(
207 codes=(
208 station.network,
209 station.station,
210 loc + '-syn',
211 cha),
212 quantity='displacement',
213 lat=station.lat,
214 lon=station.lon,
215 depth=station.depth,
216 store_id=self.store_id,
217 optimization='enable',
218 interpolation='nearest_neighbor')
220 _, bazi = source.azibazi_to(target)
222 if cha.endswith('T'):
223 dip = 0.
224 azi = bazi + 270.
225 elif cha.endswith('R'):
226 dip = 0.
227 azi = bazi + 180.
228 elif cha.endswith('1'):
229 dip = 0.
230 azi = 0.
231 elif cha.endswith('2'):
232 dip = 0.
233 azi = 90.
234 else:
235 dip = None
236 azi = None
238 target.azimuth = azi
239 target.dip = dip
241 targets.append(target)
243 req = gf.Request(
244 sources=[source],
245 targets=targets)
247 req.regularize()
249 try:
250 resp = self.get_engine().process(req)
251 except (gf.meta.OutOfBounds, gf.store_ext.StoreExtError)as e:
252 self.fail(e)
254 traces = resp.pyrocko_traces()
256 if self.waveform_type.startswith('Velocity'):
257 for tr in traces:
258 tr.set_ydata(num.diff(tr.ydata) / tr.deltat)
260 elif self.waveform_type.startswith('Acceleration'):
261 for tr in traces:
262 tr.set_ydata(num.diff(num.diff(tr.ydata)) / tr.deltat**2)
264 if self.waveform_type.endswith('[nm]') or \
265 self.waveform_type.endswith('[nm/s]') or \
266 self.waveform_type.endswith('[nm/s^2]'):
268 for tr in traces:
269 tr.set_ydata(tr.ydata * 1e9)
271 self.add_traces(traces)
273 def mechanism_from_event(self):
275 event = self.get_viewer().get_active_event()
277 if event is None:
278 self.fail('No active event set.')
280 if event.moment_tensor is not None:
281 strike, dip, slip_rake = event.moment_tensor\
282 .both_strike_dip_rake()[0]
283 moment = event.moment_tensor.scalar_moment()
284 self.set_parameter('magnitude',
285 moment_tensor.moment_to_magnitude(moment))
286 self.set_parameter('strike', strike)
287 self.set_parameter('dip', dip)
288 self.set_parameter('rake', slip_rake)
289 else:
290 self.warn(
291 'No source mechanism available for event %s. '
292 'Only setting location' % event.name)
294 self.set_parameter('lat', event.lat)
295 self.set_parameter('lon', event.lon)
296 self.set_parameter('depth_km', event.depth/km)
298 def add_store(self):
299 self._engine = self.get_engine()
300 superdir = self.input_directory()
301 if self.has_config(superdir):
302 self._engine.store_dirs.append(superdir)
303 else:
304 self._engine.store_superdirs.append(superdir)
305 self.store_ids = self._engine.get_store_ids()
307 self.set_parameter_choices('store_id', self.store_ids)
309 def has_config(self, directory):
310 return 'config' in os.listdir(directory)
313def __snufflings__():
314 '''
315 Returns a list of snufflings to be exported by this module.
316 '''
317 return [Seismosizer()]