Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/gui/snuffler/snufflings/seismosizer.py: 46%
229 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-04 09:52 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-04 09:52 +0000
1# https://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 ..snuffling import Snuffling, Param, Choice, Switch, EventMarker
11from pyrocko import gf
13km = 1000.
16class Seismosizer(Snuffling):
18 def help(self):
19 return '''
20Generate synthetic traces on the fly
21====================================
23Activate an event (press `e`) to generate synthetic waveforms for it.
24If no stations have been loaded pripor to execution, two stations will be
25generated at lat/lon = (5., 0) and (-5., 0.).
27All geometrical units are kilometers (if not stated otherwise).
28`GF Stores` will be loaded on start from `gf_store_superdirs` defined
29in your pyrocko config file located at `$HOME/.pyrocko/config.pf`.
30'''
32 def __init__(self):
33 Snuffling.__init__(self)
34 self.stf_types = ['half sin', 'triangular', 'boxcar', 'None']
35 self.stf_instances = [gf.HalfSinusoidSTF(), gf.TriangularSTF(),
36 gf.BoxcarSTF(), None]
38 def get_source(self, event):
39 raise NotImplementedError()
41 def panel_visibility_changed(self, bool):
42 if bool:
43 if self._engine is None:
44 self.set_engine()
46 def set_engine(self):
47 self._engine = None
48 self.store_ids = self.get_store_ids()
49 if self.store_ids == []:
50 return
52 self.set_parameter_choices('store_id', self.store_ids)
53 self.store_id = self.store_ids[0]
55 def get_engine(self):
56 if not self._engine:
57 self._engine = gf.LocalEngine(use_config=True)
59 return self._engine
61 def get_store_ids(self):
62 return self.get_engine().get_store_ids()
64 def get_stf(self):
65 stf = dict(zip(self.stf_types, self.stf_instances))[self.stf_type]
66 if stf is not None:
67 stf.duration = self.stf_duration
69 return stf
71 def setup(self):
72 self.add_parameter(
73 Choice('GF Store', 'store_id',
74 '<not loaded yet>', ['<not loaded yet>']))
75 self.add_parameter(
76 Choice('Waveform type', 'waveform_type', 'Displacement [m]',
77 ['Displacement [m]',
78 'Displacement [nm]',
79 'Velocity [m/s]',
80 'Velocity [nm/s]',
81 'Acceleration [m/s^2]',
82 'Acceleration [nm/s^2]']))
84 self.add_trigger('Set Engine', self.set_engine)
85 self.add_trigger('Set Params from Event', self.mechanism_from_event)
86 self.add_trigger('Add Stores', self.add_store)
88 self.store_ids = None
89 self.offline_config = None
90 self._engine = None
92 def call(self):
93 '''
94 Main work routine of the snuffling.
95 '''
96 self.cleanup()
98 # get time range visible in viewer
99 viewer = self.get_viewer()
101 event = viewer.get_active_event()
102 if event:
103 event, stations = self.get_active_event_and_stations(
104 missing='warn')
105 else:
106 # event = model.Event(lat=self.lat, lon=self.lon)
107 event = model.Event(lat=0., lon=0.)
108 stations = []
110 stations = self.get_stations()
112 s2c = {}
113 for traces in self.chopper_selected_traces(fallback=True,
114 mode='visible'):
115 for tr in traces:
116 net, sta, loc, cha = tr.nslc_id
117 ns = net, sta
118 if ns not in s2c:
119 s2c[ns] = set()
121 s2c[ns].add((loc, cha))
123 if not stations:
124 stations = []
125 for (lat, lon) in [(5., 0.), (-5., 0.)]:
126 s = model.Station(station='(%g, %g)' % (lat, lon),
127 lat=lat, lon=lon)
128 stations.append(s)
129 viewer.add_stations(stations)
131 for s in stations:
132 ns = s.nsl()[:2]
133 if ns not in s2c:
134 s2c[ns] = set()
136 for cha in 'NEZ':
137 s2c[ns].add(('', cha))
139 source = self.get_source(event)
140 source.regularize()
142 m = EventMarker(source.pyrocko_event())
143 self.add_marker(m)
145 targets = []
147 if self.store_id == '<not loaded yet>':
148 self.fail('Select a GF Store first')
150 for station in stations:
152 nsl = station.nsl()
153 if nsl[:2] not in s2c:
154 continue
156 for loc, cha in s2c[nsl[:2]]:
158 target = gf.Target(
159 codes=(
160 station.network,
161 station.station,
162 loc + '-syn',
163 cha),
164 quantity='displacement',
165 lat=station.lat,
166 lon=station.lon,
167 depth=station.depth,
168 store_id=self.store_id,
169 optimization='enable',
170 interpolation='nearest_neighbor')
172 _, bazi = source.azibazi_to(target)
174 if cha.endswith('T'):
175 dip = 0.
176 azi = bazi + 270.
177 elif cha.endswith('R'):
178 dip = 0.
179 azi = bazi + 180.
180 elif cha.endswith('1'):
181 dip = 0.
182 azi = 0.
183 elif cha.endswith('2'):
184 dip = 0.
185 azi = 90.
186 else:
187 dip = None
188 azi = None
190 target.azimuth = azi
191 target.dip = dip
193 targets.append(target)
195 req = gf.Request(
196 sources=[source],
197 targets=targets)
199 req.regularize()
201 try:
202 resp = self.get_engine().process(req, nthreads=0)
203 except (gf.meta.OutOfBounds, gf.store_ext.StoreExtError)as e:
204 self.fail(e)
206 traces = resp.pyrocko_traces()
208 if self.waveform_type.startswith('Velocity'):
209 for tr in traces:
210 tr.set_ydata(num.diff(tr.ydata) / tr.deltat)
212 elif self.waveform_type.startswith('Acceleration'):
213 for tr in traces:
214 tr.set_ydata(num.diff(num.diff(tr.ydata)) / tr.deltat**2)
216 if self.waveform_type.endswith('[nm]') or \
217 self.waveform_type.endswith('[nm/s]') or \
218 self.waveform_type.endswith('[nm/s^2]'):
220 for tr in traces:
221 tr.set_ydata(tr.ydata * 1e9)
223 self.add_traces(traces)
225 def mechanism_from_event(self):
227 event = self.get_viewer().get_active_event()
229 if event is None:
230 self.fail('No active event set.')
232 if event.moment_tensor is not None:
233 strike, dip, slip_rake = event.moment_tensor\
234 .both_strike_dip_rake()[0]
235 moment = event.moment_tensor.scalar_moment()
236 self.set_parameter('magnitude',
237 moment_tensor.moment_to_magnitude(moment))
238 self.set_parameter('strike', strike)
239 self.set_parameter('dip', dip)
240 self.set_parameter('rake', slip_rake)
241 else:
242 self.warn(
243 'No source mechanism available for event %s. '
244 'Only setting location' % event.name)
246 if event.duration is not None:
247 self.set_parameter('stf_duration', event.duration)
248 else:
249 self.warn(
250 'No source duration available for event %s. ' % event.name)
252 self.set_parameter('lat', event.lat)
253 self.set_parameter('lon', event.lon)
254 self.set_parameter('depth_km', event.depth/km)
256 def add_store(self):
257 self._engine = self.get_engine()
258 superdir = self.input_directory()
259 if self.has_config(superdir):
260 self._engine.store_dirs.append(superdir)
261 else:
262 self._engine.store_superdirs.append(superdir)
263 self.store_ids = self._engine.get_store_ids()
265 self.set_parameter_choices('store_id', self.store_ids)
267 def has_config(self, directory):
268 return 'config' in os.listdir(directory)
271class DCSource(Seismosizer):
273 def setup(self):
274 '''Customization of the snuffling.'''
276 self.set_name('Seismosizer: DCSource')
277 self.add_parameter(
278 Param('Time', 'time', 0.0, -50., 50.))
279 # self.add_parameter(
280 # Param('Latitude', 'lat', 0.0, -90., 90.))
281 # self.add_parameter(
282 # Param('Longitude', 'lon', 0.0, -180., 180.))
283 self.add_parameter(
284 Param('North shift', 'north_km', 0.0, -50., 50.))
285 self.add_parameter(
286 Param('East shift', 'east_km', 0.0, -50., 50.))
287 self.add_parameter(
288 Param('Depth', 'depth_km', 10.0, -100.0, 600.0))
289 self.add_parameter(
290 Param('Magnitude', 'magnitude', 6.0, 0.0, 10.0))
291 self.add_parameter(
292 Param('Strike', 'strike', 0., -180., 180.))
293 self.add_parameter(
294 Param('Dip', 'dip', 90., 0., 90.))
295 self.add_parameter(
296 Param('Rake', 'rake', 0., -180., 180.))
297 self.add_parameter(
298 Param('STF duration', 'stf_duration', 0., 0., 20.))
299 self.add_parameter(
300 Choice('STF type', 'stf_type', self.stf_types[0], self.stf_types))
302 Seismosizer.setup(self)
304 def get_source(self, event):
305 return gf.DCSource(
306 time=event.time+self.time,
307 lat=event.lat,
308 lon=event.lon,
309 north_shift=self.north_km*km,
310 east_shift=self.east_km*km,
311 depth=self.depth_km*km,
312 magnitude=self.magnitude,
313 strike=self.strike,
314 dip=self.dip,
315 rake=self.rake,
316 stf=self.get_stf())
319class SFSource(Seismosizer):
320 def setup(self):
321 '''Customization of the snuffling.'''
323 self.set_name('Seismosizer: SFSource')
324 self.add_parameter(
325 Param('Time', 'time', 0.0, -50., 50.))
326 # self.add_parameter(
327 # Param('Latitude', 'lat', 0.0, -90., 90.))
328 # self.add_parameter(
329 # Param('Longitude', 'lon', 0.0, -180., 180.))
330 self.add_parameter(
331 Param('North shift', 'north_km', 0.0, -50., 50.))
332 self.add_parameter(
333 Param('East shift', 'east_km', 0.0, -50., 50.))
334 self.add_parameter(
335 Param('Depth', 'depth_km', 10.0, -100.0, 600.0))
336 self.add_parameter(
337 Param('North force', 'fn', 1e3, -1e9, 1e9))
338 self.add_parameter(
339 Param('East force', 'fe', 1e3, -1e9, 1e9))
340 self.add_parameter(
341 Param('Down force', 'fd', 1e3, -1e9, 1e9))
342 self.add_parameter(
343 Param('STF duration', 'stf_duration', 0., 0., 100.))
344 self.add_parameter(
345 Choice('STF type', 'stf_type', self.stf_types[0], self.stf_types))
347 Seismosizer.setup(self)
349 def get_source(self, event):
350 return gf.SFSource(
351 time=event.time+self.time,
352 lat=event.lat,
353 lon=event.lon,
354 north_shift=self.north_km*km,
355 east_shift=self.east_km*km,
356 depth=self.depth_km*km,
357 fn=self.fn,
358 fe=self.fe,
359 fd=self.fd,
360 stf=self.get_stf())
363class RectangularSource(Seismosizer):
365 def setup(self):
366 '''Customization of the snuffling.'''
368 self.set_name('Seismosizer: RectangularSource')
369 self.add_parameter(
370 Param('Time', 'time', 0.0, -50., 50.))
371 # self.add_parameter(
372 # Param('Latitude', 'lat', 0.0, -90., 90.))
373 # self.add_parameter(
374 # Param('Longitude', 'lon', 0.0, -180., 180.))
375 self.add_parameter(
376 Param('North shift', 'north_km', 0.0, -50., 50.))
377 self.add_parameter(
378 Param('East shift', 'east_km', 0.0, -50., 50.))
379 self.add_parameter(
380 Param('Depth', 'depth_km', 10.0, 0.0, 600.0))
381 self.add_parameter(
382 Param('Magnitude', 'magnitude', 6.0, 0.0, 10.0))
383 self.add_parameter(
384 Param('Strike', 'strike', 0., -180., 180.))
385 self.add_parameter(
386 Param('Dip', 'dip', 90., 0., 90.))
387 self.add_parameter(
388 Param('Rake', 'rake', 0., -180., 180.))
389 self.add_parameter(
390 Param('Length', 'length', 10.*km, .1*km, 100*km))
391 self.add_parameter(
392 Param('Width', 'width', 5.*km, .1*km, 50*km))
393 self.add_parameter(
394 Param('Nucleation X', 'nucleation_x', 0., -1., 1.))
395 self.add_parameter(
396 Param('Nucleation Y', 'nucleation_y', 0., -1., 1.))
397 self.add_parameter(
398 Param('Rupture velocity', 'velocity', 3500.0, 0.0, 5000.0))
399 self.add_parameter(
400 Param('STF duration', 'stf_duration', 0., 0., 20.))
401 self.add_parameter(
402 Choice('STF type', 'stf_type', self.stf_types[0], self.stf_types))
404 Seismosizer.setup(self)
406 def get_source(self, event):
407 return gf.RectangularSource(
408 time=event.time+self.time,
409 lat=event.lat,
410 lon=event.lon,
411 north_shift=self.north_km*km,
412 east_shift=self.east_km*km,
413 depth=self.depth_km*km,
414 magnitude=self.magnitude,
415 strike=self.strike,
416 dip=self.dip,
417 rake=self.rake,
418 length=self.length,
419 width=self.width,
420 nucleation_x=self.nucleation_x,
421 nucleation_y=self.nucleation_y,
422 velocity=self.velocity,
423 stf=self.get_stf())
426class PseudoDynamicRuptureSource(Seismosizer):
428 def setup(self):
429 '''Customization of the snuffling.'''
431 self.set_name('Seismosizer: PseudoDynamicRupture')
432 self.add_parameter(
433 Param('Time', 'time', 0.0, -50., 50.))
434 # self.add_parameter(
435 # Param('Latitude', 'lat', 0.0, -90., 90.))
436 # self.add_parameter(
437 # Param('Longitude', 'lon', 0.0, -180., 180.))
438 self.add_parameter(
439 Param('North shift', 'north_km', 0.0, -50., 50.))
440 self.add_parameter(
441 Param('East shift', 'east_km', 0.0, -50., 50.))
442 self.add_parameter(
443 Param('Depth', 'depth_km', 10.0, 0.0, 600.0))
444 self.add_parameter(
445 Param('Slip', 'slip', 1.0, 0.0, 20.0))
446 self.add_parameter(
447 Param('Strike', 'strike', 0., -180., 180.))
448 self.add_parameter(
449 Param('Dip', 'dip', 90., 0., 90.))
450 self.add_parameter(
451 Param('Rake', 'rake', 0., -180., 180.))
452 self.add_parameter(
453 Param('Length', 'length', 10.*km, .1*km, 300*km))
454 self.add_parameter(
455 Param('Width', 'width', 5.*km, .1*km, 50*km))
456 self.add_parameter(
457 Param('Nucleation X', 'nucleation_x', 0., -1., 1.))
458 self.add_parameter(
459 Param('Nucleation Y', 'nucleation_y', 0., -1., 1.))
460 self.add_parameter(
461 Param('Gamma', 'gamma', 0.8, 0.1, 1.5))
462 self.add_parameter(
463 Param('nx', 'nx', 5, 1, 20))
464 self.add_parameter(
465 Param('ny', 'ny', 5, 1, 20))
466 self.add_parameter(
467 Param('STF duration', 'stf_duration', 0., 0., 20.))
468 self.add_parameter(
469 Choice('STF type', 'stf_type', self.stf_types[0], self.stf_types))
471 self.add_parameter(Switch(
472 'Tapered tractions', 'tapered', True))
474 Seismosizer.setup(self)
476 def get_source(self, event):
477 source = gf.PseudoDynamicRupture(
478 time=event.time + self.time,
479 lat=event.lat,
480 lon=event.lon,
481 nx=int(self.nx),
482 ny=int(self.ny),
483 north_shift=self.north_km*km,
484 east_shift=self.east_km*km,
485 depth=self.depth_km*km,
486 slip=self.slip,
487 strike=self.strike,
488 dip=self.dip,
489 rake=self.rake,
490 length=self.length,
491 width=self.width,
492 nucleation_x=self.nucleation_x,
493 nucleation_y=self.nucleation_y,
494 gamma=self.gamma,
495 nthreads=5,
496 pure_shear=True,
497 smooth_rupture=True)
499 return source
502def __snufflings__():
503 '''Returns a list of snufflings to be exported by this module.'''
504 return [
505 DCSource(),
506 SFSource(),
507 RectangularSource(),
508 PseudoDynamicRuptureSource()]