def setup_distances_crusts(stations, events, do_round=True): ''' Propose a fomosto store configuration for P-pP Array beam forming. :param event: Event instance :param station: Station instance.''' distances = {} models = {} for s in stations: for e in events: event_profile = crust2x2.get_profile(e.lat, e.lon) station_profile = crust2x2.get_profile(s.lat, s.lon) k1 = station_profile._ident k2 = event_profile._ident models[k1] = station_profile models[k2] = event_profile key = (s.station, k1, k2) distance = ortho.distance_accurate50m(e, s) if not key in distances: if do_round: distances[key] = (math.floor(distance - 1), math.ceil(distance + 1)) else: distances[key] = (distance - 1, distance + 1) else: d1, d2 = distances[key] if do_round: distances[key] = (min(d1, math.floor(distance - 1)), max(d2, math.ceil(distance + 1))) else: distances[key] = (min(d1, distance - 1), max(d2, distance + 1)) return distances, models
def setup_distances_crusts(stations, events, do_round=True): ''' Propose a fomosto store configuration for P-pP Array beam forming. :param event: Event instance :param station: Station instance.''' distances = {} models = {} for s in stations: for e in events: event_profile = crust2x2.get_profile(e.lat, e.lon) station_profile = crust2x2.get_profile(s.lat, s.lon) k1 = station_profile._ident k2 = event_profile._ident models[k1] = station_profile models[k2] = event_profile key = (s.station, k1, k2) distance = ortho.distance_accurate50m(e, s) if not key in distances: if do_round: distances[key] = (math.floor(distance-1), math.ceil(distance+1)) else: distances[key] = (distance-1, distance+1) else: d1, d2 = distances[key] if do_round: distances[key] = (min(d1, math.floor(distance-1)), max(d2, math.ceil(distance+1))) else: distances[key] = (min(d1, distance-1), max(d2, distance+1)) return distances, models
def optparse(required=(), optional=(), args=sys.argv, usage='%prog [options]', descr=None): want = required + optional parser = OptionParser(prog='cake', usage=usage, description=descr.capitalize() + '.', add_help_option=False, formatter=util.BetterHelpFormatter()) parser.add_option('-h', '--help', action='help', help='Show help message and exit.') if 'phases' in want: group = OptionGroup( parser, 'Phases', ''' Seismic phase arrivals may be either specified as traditional phase names (e.g. P, S, PP, PcP, ...) or in Cake's own syntax which is more powerful. Use the --classic option, for traditional phase names. Use the --phase option if you want to define phases in Cake's syntax. ''') group.add_option( '--phase', '--phases', dest='phases', action="append", default=[], metavar='PHASE1,PHASE2,...', help='''Comma separated list of seismic phases in Cake\'s syntax. The definition of a seismic propagation path in Cake's phase syntax is a string consisting of an alternating sequence of "legs" and "knees". A "leg" represents seismic wave propagation without any conversions, encountering only super-critical reflections. Legs are denoted by "P", "p", "S", or "s". The capital letters are used when the take-off of the "leg" is in downward direction, while the lower case letters indicate a take-off in upward direction. A "knee" is an interaction with an interface. It can be a mode conversion, a reflection, or propagation as a headwave or diffracted wave. * conversion is simply denoted as: "(INTERFACE)" or "DEPTH" * upperside reflection: "v(INTERFACE)" or "vDEPTH" * underside reflection: "^(INTERFACE)" or "^DEPTH" * normal kind headwave or diffracted wave: "v_(INTERFACE)" or "v_DEPTH" The interface may be given by name or by depth: INTERFACE is the name of an interface defined in the model, DEPTH is the depth of an interface in [km] (the interface closest to that depth is chosen). If two legs appear consecutively without an explicit "knee", surface interaction is assumed. The preferred standard interface names in cake are "conrad", "moho", "cmb" (core-mantle boundary), and "cb" (inner core boundary). The phase definition may end with a backslash "\\", to indicate that the ray should arrive at the receiver from above instead of from below. It is possible to restrict the maximum and minimum depth of a "leg" by appending "<(INTERFACE)" or "<DEPTH" or ">(INTERFACE)" or ">DEPTH" after the leg character, respectively. When plotting rays or travel-time curves, the color can be set by appending "{COLOR}" to the phase definition, where COLOR is the name of a color or an RGB or RGBA color tuple in the format "R/G/B" or "R/G/B/A", respectively. The values can be normalized to the range [0, 1] or to [0, 255]. The latter is only assumed when any of the values given exceeds 1.0. ''') group.add_option( '--classic', dest='classic_phases', action='append', default=[], metavar='PHASE1,PHASE2,...', help='''Comma separated list of seismic phases in classic nomenclature. Run "cake list-phase-map" for a list of available phase names. When plotting, color can be specified in the same way as in --phases.''') parser.add_option_group(group) if 'model' in want: group = OptionGroup(parser, 'Model') group.add_option( '--model', dest='model_filename', metavar='(NAME or FILENAME)', help='Use builtin model named NAME or user model from file ' 'FILENAME. By default, the "ak135-f-continental.m" model is ' 'used. Run "cake list-models" for a list of builtin models.') group.add_option( '--format', dest='model_format', metavar='FORMAT', choices=['nd', 'hyposat'], default='nd', help='Set model file format (available: nd, hyposat; default: ' 'nd).') group.add_option( '--crust2loc', dest='crust2loc', metavar='LAT,LON', help='Set model from CRUST2.0 profile at location (LAT,LON).') group.add_option( '--crust2profile', dest='crust2profile', metavar='KEY', help='Set model from CRUST2.0 profile with given KEY.') parser.add_option_group(group) if any(x in want for x in ('zstart', 'zstop', 'distances', 'sloc', 'rloc')): group = OptionGroup(parser, 'Source-receiver geometry') if 'zstart' in want: group.add_option('--sdepth', dest='sdepth', type='float', default=0.0, metavar='FLOAT', help='Source depth [km] (default: 0)') if 'zstop' in want: group.add_option('--rdepth', dest='rdepth', type='float', default=0.0, metavar='FLOAT', help='Receiver depth [km] (default: 0)') if 'distances' in want: group.add_option('--distances', dest='sdist', metavar='DISTANCES', help='Surface distances as "start:stop:n" or ' '"dist1,dist2,..." [km]') group.add_option('--sloc', dest='sloc', metavar='LAT,LON', help='Source location (LAT,LON).') group.add_option('--rloc', dest='rloc', metavar='LAT,LON', help='Receiver location (LAT,LON).') parser.add_option_group(group) if 'material' in want: group = OptionGroup( parser, 'Material', 'An isotropic elastic material may be specified by giving ' 'a combination of some of the following options. ') group.add_option('--vp', dest='vp', default=None, type='float', metavar='FLOAT', help='P-wave velocity [km/s]') group.add_option('--vs', dest='vs', default=None, type='float', metavar='FLOAT', help='S-wave velocity [km/s]') group.add_option('--rho', dest='rho', default=None, type='float', metavar='FLOAT', help='density [g/cm**3]') group.add_option('--qp', dest='qp', default=None, type='float', metavar='FLOAT', help='P-wave attenuation Qp (default: 1456)') group.add_option('--qs', dest='qs', default=None, type='float', metavar='FLOAT', help='S-wave attenuation Qs (default: 600)') group.add_option('--poisson', dest='poisson', default=None, type='float', metavar='FLOAT', help='Poisson ratio') group.add_option('--lambda', dest='lame_lambda', default=None, type='float', metavar='FLOAT', help='Lame parameter lambda [GPa]') group.add_option('--mu', dest='lame_mu', default=None, type='float', metavar='FLOAT', help='Shear modulus [GPa]') group.add_option('--qk', dest='qk', default=None, type='float', metavar='FLOAT', help='Bulk attenuation Qk') group.add_option('--qmu', dest='qmu', default=None, type='float', metavar='FLOAT', help='Shear attenuation Qmu') parser.add_option_group(group) if any(x in want for x in ('vred', 'as_degrees', 'accuracy', 'slowness', 'interface', 'aspect', 'shade_model')): group = OptionGroup(parser, 'General') if 'vred' in want: group.add_option('--vred', dest='vred', type='float', metavar='FLOAT', help='Velocity for time reduction in plot [km/s]') if 'as_degrees' in want: group.add_option( '--degrees', dest='as_degrees', action='store_true', default=False, help='Distances are in [deg] instead of [km], velocities in ' '[deg/s] instead of [km/s], slownesses in [s/deg] ' 'instead of [s/km].') if 'accuracy' in want: group.add_option('--accuracy', dest='accuracy', type='float', metavar='MAXIMUM_RELATIVE_RMS', default=0.002, help='Set accuracy for model simplification.') if 'slowness' in want: group.add_option( '--slowness', dest='slowness', type='float', metavar='FLOAT', default=0.0, help='Select surface slowness [s/km] (default: 0)') if 'interface' in want: group.add_option('--interface', dest='interface', metavar='(NAME or DEPTH)', help='Name or depth [km] of interface to select') if 'aspect' in want: group.add_option('--aspect', dest='aspect', type='float', metavar='FLOAT', help='Aspect ratio for plot') if 'shade_model' in want: group.add_option('--no-shade-model', dest='shade_model', action='store_false', default=True, help='Suppress shading of earth model layers') parser.add_option_group(group) if any(x in want for x in ('output_format', )): group = OptionGroup(parser, 'Output') if 'output_format' in want: group.add_option( '--output-format', dest='output_format', metavar='FORMAT', default='textual', choices=('textual', 'nd'), help='Set model output format (available: textual, nd, ' 'default: textual)') parser.add_option_group(group) if usage == 'cake help-options': parser.print_help() (options, args) = parser.parse_args(args) if len(args) != 2: parser.error( 'Cake arguments should look like "--option" or "--option=...".') d = {} as_degrees = False if 'as_degrees' in want: as_degrees = options.as_degrees d['as_degrees'] = as_degrees if 'accuracy' in want: d['accuracy'] = options.accuracy if 'output_format' in want: d['output_format'] = options.output_format if 'aspect' in want: d['aspect'] = options.aspect if 'shade_model' in want: d['shade_model'] = options.shade_model if 'phases' in want: phases = [] phase_colors = {} try: for ss in options.phases: for s in ss.split(','): s = process_color(s, phase_colors) phases.append(cake.PhaseDef(s)) for pp in options.classic_phases: for p in pp.split(','): p = process_color(p, phase_colors) phases.extend(cake.PhaseDef.classic(p)) except (cake.PhaseDefParseError, cake.UnknownClassicPhase) as e: parser.error(e) if not phases and 'phases' in required: s = process_color('P', phase_colors) phases.append(cake.PhaseDef(s)) if phases: d['phase_colors'] = phase_colors d['phases'] = phases if 'model' in want: if options.model_filename: d['model'] = cake.load_model(options.model_filename, options.model_format) if options.crust2loc or options.crust2profile: if options.crust2loc: try: args = tuple( [float(x) for x in options.crust2loc.split(',')]) except Exception: parser.error('format for --crust2loc option is ' '"LATITUDE,LONGITUDE"') elif options.crust2profile: args = (options.crust2profile.upper(), ) else: assert False if 'model' in d: d['model'] = d['model'].replaced_crust(args) else: from pyrocko import crust2x2 profile = crust2x2.get_profile(*args) d['model'] = cake.LayeredModel.from_scanlines( cake.from_crust2x2_profile(profile)) if 'vred' in want: d['vred'] = options.vred if d['vred'] is not None: if not as_degrees: d['vred'] *= r2d * cake.km / cake.earthradius if 'distances' in want: distances = None if options.sdist: if options.sdist.find(':') != -1: ssn = options.sdist.split(':') if len(ssn) != 3: parser.error('format for distances is ' '"min_distance:max_distance:n_distances"') distances = num.linspace(*map(float, ssn)) else: distances = num.array(list(map(float, options.sdist.split(','))), dtype=num.float) if not as_degrees: distances *= r2d * cake.km / cake.earthradius if options.sloc and options.rloc: try: slat, slon = tuple([float(x) for x in options.sloc.split(',')]) rlat, rlon = tuple([float(x) for x in options.rloc.split(',')]) except Exception: parser.error('format for --sloc and --rloc options is ' '"LATITUDE,LONGITUDE"') distance_sr = orthodrome.distance_accurate50m_numpy( slat, slon, rlat, rlon) distance_sr *= r2d / cake.earthradius if distances is not None: distances = num.concatenate((distances, [distance_sr])) else: distances = num.array([distance_sr], dtype=num.float) if distances is not None: d['distances'] = distances else: if 'distances' not in required: d['distances'] = None if 'slowness' in want: d['slowness'] = options.slowness / cake.d2r if not as_degrees: d['slowness'] /= cake.km * cake.m2d if 'interface' in want: if options.interface: try: d['interface'] = float(options.interface) * cake.km except ValueError: d['interface'] = options.interface else: d['interface'] = None if 'zstart' in want: d['zstart'] = options.sdepth * cake.km if 'zstop' in want: d['zstop'] = options.rdepth * cake.km if 'material' in want: md = {} userfactor = dict(vp=1000., vs=1000., rho=1000., qp=1., qs=1., qmu=1., qk=1., lame_lambda=1.0e9, lame_mu=1.0e9, poisson=1.) for k in userfactor.keys(): if getattr(options, k) is not None: md[k] = getattr(options, k) * userfactor[k] if not (bool('lame_lambda' in md) == bool('lame_mu' in md)): parser.error('lambda and mu must be specified both.') if 'lame_lambda' in md and 'lame_mu' in md: md['lame'] = md.pop('lame_lambda'), md.pop('lame_mu') if md: try: d['material'] = cake.Material(**md) except cake.InvalidArguments as e: parser.error(str(e)) for k in list(d.keys()): if k not in want: del d[k] for k in required: if k not in d: if k == 'model': d['model'] = cake.load_model('ak135-f-continental.m') elif k == 'distances': d['distances'] = num.linspace(10*cake.km, 100*cake.km, 10) \ / cake.earthradius * r2d elif k == 'phases': d['phases'] = list(map(cake.PhaseDef, 'Pp')) else: parser.error('missing %s' % k) return Anon(d)
def optparse( required=(), optional=(), args=sys.argv, usage='%prog [options]', descr=None): want = required + optional parser = OptionParser( prog='cake', usage=usage, description=descr.capitalize()+'.', add_help_option=False, formatter=util.BetterHelpFormatter()) parser.add_option( '-h', '--help', action='help', help='Show help message and exit.') if 'phases' in want: group = OptionGroup(parser, 'Phases', ''' Seismic phase arrivals may be either specified as traditional phase names (e.g. P, S, PP, PcP, ...) or in Cake's own syntax which is more powerful. Use the --classic option, for traditional phase names. Use the --phase option if you want to define phases in Cake's syntax. ''') group.add_option( '--phase', '--phases', dest='phases', action="append", default=[], metavar='PHASE1,PHASE2,...', help='''Comma separated list of seismic phases in Cake\'s syntax. The definition of a seismic propagation path in Cake's phase syntax is a string consisting of an alternating sequence of "legs" and "knees". A "leg" represents seismic wave propagation without any conversions, encountering only super-critical reflections. Legs are denoted by "P", "p", "S", or "s". The capital letters are used when the take-off of the "leg" is in downward direction, while the lower case letters indicate a take-off in upward direction. A "knee" is an interaction with an interface. It can be a mode conversion, a reflection, or propagation as a headwave or diffracted wave. * conversion is simply denoted as: "(INTERFACE)" or "DEPTH" * upperside reflection: "v(INTERFACE)" or "vDEPTH" * underside reflection: "^(INTERFACE)" or "^DEPTH" * normal kind headwave or diffracted wave: "v_(INTERFACE)" or "v_DEPTH" The interface may be given by name or by depth: INTERFACE is the name of an interface defined in the model, DEPTH is the depth of an interface in [km] (the interface closest to that depth is chosen). If two legs appear consecutively without an explicit "knee", surface interaction is assumed. The preferred standard interface names in cake are "conrad", "moho", "cmb" (core-mantle boundary), and "cb" (inner core boundary). The phase definition may end with a backslash "\\", to indicate that the ray should arrive at the receiver from above instead of from below. It is possible to restrict the maximum and minimum depth of a "leg" by appending "<(INTERFACE)" or "<DEPTH" or ">(INTERFACE)" or ">DEPTH" after the leg character, respectively. When plotting rays or travel-time curves, the color can be set by appending "{COLOR}" to the phase definition, where COLOR is the name of a color or an RGB or RGBA color tuple in the format "R/G/B" or "R/G/B/A", respectively. The values can be normalized to the range [0, 1] or to [0, 255]. The latter is only assumed when any of the values given exceeds 1.0. ''') group.add_option( '--classic', dest='classic_phases', action='append', default=[], metavar='PHASE1,PHASE2,...', help='''Comma separated list of seismic phases in classic nomenclature. Run "cake list-phase-map" for a list of available phase names. When plotting, color can be specified in the same way as in --phases.''') parser.add_option_group(group) if 'model' in want: group = OptionGroup(parser, 'Model') group.add_option( '--model', dest='model_filename', metavar='(NAME or FILENAME)', help='Use builtin model named NAME or user model from file ' 'FILENAME. By default, the "ak135-f-continental.m" model is ' 'used. Run "cake list-models" for a list of builtin models.') group.add_option( '--format', dest='model_format', metavar='FORMAT', choices=['nd', 'hyposat'], default='nd', help='Set model file format (available: nd, hyposat; default: ' 'nd).') group.add_option( '--crust2loc', dest='crust2loc', metavar='LAT,LON', help='Set model from CRUST2.0 profile at location (LAT,LON).') group.add_option( '--crust2profile', dest='crust2profile', metavar='KEY', help='Set model from CRUST2.0 profile with given KEY.') parser.add_option_group(group) if any(x in want for x in ( 'zstart', 'zstop', 'distances', 'sloc', 'rloc')): group = OptionGroup(parser, 'Source-receiver geometry') if 'zstart' in want: group.add_option( '--sdepth', dest='sdepth', type='float', default=0.0, metavar='FLOAT', help='Source depth [km] (default: 0)') if 'zstop' in want: group.add_option( '--rdepth', dest='rdepth', type='float', default=0.0, metavar='FLOAT', help='Receiver depth [km] (default: 0)') if 'distances' in want: group.add_option( '--distances', dest='sdist', metavar='DISTANCES', help='Surface distances as "start:stop:n" or ' '"dist1,dist2,..." [km]') group.add_option( '--sloc', dest='sloc', metavar='LAT,LON', help='Source location (LAT,LON).') group.add_option( '--rloc', dest='rloc', metavar='LAT,LON', help='Receiver location (LAT,LON).') parser.add_option_group(group) if 'material' in want: group = OptionGroup( parser, 'Material', 'An isotropic elastic material may be specified by giving ' 'a combination of some of the following options. ') group.add_option( '--vp', dest='vp', default=None, type='float', metavar='FLOAT', help='P-wave velocity [km/s]') group.add_option( '--vs', dest='vs', default=None, type='float', metavar='FLOAT', help='S-wave velocity [km/s]') group.add_option( '--rho', dest='rho', default=None, type='float', metavar='FLOAT', help='density [g/cm**3]') group.add_option( '--qp', dest='qp', default=None, type='float', metavar='FLOAT', help='P-wave attenuation Qp (default: 1456)') group.add_option( '--qs', dest='qs', default=None, type='float', metavar='FLOAT', help='S-wave attenuation Qs (default: 600)') group.add_option( '--poisson', dest='poisson', default=None, type='float', metavar='FLOAT', help='Poisson ratio') group.add_option( '--lambda', dest='lame_lambda', default=None, type='float', metavar='FLOAT', help='Lame parameter lambda [GPa]') group.add_option( '--mu', dest='lame_mu', default=None, type='float', metavar='FLOAT', help='Shear modulus [GPa]') group.add_option( '--qk', dest='qk', default=None, type='float', metavar='FLOAT', help='Bulk attenuation Qk') group.add_option( '--qmu', dest='qmu', default=None, type='float', metavar='FLOAT', help='Shear attenuation Qmu') parser.add_option_group(group) if any(x in want for x in ( 'vred', 'as_degrees', 'accuracy', 'slowness', 'interface', 'aspect', 'shade_model')): group = OptionGroup(parser, 'General') if 'vred' in want: group.add_option( '--vred', dest='vred', type='float', metavar='FLOAT', help='Velocity for time reduction in plot [km/s]') if 'as_degrees' in want: group.add_option( '--degrees', dest='as_degrees', action='store_true', default=False, help='Distances are in [deg] instead of [km], velocities in ' '[deg/s] instead of [km/s], slownesses in [s/deg] ' 'instead of [s/km].') if 'accuracy' in want: group.add_option( '--accuracy', dest='accuracy', type='float', metavar='MAXIMUM_RELATIVE_RMS', default=0.002, help='Set accuracy for model simplification.') if 'slowness' in want: group.add_option( '--slowness', dest='slowness', type='float', metavar='FLOAT', default=0.0, help='Select surface slowness [s/km] (default: 0)') if 'interface' in want: group.add_option( '--interface', dest='interface', metavar='(NAME or DEPTH)', help='Name or depth [km] of interface to select') if 'aspect' in want: group.add_option( '--aspect', dest='aspect', type='float', metavar='FLOAT', help='Aspect ratio for plot') if 'shade_model' in want: group.add_option( '--no-shade-model', dest='shade_model', action='store_false', default=True, help='Suppress shading of earth model layers') parser.add_option_group(group) if any(x in want for x in ('output_format',)): group = OptionGroup(parser, 'Output') if 'output_format' in want: group.add_option( '--output-format', dest='output_format', metavar='FORMAT', default='textual', choices=('textual', 'nd'), help='Set model output format (available: textual, nd, ' 'default: textual)') parser.add_option_group(group) if usage == 'cake help-options': parser.print_help() (options, args) = parser.parse_args(args) if len(args) != 2: parser.error( 'Cake arguments should look like "--option" or "--option=...".') d = {} as_degrees = False if 'as_degrees' in want: as_degrees = options.as_degrees d['as_degrees'] = as_degrees if 'accuracy' in want: d['accuracy'] = options.accuracy if 'output_format' in want: d['output_format'] = options.output_format if 'aspect' in want: d['aspect'] = options.aspect if 'shade_model' in want: d['shade_model'] = options.shade_model if 'phases' in want: phases = [] phase_colors = {} try: for ss in options.phases: for s in ss.split(','): s = process_color(s, phase_colors) phases.append(cake.PhaseDef(s)) for pp in options.classic_phases: for p in pp.split(','): p = process_color(p, phase_colors) phases.extend(cake.PhaseDef.classic(p)) except (cake.PhaseDefParseError, cake.UnknownClassicPhase) as e: parser.error(e) if not phases and 'phases' in required: s = process_color('P', phase_colors) phases.append(cake.PhaseDef(s)) if phases: d['phase_colors'] = phase_colors d['phases'] = phases if 'model' in want: if options.model_filename: d['model'] = cake.load_model( options.model_filename, options.model_format) if options.crust2loc or options.crust2profile: if options.crust2loc: try: args = tuple( [float(x) for x in options.crust2loc.split(',')]) except Exception: parser.error( 'format for --crust2loc option is ' '"LATITUDE,LONGITUDE"') elif options.crust2profile: args = (options.crust2profile.upper(),) else: assert False if 'model' in d: d['model'] = d['model'].replaced_crust(args) else: from pyrocko import crust2x2 profile = crust2x2.get_profile(*args) d['model'] = cake.LayeredModel.from_scanlines( cake.from_crust2x2_profile(profile)) if 'vred' in want: d['vred'] = options.vred if d['vred'] is not None: if not as_degrees: d['vred'] *= r2d * cake.km / cake.earthradius if 'distances' in want: distances = None if options.sdist: if options.sdist.find(':') != -1: ssn = options.sdist.split(':') if len(ssn) != 3: parser.error( 'format for distances is ' '"min_distance:max_distance:n_distances"') distances = num.linspace(*map(float, ssn)) else: distances = num.array( list(map( float, options.sdist.split(','))), dtype=num.float) if not as_degrees: distances *= r2d * cake.km / cake.earthradius if options.sloc and options.rloc: try: slat, slon = tuple([float(x) for x in options.sloc.split(',')]) rlat, rlon = tuple([float(x) for x in options.rloc.split(',')]) except Exception: parser.error( 'format for --sloc and --rloc options is ' '"LATITUDE,LONGITUDE"') distance_sr = orthodrome.distance_accurate50m_numpy( slat, slon, rlat, rlon) distance_sr *= r2d / cake.earthradius if distances is not None: distances = num.concatenate((distances, [distance_sr])) else: distances = num.array([distance_sr], dtype=num.float) if distances is not None: d['distances'] = distances else: if 'distances' not in required: d['distances'] = None if 'slowness' in want: d['slowness'] = options.slowness/cake.d2r if not as_degrees: d['slowness'] /= cake.km*cake.m2d if 'interface' in want: if options.interface: try: d['interface'] = float(options.interface)*cake.km except ValueError: d['interface'] = options.interface else: d['interface'] = None if 'zstart' in want: d['zstart'] = options.sdepth*cake.km if 'zstop' in want: d['zstop'] = options.rdepth*cake.km if 'material' in want: md = {} userfactor = dict( vp=1000., vs=1000., rho=1000., qp=1., qs=1., qmu=1., qk=1., lame_lambda=1.0e9, lame_mu=1.0e9, poisson=1.) for k in userfactor.keys(): if getattr(options, k) is not None: md[k] = getattr(options, k) * userfactor[k] if not (bool('lame_lambda' in md) == bool('lame_mu' in md)): parser.error('lambda and mu must be specified both.') if 'lame_lambda' in md and 'lame_mu' in md: md['lame'] = md.pop('lame_lambda'), md.pop('lame_mu') if md: try: d['material'] = cake.Material(**md) except cake.InvalidArguments as e: parser.error(str(e)) for k in list(d.keys()): if k not in want: del d[k] for k in required: if k not in d: if k == 'model': d['model'] = cake.load_model('ak135-f-continental.m') elif k == 'distances': d['distances'] = num.linspace(10*cake.km, 100*cake.km, 10) \ / cake.earthradius * r2d elif k == 'phases': d['phases'] = list(map(cake.PhaseDef, 'Pp')) else: parser.error('missing %s' % k) return Anon(d)
def call(self): self.cleanup() viewer = self.get_viewer() master = viewer.get_active_event() if master is None: self.fail('no master event selected') stations = list(viewer.stations.values()) stations.sort(key=lambda s: (s.network,s.station)) if not stations: self.fail('no station information available') # gather events to be processed events = [] for m in viewer.markers: if isinstance(m, EventMarker): if m.kind == 0: events.append( m.get_event() ) events.sort(key=lambda ev: ev.time) event_to_number = {} for iev, ev in enumerate(events): event_to_number[ev] = iev if self.model_select.startswith('Global'): model_key = 'global' else: model_key = master.lat, master.lon if model_key != self.model_key: if self.model_select.startswith('Global'): self.model = cake.load_model() else: latlon = master.lat, master.lon profile = crust2x2.get_profile(*latlon) profile.set_layer_thickness(crust2x2.LWATER, 0.0) self.model = cake.LayeredModel.from_scanlines( cake.from_crust2x2_profile(profile)) self.model_key = model_key phases = { 'P': ([ cake.PhaseDef(x) for x in 'P p'.split() ], 'Z'), 'S': ([ cake.PhaseDef(x) for x in 'S s'.split() ], 'NE'), } phasenames = phases.keys() phasenames.sort() # synthetic arrivals and ray geometry for master event master_depth = master.depth if self.master_depth_km is not None: master_depth = self.master_depth_km * km tt = {} g = {} for iphase, phasename in enumerate(phasenames): for istation, station in enumerate(stations): dist = orthodrome.distance_accurate50m(master, station) azi = orthodrome.azimuth(master, station) arrivals = self.model.arrivals( phases=phases[phasename][0], distances=[ dist*cake.m2d ], zstart = master_depth, zstop = 0.0) if arrivals: first = arrivals[0] tt[station.network, station.station, phasename] = first.t takeoff = first.takeoff_angle() u = first.path.first_straight().u_in(first.endgaps) g[iphase, istation] = num.array([ math.cos(azi*d2r) * math.sin(takeoff*d2r) * u, math.sin(azi*d2r) * math.sin(takeoff*d2r) * u, math.cos(takeoff*d2r) * u ]) # gather picks for each event for ev in events: picks = {} for m2 in viewer.markers: if isinstance(m2, PhaseMarker) and m2.kind == 0: if m2.get_event() == ev: net, sta, _, _ = m2.one_nslc() picks[net,sta,m2.get_phasename()] = (m2.tmax + m2.tmin) / 2.0 ev.picks = picks # time corrections for extraction windows dataobs = [] datasyn = [] for phasename in phasenames: for station in stations: nsp = station.network, station.station, phasename datasyn.append(tt.get(nsp,None)) for ev in events: if nsp in ev.picks: ttobs = ev.picks[nsp] - ev.time else: ttobs = None dataobs.append(ttobs) ttsyn = num.array(datasyn, dtype=num.float).reshape(( len(phasenames), len(stations))) ttobs = num.array(dataobs, dtype=num.float).reshape(( len(phasenames), len(stations), len(events))) ttres = ttobs - ttsyn[:,:,num.newaxis] tt_corr_event = num.nansum( ttres, axis=1) / \ num.nansum( num.isfinite(ttres), axis=1 ) tt_corr_event = num.where(num.isfinite(tt_corr_event), tt_corr_event, 0.) ttres -= tt_corr_event[:,num.newaxis,:] tt_corr_station = num.nansum( ttres, axis=2) / \ num.nansum( num.isfinite(ttres), axis=2 ) tt_corr_station = num.where(num.isfinite(tt_corr_station), tt_corr_station, 0.) ttres -= tt_corr_station[:,:, num.newaxis] tevents_raw = num.array( [ ev.time for ev in events ] ) tevents_corr = tevents_raw + num.mean(tt_corr_event, axis=0) # print timing information print 'timing stats' for iphasename, phasename in enumerate(phasenames): data = [] for ev in events: iev = event_to_number[ev] for istation, station in enumerate(stations): nsp = station.network, station.station, phasename if nsp in tt and nsp in ev.picks: tarr = ev.time + tt[nsp] tarr_ec = tarr + tt_corr_event[iphasename, iev] tarr_ec_sc = tarr_ec + tt_corr_station[iphasename, istation] tobs = ev.picks[nsp] data.append((tobs-tarr, tobs-tarr_ec, tobs-tarr_ec_sc)) if data: data = num.array(data, dtype=num.float).T print 'event %10s %3s %3i %15.2g %15.2g %15.2g' % ( (ev.name, phasename, data.shape[1]) + tuple( num.mean(num.abs(x)) for x in data )) else: print 'event %10s %3s no picks' % (ev.name, phasename) # extract and preprocess waveforms tpad = 0.0 for f in self.corner_highpass, self.corner_lowpass: if f is not None: tpad = max(tpad, 1.0/f) pile = self.get_pile() waveforms = {} for ev in events: iev = event_to_number[ev] markers = [] for iphasename, phasename in enumerate(phasenames): for istation, station in enumerate(stations): nsp = station.network, station.station, phasename if nsp in tt: tarr = ev.time + tt[nsp] nslcs = [ ( station.network, station.station, '*', '*' ) ] marker = PhaseMarker( nslcs, tarr, tarr, 1, event=ev, phasename=phasename) markers.append(marker) tarr2 = tarr + tt_corr_station[iphasename, istation] + \ tt_corr_event[iphasename, iev] marker = PhaseMarker( nslcs, tarr2, tarr2, 2, event=ev, phasename=phasename) markers.append(marker) tmin = tarr2+self.tstart tmax = tarr2+self.tend marker = PhaseMarker( nslcs, tmin, tmax, 3, event=ev, phasename=phasename) markers.append(marker) trs = pile.all(tmin, tmax, tpad=tpad, trace_selector= lambda tr: tr.nslc_id[:2] == nsp[:2], want_incomplete=False) trok = [] for tr in trs: if num.all(tr.ydata[0] == tr.ydata): continue if self.corner_highpass: tr.highpass(4, self.corner_highpass) if self.corner_lowpass: tr.lowpass(4, self.corner_lowpass) tr.chop(tmin, tmax) tr.set_location(ev.name) #tr.shift( - (tmin - master.time) ) if num.all(num.isfinite(tr.ydata)): trok.append(tr) waveforms[nsp+(iev,)] = trok self.add_markers(markers) def get_channel(trs, cha): for tr in trs: if tr.channel == cha: return tr return None nevents = len(events) nstations = len(stations) nphases = len(phasenames) # correlate waveforms coefs = num.zeros((nphases, nstations, nevents, nevents)) coefs.fill(num.nan) tshifts = coefs.copy() tshifts_picked = coefs.copy() for iphase, phasename in enumerate(phasenames): for istation, station in enumerate(stations): nsp = station.network, station.station, phasename for a in events: ia = event_to_number[a] for b in events: ib = event_to_number[b] if ia == ib: continue if nsp in a.picks and nsp in b.picks: tshifts_picked[iphase,istation,ia,ib] = \ b.picks[nsp] - a.picks[nsp] wa = waveforms[nsp+(ia,)] wb = waveforms[nsp+(ib,)] channels = list(set([ tr.channel for tr in wa + wb ])) channels.sort() tccs = [] for cha in channels: if cha[-1] not in phases[phasename][1]: continue ta = get_channel(wa, cha) tb = get_channel(wb, cha) if ta is None or tb is None: continue tcc = trace.correlate(ta,tb, mode='full', normalization='normal', use_fft=True) tccs.append(tcc) if not tccs: continue tc = None for tcc in tccs: if tc is None: tc = tcc else: tc.add(tcc) tc.ydata *= 1./len(tccs) tmid = tc.tmin*0.5 + tc.tmax*0.5 tlen = (tc.tmax - tc.tmin)*0.5 tc_cut = tc.chop(tmid-tlen*0.5, tmid+tlen*0.5, inplace=False) tshift, coef = tc_cut.max() if (tshift < tc.tmin + 0.5*tc.deltat or tc.tmax - 0.5*tc.deltat < tshift): continue coefs[iphase,istation,ia,ib] = coef tshifts[iphase,istation,ia,ib] = tshift if self.show_correlation_traces: tc.shift(master.time - (tc.tmax + tc.tmin)/2.) self.add_trace(tc) #tshifts = tshifts_picked coefssum_sta = num.nansum(coefs, axis=2) / num.sum(num.isfinite(coefs), axis=2) csum_sta = num.nansum(coefssum_sta, axis=2) / num.sum(num.isfinite(coefssum_sta), axis=2) for iphase, phasename in enumerate(phasenames): for istation, station in enumerate(stations): print 'station %-5s %s %15.2g' % (station.station, phasename, csum_sta[iphase,istation]) coefssum = num.nansum(coefs, axis=1) / num.sum(num.isfinite(coefs), axis=1) csumevent = num.nansum(coefssum, axis=2) / num.sum(num.isfinite(coefssum), axis=2) above = num.where(num.isfinite(coefs), coefs >= self.min_corr, 0) csumabove = num.sum(num.sum(above, axis=1), axis=2) coefssum = num.ma.masked_invalid(coefssum) print 'correlation stats' for iphase, phasename in enumerate(phasenames): for ievent, event in enumerate(events): print 'event %10s %3s %8i %15.2g' % ( event.name, phasename, csumabove[iphase,ievent], csumevent[iphase,ievent]) # plot event correlation matrix fframe = self.figure_frame() fig = fframe.gcf() for iphase, phasename in enumerate(phasenames): p = fig.add_subplot(1,nphases,iphase+1) p.set_xlabel('Event number') p.set_ylabel('Event number') mesh = p.pcolormesh(coefssum[iphase]) cb = fig.colorbar(mesh, ax=p) cb.set_label('Max correlation coefficient') if self.save: fig.savefig(self.output_filename(dir='correlation.pdf')) fig.canvas.draw() # setup and solve linear system data = [] rows = [] weights = [] for iphase in xrange(nphases): for istation in xrange(nstations): for ia in xrange(nevents): for ib in xrange(ia+1,nevents): k = iphase, istation, ia, ib w = coefs[k] if not num.isfinite(tshifts[k]) \ or not num.isfinite(w) or w < self.min_corr: continue row = num.zeros(nevents*4) row[ia*4:ia*4+3] = g[iphase,istation] row[ia*4+3] = -1.0 row[ib*4:ib*4+3] = -g[iphase,istation] row[ib*4+3] = 1.0 weights.append(w) rows.append(row) data.append(tshifts[iphase,istation,ia,ib]) nsamp = len(data) for i in range(4): row = num.zeros(nevents*4) row[i::4] = 1. rows.append(row) data.append(0.0) if self.fix_depth: for ievent in range(nevents): row = num.zeros(nevents*4) row[ievent*4+2] = 1.0 rows.append(row) data.append(0.0) a = num.array(rows, dtype=num.float) d = num.array(data, dtype=num.float) w = num.array(weights, dtype=num.float) if self.weighting == 'equal': w[:nsamp] = 1.0 elif self.weighting == 'linear': pass elif self.weighting == 'quadratic': w[:nsamp] = w[:nsamp]**2 a[:nsamp,:] *= w[:,num.newaxis] d[:nsamp] *= w[:nsamp] x, residuals, rank, singular = num.linalg.lstsq(a,d) x0 = num.zeros(nevents*4) x0[3::4] = tevents_corr mean_abs_residual0 = num.mean( num.abs((num.dot(a[:nsamp], x0) - d[:nsamp])/w[:nsamp])) mean_abs_residual = num.mean( num.abs((num.dot(a[:nsamp],x) - d[:nsamp])/w[:nsamp])) print mean_abs_residual0, mean_abs_residual # distorted solutions npermutations = 100 noiseamount = mean_abs_residual xdistorteds = [] for i in range(npermutations): dnoisy = d.copy() dnoisy[:nsamp] += num.random.normal(size=nsamp)*noiseamount*w[:nsamp] xdistorted, residuals, rank, singular = num.linalg.lstsq(a,dnoisy) xdistorteds.append(xdistorted) mean_abs_residual = num.mean(num.abs(num.dot(a,xdistorted)[:nsamp] - dnoisy[:nsamp])) tmean = num.mean([ e.time for e in events ]) north = x[0::4] east = x[1::4] down = x[2::4] etime = x[3::4] + tmean def plot_range(x): mi, ma = num.percentile(x, [10., 90.]) ext = (ma-mi)/5. mi -= ext ma += ext return mi, ma lat, lon = orthodrome.ne_to_latlon(master.lat, master.lon, north, east) events_out = [] for ievent, event in enumerate(events): event_out = model.Event(time=etime[ievent], lat=lat[ievent], lon=lon[ievent], depth=down[ievent] + master_depth, name = event.name) mark = EventMarker(event_out, kind=4) self.add_marker(mark) events_out.append(event_out) model.Event.dump_catalog(events_out, 'events.relocated.txt') # plot results ned_orig = [] for event in events: n, e = orthodrome.latlon_to_ne(master, event) d = event.depth ned_orig.append((n,e,d)) ned_orig = num.array(ned_orig) ned_orig[:,0] -= num.mean(ned_orig[:,0]) ned_orig[:,1] -= num.mean(ned_orig[:,1]) ned_orig[:,2] -= num.mean(ned_orig[:,2]) north0, east0, down0 = ned_orig.T north2, east2, down2, time2 = num.hstack(xdistorteds).reshape((-1,4)).T fframe = self.figure_frame() fig = fframe.gcf() color_sym = (0.1,0.1,0.0) color_scat = (0.3,0.5,1.0,0.2) d = u'\u0394 ' if not self.fix_depth: p = fig.add_subplot(2,2,1, aspect=1.0) else: p = fig.add_subplot(1,1,1, aspect=1.0) mi_north, ma_north = plot_range(north) mi_east, ma_east = plot_range(east) mi_down, ma_down = plot_range(down) p.set_xlabel(d+'East [km]') p.set_ylabel(d+'North [km]') p.plot(east2/km, north2/km, '.', color=color_scat, markersize=2) p.plot(east/km, north/km, '+', color=color_sym) p.plot(east0/km, north0/km, 'x', color=color_sym) p0 = p for i,ev in enumerate(events): p.text(east[i]/km, north[i]/km, ev.name, clip_on=True) if not self.fix_depth: p = fig.add_subplot(2,2,2, sharey=p0, aspect=1.0) p.set_xlabel(d+'Depth [km]') p.set_ylabel(d+'North [km]') p.plot(down2/km, north2/km, '.', color=color_scat, markersize=2) p.plot(down/km, north/km, '+', color=color_sym) for i,ev in enumerate(events): p.text(down[i]/km, north[i]/km, ev.name, clip_on=True) p1 = p p = fig.add_subplot(2,2,3, sharex=p0, aspect=1.0) p.set_xlabel(d+'East [km]') p.set_ylabel(d+'Depth [km]') p.plot(east2/km, down2/km, '.', color=color_scat, markersize=2) p.plot(east/km, down/km, '+', color=color_sym) for i,ev in enumerate(events): p.text(east[i]/km, down[i]/km, ev.name, clip_on=True) p.invert_yaxis() p2 = p p0.set_xlim(mi_east/km, ma_east/km) p0.set_ylim(mi_north/km, ma_north/km) if not self.fix_depth: p1.set_xlim(mi_down/km, ma_down/km) p2.set_ylim(mi_down/km, ma_down/km) if self.save: fig.savefig(self.output_filename(dir='locations.pdf')) fig.canvas.draw()
def get_store_id(self, event, st, cha): s = Template(self.template) return s.substitute( id=(crust2x2.get_profile(event.lat, event.lon)._ident).lower())
import sys import numpy as num from pyrocko import crust2x2 lat, lon = sys.argv[1:] p = crust2x2.get_profile(lat, lon) print p print '='*80 print num.array(p.get_weeded()).T