Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/gui/snuffler/snufflings/cake_phase.py: 31%
124 statements
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-06 15:01 +0000
« prev ^ index » next coverage.py v6.5.0, created at 2023-10-06 15:01 +0000
1# http://pyrocko.org - GPLv3
2#
3# The Pyrocko Developers, 21st Century
4# ---|P------/S----------~Lg----------
6from ..snuffling import Snuffling, Param, Switch, Choice
7from ..marker import PhaseMarker
8from pyrocko import gf
9from pyrocko import cake
10import numpy as num
13class CakePhase(Snuffling):
15 def help(self):
16 return '''
17<html>
18<head>
19<style type="text/css">
20 body { margin-left:10px };
21</style>
22</head>
23<body>
24<h1 align="center">Theoretical Phase Arrivals</h1>
25<p>
26This snuffling uses pyrocko's
27<a href="http://emolch.github.io/pyrocko/v0.3/cake_doc.html">Cake</a>
28module to calculate seismic rays for layered earth models. </p>
29<p>
30<b>Parameters:</b><br />
31 <b>· Global shift</b> - Add time onset to phases. <br />
32 <b>· Add Model</b> - Add a model to drop down menu. <br />
33 <b>· Add Phase</b> - Add a phase definition.
34 (GUI reset required)<br />
35</p>
36<p>
37Instructions and information on Cake's syntax of seismic rays can be
38found in the
39<a href="http://emolch.github.io/pyrocko/
40v0.3/cake_doc.html#cmdoption-cake--phase">Cake documentation</a>.
41</p>
42</body>
43</html>
44 '''
46 def setup(self):
47 self.set_name('Cake Phase')
49 # self._phase_names = ('PmP ~S ~P ~P(moho)s ~P(sill-top)s'
50 # ' ~P(sill-bottom)s Pdiff').split()
51 self._phase_names = ('~P Pg Sg pP p P Pdiff PKP PcP PcS PKIKP pPKIKP'
52 ' SSP PPS SPP PSP SP PS ~PS ~SP Pn s S Sn PP PPP'
53 ' ScS Sdiff SS SSS SKS SKIKS').split()
55 for iphase, name in enumerate(self._phase_names):
56 self.add_parameter(Switch(name, 'wantphase_%i' % iphase,
57 iphase == 0))
59 model_names = cake.builtin_models()
60 model_names = [
61 'Cake builtin: %s' % model_name for model_name in model_names]
63 self._engine = gf.LocalEngine(use_config=True)
64 store_ids = self._engine.get_store_ids()
66 for store_id in store_ids:
67 model_names.append('GF Store: %s' % store_id)
69 self._models = model_names
71 self.model_choice = Choice('Model', 'chosen_model',
72 'ak135-f-continental.m', self._models)
74 self.add_parameter(self.model_choice)
76 self.add_parameter(Param('Global shift', 'tshift', 0., -20., 20.))
77 self.add_parameter(Switch('Use station depth',
78 'use_station_depth', False))
79 self.add_trigger('Add Phase', self.add_phase_definition)
80 self.add_trigger('Add Model', self.add_model_to_choice)
81 self.add_trigger('Plot Model', self.plot_model)
82 self.add_trigger('Plot Rays', self.plot_rays)
84 self._phases = {}
85 self._model = None
87 def panel_visibility_changed(self, bool):
88 pass
90 def wanted_phases(self):
91 try:
92 wanted = []
93 for iphase, name in enumerate(self._phase_names):
94 if getattr(self, 'wantphase_%i' % iphase):
95 if name in self._phases:
96 phases = self._phases[name]
97 else:
98 if name.startswith('~'):
99 phases = [cake.PhaseDef(name[1:])]
100 else:
101 phases = cake.PhaseDef.classic(name)
103 self._phases[name] = phases
104 for pha in phases:
105 pha.name = name
107 wanted.extend(phases)
108 except (cake.UnknownClassicPhase, cake.PhaseDefParseError) as e:
109 self.fail(str(e))
111 return wanted
113 def call(self, plot_rays=False):
115 self.cleanup()
116 wanted = self.wanted_phases()
118 if not wanted:
119 return
121 event, stations = self.get_active_event_and_stations()
123 if not stations:
124 self.fail('No station information available.')
126 self.update_model()
127 model = self._model[1]
129 depth = event.depth
130 if depth is None:
131 depth = 0.0
133 allrays = []
134 alldists = []
135 for station in stations:
136 dist = event.distance_to(station)
137 alldists.append(dist)
139 if self.use_station_depth:
140 rdepth = station.depth
141 else:
142 rdepth = 0.0
144 multi_dists = []
145 nmax = 1
146 for i in range(0, nmax):
147 multi_dists.append(dist*cake.m2d + 360.*i)
148 multi_dists.append((i+1)*360. - dist*cake.m2d)
150 rays = model.arrivals(
151 phases=wanted,
152 distances=multi_dists,
153 zstart=depth,
154 zstop=rdepth)
156 for ray in rays:
157 time = ray.t
158 name = ray.given_phase().name
159 incidence_angle = ray.incidence_angle()
160 takeoff_angle = ray.takeoff_angle()
162 time += event.time + self.tshift
163 m = PhaseMarker([(station.network, station.station, '*', '*')],
164 time, time, 2,
165 phasename=name,
166 event=event,
167 incidence_angle=incidence_angle,
168 takeoff_angle=takeoff_angle)
169 self.add_marker(m)
171 allrays.extend(rays)
173 if plot_rays:
174 fig = self.figure(name='Ray Paths')
175 from pyrocko.plot import cake_plot
176 cake_plot.my_rays_plot(model, None, allrays, depth, 0.0,
177 num.array(alldists)*cake.m2d,
178 axes=fig.gca())
180 fig.canvas.draw()
182 def update_model(self):
183 if not self._model or self._model[0] != self.chosen_model:
184 if self.chosen_model.startswith('Cake builtin: '):
185 load_model = cake.load_model(
186 self.chosen_model.split(': ', 1)[1])
188 elif self.chosen_model.startswith('GF Store: '):
189 store_id = self.chosen_model.split(': ', 1)[1]
191 load_model = self._engine.get_store(store_id)\
192 .config.earthmodel_1d
193 else:
194 load_model = cake.load_model(self.chosen_model)
196 self._model = (self.chosen_model, load_model)
198 def update_model_choices(self):
199 self.set_parameter_choices('chosen_model', self._models)
201 def add_model_to_choice(self):
202 '''
203 Called from trigger 'Add Model'.
205 Adds another choice to the drop down 'Model' menu.
206 '''
208 in_model = self.input_filename('Load Model')
209 if in_model not in self._models:
210 self._models.append(in_model)
211 self.update_model_choices()
213 self.set_parameter('chosen_model', in_model)
214 self.call()
216 def add_phase_definition(self):
217 '''
218 Called from trigger 'Add Phase Definition'.
220 Adds another phase option.
221 Requires a reset of the GUI.
222 '''
223 phase_def = str(self.input_dialog('Add New Phase',
224 'Enter Phase Definition'))
225 self._phase_names.append(phase_def)
227 self.add_parameter(
228 Switch(phase_def,
229 'wantphase_%s' % str(len(self._phase_names)-1), True))
230 self.reset_gui(reloaded=True)
231 self.call()
233 def plot_model(self):
234 self.update_model()
236 from pyrocko.plot import cake_plot
238 fig = self.figure(name='Model: %s' % self._model[0])
240 cake_plot.my_model_plot(self._model[1], axes=fig.gca())
242 fig.canvas.draw()
244 def plot_rays(self):
245 self.call(plot_rays=True)
248def __snufflings__():
249 '''
250 Returns a list of snufflings to be exported by this module.
251 '''
253 return [CakePhase()]