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-10 10:12 +0000

1# https://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 ..snuffling import Snuffling, Param, Choice, Switch, EventMarker 

11from pyrocko import gf 

12 

13km = 1000. 

14 

15 

16class Seismosizer(Snuffling): 

17 

18 def help(self): 

19 return ''' 

20Generate synthetic traces on the fly 

21==================================== 

22 

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

26 

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

31 

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] 

37 

38 def get_source(self, event): 

39 raise NotImplementedError() 

40 

41 def panel_visibility_changed(self, bool): 

42 if bool: 

43 if self._engine is None: 

44 self.set_engine() 

45 

46 def set_engine(self): 

47 self._engine = None 

48 self.store_ids = self.get_store_ids() 

49 if self.store_ids == []: 

50 return 

51 

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

53 self.store_id = self.store_ids[0] 

54 

55 def get_engine(self): 

56 if not self._engine: 

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

58 

59 return self._engine 

60 

61 def get_store_ids(self): 

62 return self.get_engine().get_store_ids() 

63 

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 

68 

69 return stf 

70 

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

83 

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) 

87 

88 self.store_ids = None 

89 self.offline_config = None 

90 self._engine = None 

91 

92 def call(self): 

93 ''' 

94 Main work routine of the snuffling. 

95 ''' 

96 self.cleanup() 

97 

98 # get time range visible in viewer 

99 viewer = self.get_viewer() 

100 

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

109 

110 stations = self.get_stations() 

111 

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

120 

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

122 

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) 

130 

131 for s in stations: 

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

133 if ns not in s2c: 

134 s2c[ns] = set() 

135 

136 for cha in 'NEZ': 

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

138 

139 source = self.get_source(event) 

140 source.regularize() 

141 

142 m = EventMarker(source.pyrocko_event()) 

143 self.add_marker(m) 

144 

145 targets = [] 

146 

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

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

149 

150 for station in stations: 

151 

152 nsl = station.nsl() 

153 if nsl[:2] not in s2c: 

154 continue 

155 

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

157 

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

171 

172 _, bazi = source.azibazi_to(target) 

173 

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 

189 

190 target.azimuth = azi 

191 target.dip = dip 

192 

193 targets.append(target) 

194 

195 req = gf.Request( 

196 sources=[source], 

197 targets=targets) 

198 

199 req.regularize() 

200 

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) 

205 

206 traces = resp.pyrocko_traces() 

207 

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

209 for tr in traces: 

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

211 

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) 

215 

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

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

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

219 

220 for tr in traces: 

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

222 

223 self.add_traces(traces) 

224 

225 def mechanism_from_event(self): 

226 

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

228 

229 if event is None: 

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

231 

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) 

245 

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) 

251 

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

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

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

255 

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

264 

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

266 

267 def has_config(self, directory): 

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

269 

270 

271class DCSource(Seismosizer): 

272 

273 def setup(self): 

274 '''Customization of the snuffling.''' 

275 

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

301 

302 Seismosizer.setup(self) 

303 

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

317 

318 

319class SFSource(Seismosizer): 

320 def setup(self): 

321 '''Customization of the snuffling.''' 

322 

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

346 

347 Seismosizer.setup(self) 

348 

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

361 

362 

363class RectangularSource(Seismosizer): 

364 

365 def setup(self): 

366 '''Customization of the snuffling.''' 

367 

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

403 

404 Seismosizer.setup(self) 

405 

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

424 

425 

426class PseudoDynamicRuptureSource(Seismosizer): 

427 

428 def setup(self): 

429 '''Customization of the snuffling.''' 

430 

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

470 

471 self.add_parameter(Switch( 

472 'Tapered tractions', 'tapered', True)) 

473 

474 Seismosizer.setup(self) 

475 

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) 

498 

499 return source 

500 

501 

502def __snufflings__(): 

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

504 return [ 

505 DCSource(), 

506 SFSource(), 

507 RectangularSource(), 

508 PseudoDynamicRuptureSource()]