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 

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 setup(self): 

37 ''' 

38 Customization of the snuffling. 

39 ''' 

40 

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

85 

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) 

89 

90 self.store_ids = None 

91 self.offline_config = None 

92 self._engine = None 

93 

94 def panel_visibility_changed(self, bool): 

95 if bool: 

96 if self._engine is None: 

97 self.set_engine() 

98 

99 def set_engine(self): 

100 self._engine = None 

101 self.store_ids = self.get_store_ids() 

102 if self.store_ids == []: 

103 return 

104 

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

106 self.store_id = self.store_ids[0] 

107 

108 def get_engine(self): 

109 if not self._engine: 

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

111 

112 return self._engine 

113 

114 def get_store_ids(self): 

115 return self.get_engine().get_store_ids() 

116 

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 

121 

122 return stf 

123 

124 def call(self): 

125 ''' 

126 Main work routine of the snuffling. 

127 ''' 

128 self.cleanup() 

129 

130 # get time range visible in viewer 

131 viewer = self.get_viewer() 

132 

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

141 

142 stations = self.get_stations() 

143 

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

152 

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

154 

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) 

162 

163 for s in stations: 

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

165 if ns not in s2c: 

166 s2c[ns] = set() 

167 

168 for cha in 'NEZ': 

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

170 

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

187 

188 source.regularize() 

189 

190 m = EventMarker(source.pyrocko_event()) 

191 self.add_marker(m) 

192 

193 targets = [] 

194 

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

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

197 

198 for station in stations: 

199 

200 nsl = station.nsl() 

201 if nsl[:2] not in s2c: 

202 continue 

203 

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

205 

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

219 

220 _, bazi = source.azibazi_to(target) 

221 

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 

237 

238 target.azimuth = azi 

239 target.dip = dip 

240 

241 targets.append(target) 

242 

243 req = gf.Request( 

244 sources=[source], 

245 targets=targets) 

246 

247 req.regularize() 

248 

249 try: 

250 resp = self.get_engine().process(req) 

251 except (gf.meta.OutOfBounds, gf.store_ext.StoreExtError)as e: 

252 self.fail(e) 

253 

254 traces = resp.pyrocko_traces() 

255 

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

257 for tr in traces: 

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

259 

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) 

263 

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

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

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

267 

268 for tr in traces: 

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

270 

271 self.add_traces(traces) 

272 

273 def mechanism_from_event(self): 

274 

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

276 

277 if event is None: 

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

279 

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) 

293 

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

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

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

297 

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

306 

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

308 

309 def has_config(self, directory): 

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

311 

312 

313def __snufflings__(): 

314 ''' 

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

316 ''' 

317 return [Seismosizer()]