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, Switch
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 get_source(self, event):
37 raise NotImplementedError()
39 def panel_visibility_changed(self, bool):
40 if bool:
41 if self._engine is None:
42 self.set_engine()
44 def set_engine(self):
45 self._engine = None
46 self.store_ids = self.get_store_ids()
47 if self.store_ids == []:
48 return
50 self.set_parameter_choices('store_id', self.store_ids)
51 self.store_id = self.store_ids[0]
53 def get_engine(self):
54 if not self._engine:
55 self._engine = gf.LocalEngine(use_config=True)
57 return self._engine
59 def get_store_ids(self):
60 return self.get_engine().get_store_ids()
62 def get_stf(self):
63 stf = dict(zip(self.stf_types, self.stf_instances))[self.stf_type]
64 if stf is not None:
65 stf.duration = self.stf_duration
67 return stf
69 def call(self):
70 '''
71 Main work routine of the snuffling.
72 '''
73 self.cleanup()
75 # get time range visible in viewer
76 viewer = self.get_viewer()
78 event = viewer.get_active_event()
79 if event:
80 event, stations = self.get_active_event_and_stations(
81 missing='warn')
82 else:
83 # event = model.Event(lat=self.lat, lon=self.lon)
84 event = model.Event(lat=0., lon=0.)
85 stations = []
87 stations = self.get_stations()
89 s2c = {}
90 for traces in self.chopper_selected_traces(fallback=True,
91 mode='visible'):
92 for tr in traces:
93 net, sta, loc, cha = tr.nslc_id
94 ns = net, sta
95 if ns not in s2c:
96 s2c[ns] = set()
98 s2c[ns].add((loc, cha))
100 if not stations:
101 stations = []
102 for (lat, lon) in [(5., 0.), (-5., 0.)]:
103 s = model.Station(station='(%g, %g)' % (lat, lon),
104 lat=lat, lon=lon)
105 stations.append(s)
106 viewer.add_stations(stations)
108 for s in stations:
109 ns = s.nsl()[:2]
110 if ns not in s2c:
111 s2c[ns] = set()
113 for cha in 'NEZ':
114 s2c[ns].add(('', cha))
116 source = self.get_source(event)
117 source.regularize()
119 m = EventMarker(source.pyrocko_event())
120 self.add_marker(m)
122 targets = []
124 if self.store_id == '<not loaded yet>':
125 self.fail('Select a GF Store first')
127 for station in stations:
129 nsl = station.nsl()
130 if nsl[:2] not in s2c:
131 continue
133 for loc, cha in s2c[nsl[:2]]:
135 target = gf.Target(
136 codes=(
137 station.network,
138 station.station,
139 loc + '-syn',
140 cha),
141 quantity='displacement',
142 lat=station.lat,
143 lon=station.lon,
144 depth=station.depth,
145 store_id=self.store_id,
146 optimization='enable',
147 interpolation='nearest_neighbor')
149 _, bazi = source.azibazi_to(target)
151 if cha.endswith('T'):
152 dip = 0.
153 azi = bazi + 270.
154 elif cha.endswith('R'):
155 dip = 0.
156 azi = bazi + 180.
157 elif cha.endswith('1'):
158 dip = 0.
159 azi = 0.
160 elif cha.endswith('2'):
161 dip = 0.
162 azi = 90.
163 else:
164 dip = None
165 azi = None
167 target.azimuth = azi
168 target.dip = dip
170 targets.append(target)
172 req = gf.Request(
173 sources=[source],
174 targets=targets)
176 req.regularize()
178 try:
179 resp = self.get_engine().process(req, nthreads=0)
180 except (gf.meta.OutOfBounds, gf.store_ext.StoreExtError)as e:
181 self.fail(e)
183 traces = resp.pyrocko_traces()
185 if self.waveform_type.startswith('Velocity'):
186 for tr in traces:
187 tr.set_ydata(num.diff(tr.ydata) / tr.deltat)
189 elif self.waveform_type.startswith('Acceleration'):
190 for tr in traces:
191 tr.set_ydata(num.diff(num.diff(tr.ydata)) / tr.deltat**2)
193 if self.waveform_type.endswith('[nm]') or \
194 self.waveform_type.endswith('[nm/s]') or \
195 self.waveform_type.endswith('[nm/s^2]'):
197 for tr in traces:
198 tr.set_ydata(tr.ydata * 1e9)
200 self.add_traces(traces)
202 def mechanism_from_event(self):
204 event = self.get_viewer().get_active_event()
206 if event is None:
207 self.fail('No active event set.')
209 if event.moment_tensor is not None:
210 strike, dip, slip_rake = event.moment_tensor\
211 .both_strike_dip_rake()[0]
212 moment = event.moment_tensor.scalar_moment()
213 self.set_parameter('magnitude',
214 moment_tensor.moment_to_magnitude(moment))
215 self.set_parameter('strike', strike)
216 self.set_parameter('dip', dip)
217 self.set_parameter('rake', slip_rake)
218 else:
219 self.warn(
220 'No source mechanism available for event %s. '
221 'Only setting location' % event.name)
223 if event.duration is not None:
224 self.set_parameter('stf_duration', event.duration)
225 else:
226 self.warn(
227 'No source duration available for event %s. ' % event.name)
229 self.set_parameter('lat', event.lat)
230 self.set_parameter('lon', event.lon)
231 self.set_parameter('depth_km', event.depth/km)
233 def add_store(self):
234 self._engine = self.get_engine()
235 superdir = self.input_directory()
236 if self.has_config(superdir):
237 self._engine.store_dirs.append(superdir)
238 else:
239 self._engine.store_superdirs.append(superdir)
240 self.store_ids = self._engine.get_store_ids()
242 self.set_parameter_choices('store_id', self.store_ids)
244 def has_config(self, directory):
245 return 'config' in os.listdir(directory)
248class DCSource(Seismosizer):
250 def setup(self):
251 '''Customization of the snuffling.'''
253 self.set_name('Seismosizer: DCSource')
254 self.add_parameter(
255 Param('Time', 'time', 0.0, -50., 50.))
256 # self.add_parameter(
257 # Param('Latitude', 'lat', 0.0, -90., 90.))
258 # self.add_parameter(
259 # Param('Longitude', 'lon', 0.0, -180., 180.))
260 self.add_parameter(
261 Param('North shift', 'north_km', 0.0, -50., 50.))
262 self.add_parameter(
263 Param('East shift', 'east_km', 0.0, -50., 50.))
264 self.add_parameter(
265 Param('Depth', 'depth_km', 10.0, 0.0, 600.0))
266 self.add_parameter(
267 Param('Magnitude', 'magnitude', 6.0, 0.0, 10.0))
268 self.add_parameter(
269 Param('Strike', 'strike', 0., -180., 180.))
270 self.add_parameter(
271 Param('Dip', 'dip', 90., 0., 90.))
272 self.add_parameter(
273 Param('Rake', 'rake', 0., -180., 180.))
274 self.add_parameter(
275 Param('STF duration', 'stf_duration', 0., 0., 20.))
276 self.add_parameter(
277 Choice('STF type', 'stf_type', self.stf_types[0], self.stf_types))
278 self.add_parameter(
279 Choice('GF Store', 'store_id',
280 '<not loaded yet>', ['<not loaded yet>']))
281 self.add_parameter(
282 Choice('Waveform type', 'waveform_type', 'Displacement [m]',
283 ['Displacement [m]',
284 'Displacement [nm]',
285 'Velocity [m/s]',
286 'Velocity [nm/s]',
287 'Acceleration [m/s^2]',
288 'Acceleration [nm/s^2]']))
290 self.add_trigger('Set Engine', self.set_engine)
291 self.add_trigger('Set Params from Event', self.mechanism_from_event)
292 self.add_trigger('Add Stores', self.add_store)
294 self.store_ids = None
295 self.offline_config = None
296 self._engine = None
298 def get_source(self, event):
299 return gf.DCSource(
300 time=event.time+self.time,
301 lat=event.lat,
302 lon=event.lon,
303 north_shift=self.north_km*km,
304 east_shift=self.east_km*km,
305 depth=self.depth_km*km,
306 magnitude=self.magnitude,
307 strike=self.strike,
308 dip=self.dip,
309 rake=self.rake,
310 stf=self.get_stf())
313class RectangularSource(Seismosizer):
315 def setup(self):
316 '''Customization of the snuffling.'''
318 self.set_name('Seismosizer: RectangularSource')
319 self.add_parameter(
320 Param('Time', 'time', 0.0, -50., 50.))
321 # self.add_parameter(
322 # Param('Latitude', 'lat', 0.0, -90., 90.))
323 # self.add_parameter(
324 # Param('Longitude', 'lon', 0.0, -180., 180.))
325 self.add_parameter(
326 Param('North shift', 'north_km', 0.0, -50., 50.))
327 self.add_parameter(
328 Param('East shift', 'east_km', 0.0, -50., 50.))
329 self.add_parameter(
330 Param('Depth', 'depth_km', 10.0, 0.0, 600.0))
331 self.add_parameter(
332 Param('Magnitude', 'magnitude', 6.0, 0.0, 10.0))
333 self.add_parameter(
334 Param('Strike', 'strike', 0., -180., 180.))
335 self.add_parameter(
336 Param('Dip', 'dip', 90., 0., 90.))
337 self.add_parameter(
338 Param('Rake', 'rake', 0., -180., 180.))
339 self.add_parameter(
340 Param('Length', 'length', 10.*km, .1*km, 100*km))
341 self.add_parameter(
342 Param('Width', 'width', 5.*km, .1*km, 50*km))
343 self.add_parameter(
344 Param('Nucleation X', 'nucleation_x', 0., -1., 1.))
345 self.add_parameter(
346 Param('Nucleation Y', 'nucleation_y', 0., -1., 1.))
347 self.add_parameter(
348 Param('Rupture velocity', 'velocity', 3500.0, 0.0, 5000.0))
349 self.add_parameter(
350 Param('STF duration', 'stf_duration', 0., 0., 20.))
351 self.add_parameter(
352 Choice('STF type', 'stf_type', self.stf_types[0], self.stf_types))
353 self.add_parameter(
354 Choice('GF Store', 'store_id',
355 '<not loaded yet>', ['<not loaded yet>']))
356 self.add_parameter(
357 Choice('Waveform type', 'waveform_type', 'Displacement [m]',
358 ['Displacement [m]',
359 'Displacement [nm]',
360 'Velocity [m/s]',
361 'Velocity [nm/s]',
362 'Acceleration [m/s^2]',
363 'Acceleration [nm/s^2]']))
365 self.add_trigger('Set Engine', self.set_engine)
366 self.add_trigger('Set Params from Event', self.mechanism_from_event)
367 self.add_trigger('Add Stores', self.add_store)
369 self.store_ids = None
370 self.offline_config = None
371 self._engine = None
373 def get_source(self, event):
374 return gf.RectangularSource(
375 time=event.time+self.time,
376 lat=event.lat,
377 lon=event.lon,
378 north_shift=self.north_km*km,
379 east_shift=self.east_km*km,
380 depth=self.depth_km*km,
381 magnitude=self.magnitude,
382 strike=self.strike,
383 dip=self.dip,
384 rake=self.rake,
385 length=self.length,
386 width=self.width,
387 nucleation_x=self.nucleation_x,
388 nucleation_y=self.nucleation_y,
389 velocity=self.velocity,
390 stf=self.get_stf())
393class PseudoDynamicRuptureSource(Seismosizer):
395 def setup(self):
396 '''Customization of the snuffling.'''
398 self.set_name('Seismosizer: PseudoDynamicRupture')
399 self.add_parameter(
400 Param('Time', 'time', 0.0, -50., 50.))
401 # self.add_parameter(
402 # Param('Latitude', 'lat', 0.0, -90., 90.))
403 # self.add_parameter(
404 # Param('Longitude', 'lon', 0.0, -180., 180.))
405 self.add_parameter(
406 Param('North shift', 'north_km', 0.0, -50., 50.))
407 self.add_parameter(
408 Param('East shift', 'east_km', 0.0, -50., 50.))
409 self.add_parameter(
410 Param('Depth', 'depth_km', 10.0, 0.0, 600.0))
411 self.add_parameter(
412 Param('Magnitude', 'magnitude', 6.0, 0.0, 10.0))
413 self.add_parameter(
414 Param('Strike', 'strike', 0., -180., 180.))
415 self.add_parameter(
416 Param('Dip', 'dip', 90., 0., 90.))
417 self.add_parameter(
418 Param('Rake', 'rake', 0., -180., 180.))
419 self.add_parameter(
420 Param('Length', 'length', 10.*km, .1*km, 100*km))
421 self.add_parameter(
422 Param('Width', 'width', 5.*km, .1*km, 50*km))
423 self.add_parameter(
424 Param('Nucleation X', 'nucleation_x', 0., -1., 1.))
425 self.add_parameter(
426 Param('Nucleation Y', 'nucleation_y', 0., -1., 1.))
427 self.add_parameter(
428 Param('Gamma', 'gamma', 0.8, 0.5, 1.5))
429 self.add_parameter(
430 Param('nx', 'nx', 5, 1, 20))
431 self.add_parameter(
432 Param('ny', 'ny', 5, 1, 20))
433 self.add_parameter(
434 Param('STF duration', 'stf_duration', 0., 0., 20.))
435 self.add_parameter(
436 Choice('STF type', 'stf_type', self.stf_types[0], self.stf_types))
438 self.add_parameter(Switch(
439 'Tapered tractions', 'tapered', True))
441 self.add_parameter(
442 Choice('GF Store', 'store_id',
443 '<not loaded yet>', ['<not loaded yet>']))
444 self.add_parameter(
445 Choice('Waveform type', 'waveform_type', 'Displacement [m]',
446 ['Displacement [m]',
447 'Displacement [nm]',
448 'Velocity [m/s]',
449 'Velocity [nm/s]',
450 'Acceleration [m/s^2]',
451 'Acceleration [nm/s^2]']))
453 self.add_trigger('Set Engine', self.set_engine)
454 self.add_trigger('Set Params from Event', self.mechanism_from_event)
455 self.add_trigger('Add Stores', self.add_store)
457 self.store_ids = None
458 self.offline_config = None
459 self._engine = None
461 def get_source(self, event):
462 source = gf.PseudoDynamicRupture(
463 time=event.time + self.time,
464 lat=event.lat,
465 lon=event.lon,
466 nx=int(self.nx),
467 ny=int(self.ny),
468 north_shift=self.north_km*km,
469 east_shift=self.east_km*km,
470 depth=self.depth_km*km,
471 magnitude=self.magnitude,
472 strike=self.strike,
473 dip=self.dip,
474 rake=self.rake,
475 length=self.length,
476 width=self.width,
477 nucleation_x=self.nucleation_x,
478 nucleation_y=self.nucleation_y,
479 gamma=self.gamma,
480 stf=self.get_stf(),
482 nthreads=5,
483 pure_shear=True,
484 smooth_rupture=False)
486 return source
489def __snufflings__():
490 '''Returns a list of snufflings to be exported by this module.'''
491 return [DCSource(), RectangularSource(), PseudoDynamicRuptureSource()]