1# http://pyrocko.org - GPLv3 

2# 

3# The Pyrocko Developers, 21st Century 

4# ---|P------/S----------~Lg---------- 

5 

6import numpy as num 

7import os 

8 

9from pyrocko import moment_tensor, model 

10from pyrocko.gui.snuffling import Snuffling, Param, Choice, EventMarker, Switch 

11from pyrocko import gf 

12 

13km = 1000. 

14 

15 

16class Seismosizer(Snuffling): 

17 ''' 

18 Generate synthetic traces on the fly 

19 ==================================== 

20 

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.). 

24 

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 ''' 

29 

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] 

35 

36 def get_source(self, event): 

37 raise NotImplementedError() 

38 

39 def panel_visibility_changed(self, bool): 

40 if bool: 

41 if self._engine is None: 

42 self.set_engine() 

43 

44 def set_engine(self): 

45 self._engine = None 

46 self.store_ids = self.get_store_ids() 

47 if self.store_ids == []: 

48 return 

49 

50 self.set_parameter_choices('store_id', self.store_ids) 

51 self.store_id = self.store_ids[0] 

52 

53 def get_engine(self): 

54 if not self._engine: 

55 self._engine = gf.LocalEngine(use_config=True) 

56 

57 return self._engine 

58 

59 def get_store_ids(self): 

60 return self.get_engine().get_store_ids() 

61 

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 

66 

67 return stf 

68 

69 def call(self): 

70 ''' 

71 Main work routine of the snuffling. 

72 ''' 

73 self.cleanup() 

74 

75 # get time range visible in viewer 

76 viewer = self.get_viewer() 

77 

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 = [] 

86 

87 stations = self.get_stations() 

88 

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() 

97 

98 s2c[ns].add((loc, cha)) 

99 

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) 

107 

108 for s in stations: 

109 ns = s.nsl()[:2] 

110 if ns not in s2c: 

111 s2c[ns] = set() 

112 

113 for cha in 'NEZ': 

114 s2c[ns].add(('', cha)) 

115 

116 source = self.get_source(event) 

117 source.regularize() 

118 

119 m = EventMarker(source.pyrocko_event()) 

120 self.add_marker(m) 

121 

122 targets = [] 

123 

124 if self.store_id == '<not loaded yet>': 

125 self.fail('Select a GF Store first') 

126 

127 for station in stations: 

128 

129 nsl = station.nsl() 

130 if nsl[:2] not in s2c: 

131 continue 

132 

133 for loc, cha in s2c[nsl[:2]]: 

134 

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') 

148 

149 _, bazi = source.azibazi_to(target) 

150 

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 

166 

167 target.azimuth = azi 

168 target.dip = dip 

169 

170 targets.append(target) 

171 

172 req = gf.Request( 

173 sources=[source], 

174 targets=targets) 

175 

176 req.regularize() 

177 

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) 

182 

183 traces = resp.pyrocko_traces() 

184 

185 if self.waveform_type.startswith('Velocity'): 

186 for tr in traces: 

187 tr.set_ydata(num.diff(tr.ydata) / tr.deltat) 

188 

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) 

192 

193 if self.waveform_type.endswith('[nm]') or \ 

194 self.waveform_type.endswith('[nm/s]') or \ 

195 self.waveform_type.endswith('[nm/s^2]'): 

196 

197 for tr in traces: 

198 tr.set_ydata(tr.ydata * 1e9) 

199 

200 self.add_traces(traces) 

201 

202 def mechanism_from_event(self): 

203 

204 event = self.get_viewer().get_active_event() 

205 

206 if event is None: 

207 self.fail('No active event set.') 

208 

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) 

222 

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) 

228 

229 self.set_parameter('lat', event.lat) 

230 self.set_parameter('lon', event.lon) 

231 self.set_parameter('depth_km', event.depth/km) 

232 

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() 

241 

242 self.set_parameter_choices('store_id', self.store_ids) 

243 

244 def has_config(self, directory): 

245 return 'config' in os.listdir(directory) 

246 

247 

248class DCSource(Seismosizer): 

249 

250 def setup(self): 

251 '''Customization of the snuffling.''' 

252 

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]'])) 

289 

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) 

293 

294 self.store_ids = None 

295 self.offline_config = None 

296 self._engine = None 

297 

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()) 

311 

312 

313class RectangularSource(Seismosizer): 

314 

315 def setup(self): 

316 '''Customization of the snuffling.''' 

317 

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]'])) 

364 

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) 

368 

369 self.store_ids = None 

370 self.offline_config = None 

371 self._engine = None 

372 

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()) 

391 

392 

393class PseudoDynamicRuptureSource(Seismosizer): 

394 

395 def setup(self): 

396 '''Customization of the snuffling.''' 

397 

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)) 

437 

438 self.add_parameter(Switch( 

439 'Tapered tractions', 'tapered', True)) 

440 

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]'])) 

452 

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) 

456 

457 self.store_ids = None 

458 self.offline_config = None 

459 self._engine = None 

460 

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(), 

481 

482 nthreads=5, 

483 pure_shear=True, 

484 smooth_rupture=False) 

485 

486 return source 

487 

488 

489def __snufflings__(): 

490 '''Returns a list of snufflings to be exported by this module.''' 

491 return [DCSource(), RectangularSource(), PseudoDynamicRuptureSource()]