Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/apps/cake.py: 77%
406 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----------
6'''
7Cake - travel-times and ray paths for 1D layered media.
8'''
10import sys
11import re
12import numpy as num
13# import logger
14from pyrocko import cake, util, orthodrome
15from pyrocko.plot import cake_plot as plot
16from optparse import OptionParser, OptionGroup
17import matplotlib.pyplot as plt
18from pyrocko.plot import mpl_init, mpl_papersize, mpl_margins
20r2d = cake.r2d
23class Anon(dict):
25 def __getattr__(self, x):
26 return self[x]
28 def getn(self, *keys):
29 return Anon([(k, self[k]) for k in keys])
31 def gett(self, *keys):
32 return tuple([self[k] for k in keys])
35def process_color(s, phase_colors):
36 m = re.match(r'^([^{]+){([^}]*)}$', s)
37 if not m:
38 return s
40 sphase = m.group(1)
41 color = m.group(2)
42 phase_colors[sphase] = plot.str_to_mpl_color(color)
44 return sphase
47def optparse(
48 required=(),
49 optional=(),
50 args=sys.argv,
51 usage='%prog [options]',
52 descr=None):
54 want = required + optional
56 parser = OptionParser(
57 prog='cake',
58 usage=usage,
59 description=descr.capitalize()+'.',
60 add_help_option=False,
61 formatter=util.BetterHelpFormatter())
63 parser.add_option(
64 '-h', '--help', action='help', help='Show help message and exit.')
66 if 'phases' in want:
67 group = OptionGroup(parser, 'Phases', '''
69Seismic phase arrivals may be either specified as traditional phase names (e.g.
70P, S, PP, PcP, ...) or in Cake's own syntax which is more powerful. Use the
71--classic option, for traditional phase names. Use the --phase option if you
72want to define phases in Cake's syntax.
74''')
75 group.add_option(
76 '--phase', '--phases', dest='phases', action='append',
77 default=[], metavar='PHASE1,PHASE2,...',
78 help='''Comma separated list of seismic phases in Cake\'s syntax.
80The definition of a seismic propagation path in Cake's phase syntax is a string
81consisting of an alternating sequence of "legs" and "knees".
83A "leg" represents seismic wave propagation without any conversions,
84encountering only super-critical reflections. Legs are denoted by "P", "p",
85"S", or "s". The capital letters are used when the take-off of the "leg" is
86in downward direction, while the lower case letters indicate a take-off in
87upward direction.
89A "knee" is an interaction with an interface. It can be a mode conversion, a
90reflection, or propagation as a headwave or diffracted wave.
92 * conversion is simply denoted as: "(INTERFACE)" or "DEPTH"
93 * upperside reflection: "v(INTERFACE)" or "vDEPTH"
94 * underside reflection: "^(INTERFACE)" or "^DEPTH"
95 * normal kind headwave or diffracted wave: "v_(INTERFACE)" or "v_DEPTH"
97The interface may be given by name or by depth: INTERFACE is the name of an
98interface defined in the model, DEPTH is the depth of an interface in [km] (the
99interface closest to that depth is chosen). If two legs appear consecutively
100without an explicit "knee", surface interaction is assumed.
102The preferred standard interface names in cake are "conrad", "moho", "cmb"
103(core-mantle boundary), and "cb" (inner core boundary).
105The phase definition may end with a backslash "\\", to indicate that the ray
106should arrive at the receiver from above instead of from below. It is possible
107to restrict the maximum and minimum depth of a "leg" by appending
108"<(INTERFACE)" or "<DEPTH" or ">(INTERFACE)" or ">DEPTH" after the leg
109character, respectively.
111When plotting rays or travel-time curves, the color can be set by appending
112"{COLOR}" to the phase definition, where COLOR is the name of a color or an RGB
113or RGBA color tuple in the format "R/G/B" or "R/G/B/A", respectively. The
114values can be normalized to the range [0, 1] or to [0, 255]. The latter is only
115assumed when any of the values given exceeds 1.0.
116''')
118 group.add_option(
119 '--classic', dest='classic_phases', action='append',
120 default=[], metavar='PHASE1,PHASE2,...',
121 help='''Comma separated list of seismic phases in classic
122nomenclature. Run "cake list-phase-map" for a list of available
123phase names. When plotting, color can be specified in the same way
124as in --phases.''')
126 parser.add_option_group(group)
128 if 'model' in want:
129 group = OptionGroup(parser, 'Model')
130 group.add_option(
131 '--model', dest='model_filename', metavar='(NAME or FILENAME)',
132 help='Use builtin model named NAME or user model from file '
133 'FILENAME. By default, the "ak135-f-continental.m" model is '
134 'used. Run "cake list-models" for a list of builtin models.')
136 group.add_option(
137 '--format', dest='model_format', metavar='FORMAT',
138 choices=['nd', 'hyposat'], default='nd',
139 help='Set model file format (available: nd, hyposat; default: '
140 'nd).')
141 group.add_option(
142 '--crust2loc', dest='crust2loc', metavar='LAT,LON',
143 help='Set model from CRUST2.0 profile at location (LAT,LON).')
144 group.add_option(
145 '--crust2profile', dest='crust2profile', metavar='KEY',
146 help='Set model from CRUST2.0 profile with given KEY.')
148 parser.add_option_group(group)
150 if any(x in want for x in (
151 'zstart', 'zstop', 'distances', 'sloc', 'rloc')):
153 group = OptionGroup(parser, 'Source-receiver geometry')
154 if 'zstart' in want:
155 group.add_option(
156 '--sdepth', dest='sdepth', type='float', default=0.0,
157 metavar='FLOAT',
158 help='Source depth [km] (default: 0)')
159 if 'zstop' in want:
160 group.add_option(
161 '--rdepth', dest='rdepth', type='float', default=0.0,
162 metavar='FLOAT',
163 help='Receiver depth [km] (default: 0)')
164 if 'distances' in want:
165 group.add_option(
166 '--distances', dest='sdist', metavar='DISTANCES',
167 help='Surface distances as "start:stop:n" or '
168 '"dist1,dist2,..." [km]')
169 group.add_option(
170 '--sloc', dest='sloc', metavar='LAT,LON',
171 help='Source location (LAT,LON).')
172 group.add_option(
173 '--rloc', dest='rloc', metavar='LAT,LON',
174 help='Receiver location (LAT,LON).')
175 parser.add_option_group(group)
177 if 'material' in want:
178 group = OptionGroup(
179 parser, 'Material',
180 'An isotropic elastic material may be specified by giving '
181 'a combination of some of the following options. ')
182 group.add_option(
183 '--vp', dest='vp', default=None, type='float', metavar='FLOAT',
184 help='P-wave velocity [km/s]')
185 group.add_option(
186 '--vs', dest='vs', default=None, type='float', metavar='FLOAT',
187 help='S-wave velocity [km/s]')
188 group.add_option(
189 '--rho', dest='rho', default=None, type='float', metavar='FLOAT',
190 help='density [g/cm**3]')
191 group.add_option(
192 '--qp', dest='qp', default=None, type='float', metavar='FLOAT',
193 help='P-wave attenuation Qp (default: 1456)')
194 group.add_option(
195 '--qs', dest='qs', default=None, type='float', metavar='FLOAT',
196 help='S-wave attenuation Qs (default: 600)')
197 group.add_option(
198 '--poisson', dest='poisson', default=None, type='float',
199 metavar='FLOAT',
200 help='Poisson ratio')
201 group.add_option(
202 '--lambda', dest='lame_lambda', default=None, type='float',
203 metavar='FLOAT',
204 help='Lame parameter lambda [GPa]')
205 group.add_option(
206 '--mu', dest='lame_mu', default=None, type='float',
207 metavar='FLOAT',
208 help='Shear modulus [GPa]')
209 group.add_option(
210 '--qk', dest='qk', default=None, type='float', metavar='FLOAT',
211 help='Bulk attenuation Qk')
212 group.add_option(
213 '--qmu', dest='qmu', default=None, type='float', metavar='FLOAT',
214 help='Shear attenuation Qmu')
215 parser.add_option_group(group)
217 if any(x in want for x in (
218 'vred', 'as_degrees', 'accuracy', 'slowness', 'interface',
219 'aspect', 'shade_model')):
221 group = OptionGroup(parser, 'General')
222 if 'vred' in want:
223 group.add_option(
224 '--vred', dest='vred', type='float', metavar='FLOAT',
225 help='Velocity for time reduction in plot [km/s]')
227 if 'as_degrees' in want:
228 group.add_option(
229 '--degrees', dest='as_degrees', action='store_true',
230 default=False,
231 help='Distances are in [deg] instead of [km], velocities in '
232 '[deg/s] instead of [km/s], slownesses in [s/deg] '
233 'instead of [s/km].')
235 if 'accuracy' in want:
236 group.add_option(
237 '--accuracy', dest='accuracy', type='float',
238 metavar='MAXIMUM_RELATIVE_RMS', default=0.002,
239 help='Set accuracy for model simplification.')
241 if 'slowness' in want:
242 group.add_option(
243 '--slowness', dest='slowness', type='float', metavar='FLOAT',
244 default=0.0,
245 help='Select surface slowness [s/km] (default: 0)')
247 if 'interface' in want:
248 group.add_option(
249 '--interface', dest='interface', metavar='(NAME or DEPTH)',
250 help='Name or depth [km] of interface to select')
252 if 'aspect' in want:
253 group.add_option(
254 '--aspect', dest='aspect', type='float', metavar='FLOAT',
255 help='Aspect ratio for plot')
257 if 'shade_model' in want:
258 group.add_option(
259 '--no-shade-model', dest='shade_model', action='store_false',
260 default=True,
261 help='Suppress shading of earth model layers')
263 parser.add_option_group(group)
265 if any(x in want for x in ('output_format', 'save', 'size', 'show')):
266 group = OptionGroup(parser, 'Output', 'Output specifications')
267 if 'output_format' in want:
268 group.add_option(
269 '--output-format', dest='output_format', metavar='FORMAT',
270 default='textual',
271 choices=('textual', 'nd'),
272 help='Set model output format (available: textual, nd, '
273 'default: textual)')
274 if 'save' in want:
275 group.add_option(
276 '-s', '--save', dest='save', metavar='PATH',
277 help='saves plot to .png (default) or other py-supported\
278 endings without showing, use --show or -u for showing',
279 default='')
280 if 'size' in want:
281 group.add_option(
282 '--size', dest='size', type='string',
283 default='a4',
284 help="gives size of returned plot, use 'a5' or 'a4'")
285 if 'show' in want:
286 group.add_option(
287 '-u', '--show', dest='show', action='store_true',
288 help='shows plot when saving (-u for unhide)')
290 parser.add_option_group(group)
292 if usage == 'cake help-options':
293 parser.print_help()
295 (options, args) = parser.parse_args(args)
297 if len(args) != 2:
298 parser.error(
299 'Cake arguments should look like "--option" or "--option=...".')
301 d = {}
302 as_degrees = False
303 if 'as_degrees' in want:
304 as_degrees = options.as_degrees
305 d['as_degrees'] = as_degrees
307 if 'accuracy' in want:
308 d['accuracy'] = options.accuracy
310 if 'output_format' in want:
311 d['output_format'] = options.output_format
313 if 'save' in want:
314 d['save'] = options.save
316 if 'size' in want:
317 d['size'] = options.size
319 if 'show' in want:
320 d['show'] = options.show
322 if 'aspect' in want:
323 d['aspect'] = options.aspect
325 if 'shade_model' in want:
326 d['shade_model'] = options.shade_model
328 if 'phases' in want:
329 phases = []
330 phase_colors = {}
331 try:
332 for ss in options.phases:
333 for s in ss.split(','):
334 s = process_color(s, phase_colors)
335 phases.append(cake.PhaseDef(s))
337 for pp in options.classic_phases:
338 for p in pp.split(','):
339 p = process_color(p, phase_colors)
340 phases.extend(cake.PhaseDef.classic(p))
342 except (cake.PhaseDefParseError, cake.UnknownClassicPhase) as e:
343 parser.error(e)
345 if not phases and 'phases' in required:
346 s = process_color('P', phase_colors)
347 phases.append(cake.PhaseDef(s))
349 if phases:
350 d['phase_colors'] = phase_colors
351 d['phases'] = phases
353 if 'model' in want:
354 if options.model_filename:
355 d['model'] = cake.load_model(
356 options.model_filename, options.model_format)
358 if options.crust2loc or options.crust2profile:
359 if options.crust2loc:
360 try:
361 args = tuple(
362 [float(x) for x in options.crust2loc.split(',')])
363 except Exception:
364 parser.error(
365 'format for --crust2loc option is '
366 '"LATITUDE,LONGITUDE"')
367 elif options.crust2profile:
368 args = (options.crust2profile.upper(),)
369 else:
370 assert False
372 if 'model' in d:
373 d['model'] = d['model'].replaced_crust(args)
374 else:
375 from pyrocko import crust2x2
376 profile = crust2x2.get_profile(*args)
377 d['model'] = cake.LayeredModel.from_scanlines(
378 cake.from_crust2x2_profile(profile))
380 if 'vred' in want:
381 d['vred'] = options.vred
382 if d['vred'] is not None:
383 if not as_degrees:
384 d['vred'] *= r2d * cake.km / cake.earthradius
386 if 'distances' in want:
387 distances = None
388 if options.sdist:
389 if options.sdist.find(':') != -1:
390 ssn = options.sdist.split(':')
391 if len(ssn) != 3:
392 parser.error(
393 'format for distances is '
394 '"min_distance:max_distance:n_distances"')
396 distances = num.linspace(
397 float(ssn[0]), float(ssn[1]), int(ssn[2]))
398 else:
399 distances = num.array(
400 list(map(
401 float, options.sdist.split(','))), dtype=float)
403 if not as_degrees:
404 distances *= r2d * cake.km / cake.earthradius
406 if options.sloc and options.rloc:
407 try:
408 slat, slon = tuple([float(x) for x in options.sloc.split(',')])
409 rlat, rlon = tuple([float(x) for x in options.rloc.split(',')])
410 except Exception:
411 parser.error(
412 'format for --sloc and --rloc options is '
413 '"LATITUDE,LONGITUDE"')
415 distance_sr = orthodrome.distance_accurate50m_numpy(
416 slat, slon, rlat, rlon)
418 distance_sr *= r2d / cake.earthradius
419 if distances is not None:
420 distances = num.concatenate((distances, [distance_sr]))
421 else:
422 distances = num.array([distance_sr], dtype=float)
424 if distances is not None:
425 d['distances'] = distances
426 else:
427 if 'distances' not in required:
428 d['distances'] = None
430 if 'slowness' in want:
431 d['slowness'] = options.slowness/cake.d2r
432 if not as_degrees:
433 d['slowness'] /= cake.km*cake.m2d
435 if 'interface' in want:
436 if options.interface:
437 try:
438 d['interface'] = float(options.interface)*cake.km
439 except ValueError:
440 d['interface'] = options.interface
442 else:
443 d['interface'] = None
445 if 'zstart' in want:
446 d['zstart'] = options.sdepth*cake.km
448 if 'zstop' in want:
449 d['zstop'] = options.rdepth*cake.km
451 if 'material' in want:
452 md = {}
453 userfactor = dict(
454 vp=1000., vs=1000., rho=1000., qp=1., qs=1., qmu=1., qk=1.,
455 lame_lambda=1.0e9, lame_mu=1.0e9, poisson=1.)
457 for k in userfactor.keys():
458 if getattr(options, k) is not None:
459 md[k] = getattr(options, k) * userfactor[k]
461 if not (bool('lame_lambda' in md) == bool('lame_mu' in md)):
462 parser.error('lambda and mu must be specified both.')
463 if 'lame_lambda' in md and 'lame_mu' in md:
464 md['lame'] = md.pop('lame_lambda'), md.pop('lame_mu')
466 if md:
467 try:
468 d['material'] = cake.Material(**md)
469 except cake.InvalidArguments as e:
470 parser.error(str(e))
472 for k in list(d.keys()):
473 if k not in want:
474 del d[k]
476 for k in required:
477 if k not in d:
478 if k == 'model':
479 d['model'] = cake.load_model('ak135-f-continental.m')
481 elif k == 'distances':
482 d['distances'] = num.linspace(10*cake.km, 100*cake.km, 10) \
483 / cake.earthradius * r2d
485 elif k == 'phases':
486 d['phases'] = list(map(cake.PhaseDef, 'Pp'))
488 else:
489 parser.error('missing %s' % k)
491 return Anon(d)
494def my_simplify_model(mod, accuracy):
495 mod_simple = mod.simplify(max_rel_error=accuracy)
496 cake.write_nd_model_fh(mod_simple, sys.stdout)
499def d2u(d):
500 return dict((k.replace('-', '_'), v) for (k, v) in d.items())
503def mini_fmt(v, d=5, try_fmts='fe'):
504 for fmt in try_fmts:
505 for i in range(d, -1, -1):
506 s = ('%%%i.%i%s' % (d, i, fmt)) % v
507 if len(s) <= d and (v == 0.0 or float(s) != 0.0):
508 return s
510 return s
513def scatter_out_fmt(d, m, v):
514 if v < 1e-8:
515 return ' ', ' '
516 else:
517 return '%s%s' % ('\\/'[d == cake.UP], 'SP'[m == cake.P]), \
518 mini_fmt(v*100, 5)
521def scatter_in_fmt(d, m, dwant):
522 if d == dwant:
523 return '%s%s' % ('\\/'[d == cake.UP], 'SP'[m == cake.P])
524 else:
525 return ' '
528def print_scatter(model, p=0.0, interface=None):
529 if interface is not None:
530 discontinuities = [model.discontinuity(interface)]
531 else:
532 discontinuities = model.discontinuities()
534 for discontinuity in discontinuities:
535 print('%s (%g km)' % (discontinuity, discontinuity.z/cake.km))
536 print()
537 cols = []
538 for in_direction in (cake.DOWN, cake.UP):
539 for in_mode in (cake.P, cake.S):
540 p_critical = discontinuity.critical_ps(in_mode)[
541 in_direction == cake.UP]
543 if p_critical is None or p >= p_critical:
544 continue
546 vals = []
547 for out_direction in (cake.UP, cake.DOWN):
548 for out_mode in (cake.S, cake.P):
549 vals.append(
550 (out_direction, out_mode, discontinuity.efficiency(
551 in_direction, out_direction,
552 in_mode, out_mode, p)))
554 if all(x[-1] == 0.0 for x in vals):
555 continue
557 sout = [scatter_out_fmt(d, m, v) for (d, m, v) in vals]
559 sin1 = scatter_in_fmt(in_direction, in_mode, cake.DOWN)
560 sin2 = scatter_in_fmt(in_direction, in_mode, cake.UP)
561 line1 = '%s %5s %5s' % (
562 ' '*len(sin1), sout[0][1], sout[1][1])
563 line2 = '%s %-5s %-5s' % (
564 sin1, sout[0][0], sout[1][0])
565 line4 = '%s %-5s %-5s' % (
566 sin2, sout[2][0], sout[3][0])
567 line5 = '%s %5s %5s' % (
568 ' '*len(sin2), sout[2][1], sout[3][1])
569 line3 = '-' * len(line1)
570 cols.append((line1, line2, line3, line4, line5))
572 for cols in zip(*cols):
573 print(' ' + ' '.join(cols))
575 print()
576 print()
579def print_arrivals(
580 model, distances=[], phases=cake.PhaseDef('P'), zstart=0.0, zstop=0.0,
581 as_degrees=False):
583 headers = 'slow dist time take inci effi spre phase used'.split()
584 space = (7, 5, 6, 4, 4, 4, 4, 17, 17)
586 if as_degrees:
587 units = 's/deg deg s deg deg % %'.split()
588 else:
589 units = 's/km km s deg deg % %'.split()
591 hline = ' '.join(x.ljust(s) for (x, s) in zip(headers, space))
592 uline = ' '.join(('%s' % x).ljust(s) for (x, s) in zip(units, space))
594 print(hline)
595 print(uline)
596 print('-' * len(hline))
598 for ray in model.arrivals(
599 distances=distances, phases=phases, zstart=zstart, zstop=zstop):
601 if as_degrees:
602 sd = ray.x
603 slow = ray.p/cake.r2d
604 else:
605 sd = ray.x*(cake.d2r*cake.earthradius/cake.km)
606 slow = ray.p/(r2d*cake.d2m/cake.km)
608 su = '(%s)' % ray.path.used_phase(p=ray.p, eps=1.0).used_repr()
610 print(' '.join(tuple(mini_fmt(x, s).rjust(s) for (x, s) in zip(
611 (slow, sd, ray.t, ray.takeoff_angle(), ray.incidence_angle(),
612 100*ray.efficiency(), 100*ray.spreading()*ray.surface_sphere()),
613 space)) + tuple(
614 x.ljust(17) for x in (ray.path.phase.definition(), su))))
617def plot_init(size, save, show):
618 fontsize = 9
619 mpl_init()
620 fig = plt.figure(figsize=mpl_papersize(size, 'landscape'))
622 labelpos = mpl_margins(fig, w=7., h=5., units=fontsize)
623 axes = fig.add_subplot(1, 1, 1)
624 labelpos(axes, 2., 1.5)
626 showplt = bool(show or not save)
628 return fig, axes, showplt
631class CakeError(Exception):
632 pass
635def plot_end(save, fig, show=True):
636 if save:
637 try:
638 fig.savefig(save)
639 if show:
640 plt.show()
641 except OSError as e:
642 raise CakeError(str(e))
643 except ValueError as e:
644 raise CakeError(str(e))
647def main(args=None):
648 '''
649 CLI entry point for Pyrocko's ``cake`` app.
650 '''
652 if args is None:
653 args = sys.argv[1:]
655 subcommand_descriptions = {
656 'print': 'get information on model/phase/material properties',
657 'arrivals': 'print list of phase arrivals',
658 'paths': 'print ray path details',
659 'plot-xt': 'plot traveltime vs distance curves',
660 'plot-xp': 'plot ray parameter vs distance curves',
661 'plot-rays': 'plot ray propagation paths',
662 'plot': 'plot combination of ray and traveltime curves',
663 'plot-model': 'plot velocity model',
664 'list-models': 'list builtin velocity models',
665 'list-phase-map': 'show translation table for classic phase names',
666 'simplify-model': 'create a simplified version of a layered model',
667 'scatter': 'show details about scattering at model interfaces'}
669 usage = '''cake <subcommand> [options]
671Subcommands:
673 print %(print)s
674 arrivals %(arrivals)s
675 paths %(paths)s
676 plot-xt %(plot_xt)s
677 plot-xp %(plot_xp)s
678 plot-rays %(plot_rays)s
679 plot %(plot)s
680 plot-model %(plot_model)s
681 list-models %(list_models)s
682 list-phase-map %(list_phase_map)s
683 simplify-model %(simplify_model)s
684 scatter %(scatter)s
686To get further help and a list of available options for any subcommand run:
688 cake <subcommand> --help
690'''.strip() % d2u(subcommand_descriptions)
692 usage_sub = 'cake %s [options]'
693 if len(args) < 1:
694 sys.exit('Usage: %s' % usage)
696 command = args[0]
698 args[0:0] = ['cake']
699 descr = subcommand_descriptions.get(command, None)
700 subusage = usage_sub % command
702 if command == 'print':
703 c = optparse(
704 (), ('model', 'phases', 'material', 'output_format'),
705 usage=subusage, descr=descr, args=args)
707 if 'model' in c:
708 if c.output_format == 'textual':
709 print(c.model)
710 print()
711 elif c.output_format == 'nd':
712 cake.write_nd_model_fh(c.model, sys.stdout)
714 if 'phases' in c:
715 for phase in c.phases:
716 print(phase)
717 print()
719 if 'material' in c:
720 print(c.material.describe())
721 print()
723 elif command == 'arrivals':
724 c = optparse(
725 ('model', 'phases', 'distances'),
726 ('zstart', 'zstop', 'as_degrees'),
727 usage=subusage, descr=descr, args=args)
729 print_arrivals(
730 c.model,
731 **c.getn('zstart', 'zstop', 'phases', 'distances', 'as_degrees'))
733 elif command == 'paths':
734 c = optparse(
735 ('model', 'phases'),
736 ('zstart', 'zstop', 'as_degrees'),
737 usage=subusage, descr=descr, args=args)
739 mod = c.model
740 for path in mod.gather_paths(**c.getn('phases', 'zstart', 'zstop')):
741 print(path.describe(path.endgaps(c.zstart, c.zstop), c.as_degrees))
743 elif command in ('plot-xt', 'plot-xp', 'plot-rays', 'plot'):
744 if command in ('plot-xt', 'plot'):
745 c = optparse(
746 ('model', 'phases',),
747 ('zstart', 'zstop', 'distances', 'as_degrees', 'vred',
748 'phase_colors', 'save', 'size', 'show'),
749 usage=subusage, descr=descr, args=args)
750 else:
751 c = optparse(
752 ('model', 'phases'),
753 ('zstart', 'zstop', 'distances', 'as_degrees', 'aspect',
754 'shade_model', 'phase_colors', 'save', 'size', 'show'),
755 usage=subusage, descr=descr, args=args)
757 mod = c.model
758 paths = mod.gather_paths(**c.getn('phases', 'zstart', 'zstop'))
760 if c.distances is not None:
761 arrivals = mod.arrivals(
762 **c.getn('phases', 'zstart', 'zstop', 'distances'))
763 else:
764 arrivals = None
766 fig, axes, showplt = plot_init(c.size, c.save, c.show)
768 if command == 'plot-xp':
769 plot.my_xp_plot(
770 paths, c.zstart, c.zstop, c.distances,
771 c.as_degrees, show=showplt, phase_colors=c.phase_colors)
773 elif command == 'plot-xt':
774 plot.my_xt_plot(
775 paths, c.zstart, c.zstop, c.distances, c.as_degrees,
776 vred=c.vred, show=showplt,
777 phase_colors=c.phase_colors)
779 elif command == 'plot-rays':
780 if c.as_degrees:
781 plot.my_rays_plot_gcs(
782 mod, paths, arrivals, c.zstart, c.zstop, c.distances,
783 show=showplt, phase_colors=c.phase_colors)
785 else:
786 plot.my_rays_plot(
787 mod, paths, arrivals, c.zstart, c.zstop, c.distances,
788 show=showplt, aspect=c.aspect, shade_model=c.shade_model,
789 phase_colors=c.phase_colors)
791 elif command == 'plot':
792 plot.my_combi_plot(
793 mod, paths, arrivals, c.zstart, c.zstop, c.distances,
794 c.as_degrees, show=showplt, vred=c.vred,
795 phase_colors=c.phase_colors)
797 try:
798 plot_end(save=c.save, fig=fig, show=c.show)
799 except CakeError as e:
800 exit('cake.py: %s' % str(e))
802 elif command in ('plot-model',):
803 c = optparse(
804 ('model',), ('save', 'size', 'show'),
805 usage=subusage, descr=descr, args=args)
806 mod = c.model
807 fig, axes, showplt = plot_init(c.size, c.save, c.show)
808 plot.my_model_plot(mod, show=showplt)
809 try:
810 plot_end(save=c.save, fig=fig, show=c.show)
811 except CakeError as e:
812 exit('cake.py: %s' % str(e))
814 elif command in ('simplify-model',):
815 c = optparse(('model',), ('accuracy',),
816 usage=subusage, descr=descr, args=args)
817 my_simplify_model(c.model, c.accuracy)
819 elif command in ('list-models',):
820 c = optparse((), (), usage=subusage, descr=descr, args=args)
821 for x in cake.builtin_models():
822 print(x)
824 elif command in ('list-phase-map',):
825 c = optparse((), (), usage=subusage, descr=descr, args=args)
826 defs = cake.PhaseDef.classic_definitions()
827 for k in sorted(defs.keys()):
828 print('%-15s: %s' % (k, ', '.join(defs[k])))
830 elif command in ('scatter',):
831 c = optparse(
832 ('model',),
833 ('slowness', 'interface', 'as_degrees'),
834 usage=subusage, descr=descr, args=args)
836 print_scatter(c.model, p=c.slowness, interface=c.interface)
838 elif command in ('help-options',):
839 optparse(
840 (),
841 ('model', 'accuracy', 'slowness', 'interface', 'phases',
842 'distances', 'zstart', 'zstop', 'distances', 'as_degrees',
843 'material', 'vred', 'save'),
844 usage='cake help-options', descr='list all available options',
845 args=args)
847 elif command in ('--help', '-h', 'help'):
848 sys.exit('Usage: %s' % usage)
850 else:
851 sys.exit('cake: no such subcommand: %s' % command)
854if __name__ == '__main__':
855 main()