Coverage for /usr/local/lib/python3.13/dist-packages/pyrocko/apps/cake.py: 76%
402 statements
« prev ^ index » next coverage.py v7.6.0, created at 2025-12-04 10:41 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2025-12-04 10:41 +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
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 mpl_init()
619 fig = plt.figure(figsize=mpl_papersize(size, 'landscape'))
620 showplt = bool(show or not save)
621 return fig, showplt
624class CakeError(Exception):
625 pass
628def plot_end(save, fig, show=True):
629 if save:
630 try:
631 fig.savefig(save)
632 if show:
633 plt.show()
634 except OSError as e:
635 raise CakeError(str(e))
636 except ValueError as e:
637 raise CakeError(str(e))
638 else:
639 plt.show()
642def main(args=None):
643 '''
644 CLI entry point for Pyrocko's ``cake`` app.
645 '''
647 if args is None:
648 args = sys.argv[1:]
650 subcommand_descriptions = {
651 'print': 'get information on model/phase/material properties',
652 'arrivals': 'print list of phase arrivals',
653 'paths': 'print ray path details',
654 'plot-xt': 'plot traveltime vs distance curves',
655 'plot-xp': 'plot ray parameter vs distance curves',
656 'plot-rays': 'plot ray propagation paths',
657 'plot': 'plot combination of ray and traveltime curves',
658 'plot-model': 'plot velocity model',
659 'list-models': 'list builtin velocity models',
660 'list-phase-map': 'show translation table for classic phase names',
661 'simplify-model': 'create a simplified version of a layered model',
662 'scatter': 'show details about scattering at model interfaces'}
664 usage = '''cake <subcommand> [options]
666Subcommands:
668 print %(print)s
669 arrivals %(arrivals)s
670 paths %(paths)s
671 plot-xt %(plot_xt)s
672 plot-xp %(plot_xp)s
673 plot-rays %(plot_rays)s
674 plot %(plot)s
675 plot-model %(plot_model)s
676 list-models %(list_models)s
677 list-phase-map %(list_phase_map)s
678 simplify-model %(simplify_model)s
679 scatter %(scatter)s
681To get further help and a list of available options for any subcommand run:
683 cake <subcommand> --help
685'''.strip() % d2u(subcommand_descriptions)
687 usage_sub = 'cake %s [options]'
688 if len(args) < 1:
689 sys.exit('Usage: %s' % usage)
691 command = args[0]
693 args[0:0] = ['cake']
694 descr = subcommand_descriptions.get(command, None)
695 subusage = usage_sub % command
697 if command == 'print':
698 c = optparse(
699 (), ('model', 'phases', 'material', 'output_format'),
700 usage=subusage, descr=descr, args=args)
702 if 'model' in c:
703 if c.output_format == 'textual':
704 print(c.model)
705 print()
706 elif c.output_format == 'nd':
707 cake.write_nd_model_fh(c.model, sys.stdout)
709 if 'phases' in c:
710 for phase in c.phases:
711 print(phase)
712 print()
714 if 'material' in c:
715 print(c.material.describe())
716 print()
718 elif command == 'arrivals':
719 c = optparse(
720 ('model', 'phases', 'distances'),
721 ('zstart', 'zstop', 'as_degrees'),
722 usage=subusage, descr=descr, args=args)
724 print_arrivals(
725 c.model,
726 **c.getn('zstart', 'zstop', 'phases', 'distances', 'as_degrees'))
728 elif command == 'paths':
729 c = optparse(
730 ('model', 'phases'),
731 ('zstart', 'zstop', 'as_degrees'),
732 usage=subusage, descr=descr, args=args)
734 mod = c.model
735 for path in mod.gather_paths(**c.getn('phases', 'zstart', 'zstop')):
736 print(path.describe(path.endgaps(c.zstart, c.zstop), c.as_degrees))
738 elif command in ('plot-xt', 'plot-xp', 'plot-rays', 'plot'):
739 if command in ('plot-xt', 'plot'):
740 c = optparse(
741 ('model', 'phases',),
742 ('zstart', 'zstop', 'distances', 'as_degrees', 'vred',
743 'phase_colors', 'save', 'size', 'show'),
744 usage=subusage, descr=descr, args=args)
745 else:
746 c = optparse(
747 ('model', 'phases'),
748 ('zstart', 'zstop', 'distances', 'as_degrees', 'aspect',
749 'shade_model', 'phase_colors', 'save', 'size', 'show'),
750 usage=subusage, descr=descr, args=args)
752 mod = c.model
753 paths = mod.gather_paths(**c.getn('phases', 'zstart', 'zstop'))
755 if c.distances is not None:
756 arrivals = mod.arrivals(
757 **c.getn('phases', 'zstart', 'zstop', 'distances'))
758 else:
759 arrivals = None
761 fig, showplt = plot_init(c.size, c.save, c.show)
763 if command == 'plot-xp':
764 plot.my_xp_plot(
765 paths, c.zstart, c.zstop, c.distances,
766 c.as_degrees, phase_colors=c.phase_colors, fig=fig)
768 elif command == 'plot-xt':
769 plot.my_xt_plot(
770 paths, c.zstart, c.zstop, c.distances, c.as_degrees,
771 vred=c.vred,
772 phase_colors=c.phase_colors, fig=fig)
774 elif command == 'plot-rays':
775 if c.as_degrees:
776 plot.my_rays_plot_gcs(
777 mod, paths, arrivals, c.zstart, c.zstop, c.distances,
778 phase_colors=c.phase_colors, fig=fig)
780 else:
781 plot.my_rays_plot(
782 mod, paths, arrivals, c.zstart, c.zstop, c.distances,
783 aspect=c.aspect, shade_model=c.shade_model,
784 phase_colors=c.phase_colors, fig=fig)
786 elif command == 'plot':
787 plot.my_combi_plot(
788 mod, paths, arrivals, c.zstart, c.zstop, c.distances,
789 c.as_degrees, vred=c.vred,
790 phase_colors=c.phase_colors, fig=fig)
792 try:
793 plot_end(save=c.save, fig=fig, show=c.show)
794 except CakeError as e:
795 exit('cake.py: %s' % str(e))
797 elif command in ('plot-model',):
798 c = optparse(
799 ('model',), ('save', 'size', 'show'),
800 usage=subusage, descr=descr, args=args)
801 mod = c.model
802 fig, showplt = plot_init(c.size, c.save, c.show)
803 plot.my_model_plot(mod, fig=fig)
804 try:
805 plot_end(save=c.save, fig=fig, show=c.show)
806 except CakeError as e:
807 exit('cake.py: %s' % str(e))
809 elif command in ('simplify-model',):
810 c = optparse(('model',), ('accuracy',),
811 usage=subusage, descr=descr, args=args)
812 my_simplify_model(c.model, c.accuracy)
814 elif command in ('list-models',):
815 c = optparse((), (), usage=subusage, descr=descr, args=args)
816 for x in cake.builtin_models():
817 print(x)
819 elif command in ('list-phase-map',):
820 c = optparse((), (), usage=subusage, descr=descr, args=args)
821 defs = cake.PhaseDef.classic_definitions()
822 for k in sorted(defs.keys()):
823 print('%-15s: %s' % (k, ', '.join(defs[k])))
825 elif command in ('scatter',):
826 c = optparse(
827 ('model',),
828 ('slowness', 'interface', 'as_degrees'),
829 usage=subusage, descr=descr, args=args)
831 print_scatter(c.model, p=c.slowness, interface=c.interface)
833 elif command in ('help-options',):
834 optparse(
835 (),
836 ('model', 'accuracy', 'slowness', 'interface', 'phases',
837 'distances', 'zstart', 'zstop', 'distances', 'as_degrees',
838 'material', 'vred', 'save'),
839 usage='cake help-options', descr='list all available options',
840 args=args)
842 elif command in ('--help', '-h', 'help'):
843 sys.exit('Usage: %s' % usage)
845 else:
846 sys.exit('cake: no such subcommand: %s' % command)
849if __name__ == '__main__':
850 main()