Coverage for /usr/local/lib/python3.11/dist-packages/pyrocko/gf/meta.py: 74%
1425 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'''
7Data model for Green's function store meta-data.
8'''
10import math
11import re
12import fnmatch
13import logging
15import numpy as num
16from scipy.interpolate import interp1d
18from pyrocko.guts import (Object, SObject, String, StringChoice,
19 StringPattern, Unicode, Float, Bool, Int, TBase,
20 List, ValidationError, Timestamp, Tuple, Dict)
21from pyrocko.guts import dump, load # noqa
22from pyrocko.guts_array import literal, Array
23from pyrocko.model import Location, gnss
25from pyrocko import cake, orthodrome, spit, moment_tensor, trace
26from pyrocko.util import num_full
28from .error import StoreError
30try:
31 new_str = unicode
32except NameError:
33 new_str = str
35guts_prefix = 'pf'
37d2r = math.pi / 180.
38r2d = 1.0 / d2r
39km = 1000.
40vicinity_eps = 1e-5
42logger = logging.getLogger('pyrocko.gf.meta')
45def fmt_choices(cls):
46 return 'choices: %s' % ', '.join(
47 "``'%s'``" % choice for choice in cls.choices)
50class InterpolationMethod(StringChoice):
51 choices = ['nearest_neighbor', 'multilinear']
54class SeismosizerTrace(Object):
56 codes = Tuple.T(
57 4, String.T(),
58 default=('', 'STA', '', 'Z'),
59 help='network, station, location and channel codes')
61 data = Array.T(
62 shape=(None,),
63 dtype=num.float32,
64 serialize_as='base64',
65 serialize_dtype=num.dtype('<f4'),
66 help='numpy array with data samples')
68 deltat = Float.T(
69 default=1.0,
70 help='sampling interval [s]')
72 tmin = Timestamp.T(
73 default=Timestamp.D('1970-01-01 00:00:00'),
74 help='time of first sample as a system timestamp [s]')
76 def pyrocko_trace(self):
77 c = self.codes
78 return trace.Trace(c[0], c[1], c[2], c[3],
79 ydata=self.data,
80 deltat=self.deltat,
81 tmin=self.tmin)
83 @classmethod
84 def from_pyrocko_trace(cls, tr, **kwargs):
85 d = dict(
86 codes=tr.nslc_id,
87 tmin=tr.tmin,
88 deltat=tr.deltat,
89 data=num.asarray(tr.get_ydata(), dtype=num.float32))
90 d.update(kwargs)
91 return cls(**d)
94class SeismosizerResult(Object):
95 n_records_stacked = Int.T(optional=True, default=1)
96 t_stack = Float.T(optional=True, default=0.)
99class Result(SeismosizerResult):
100 trace = SeismosizerTrace.T(optional=True)
101 n_shared_stacking = Int.T(optional=True, default=1)
102 t_optimize = Float.T(optional=True, default=0.)
105class StaticResult(SeismosizerResult):
106 result = Dict.T(
107 String.T(),
108 Array.T(shape=(None,), dtype=float, serialize_as='base64'))
111class GNSSCampaignResult(StaticResult):
112 campaign = gnss.GNSSCampaign.T(
113 optional=True)
116class SatelliteResult(StaticResult):
118 theta = Array.T(
119 optional=True,
120 shape=(None,), dtype=float, serialize_as='base64')
122 phi = Array.T(
123 optional=True,
124 shape=(None,), dtype=float, serialize_as='base64')
127class KiteSceneResult(SatelliteResult):
129 shape = Tuple.T()
131 def get_scene(self, component='displacement.los'):
132 try:
133 from kite import Scene
134 except ImportError:
135 raise ImportError('Kite not installed')
137 def reshape(arr):
138 return arr.reshape(self.shape)
140 displacement = self.result[component]
142 displacement, theta, phi = map(
143 reshape, (displacement, self.theta, self.phi))
145 sc = Scene(
146 displacement=displacement,
147 theta=theta, phi=phi,
148 config=self.config)
150 return sc
153class ComponentSchemeDescription(Object):
154 name = String.T()
155 source_terms = List.T(String.T())
156 ncomponents = Int.T()
157 default_stored_quantity = String.T(optional=True)
158 provided_components = List.T(String.T())
161component_scheme_descriptions = [
162 ComponentSchemeDescription(
163 name='elastic2',
164 source_terms=['m0'],
165 ncomponents=2,
166 default_stored_quantity='displacement',
167 provided_components=[
168 'n', 'e', 'd']),
169 ComponentSchemeDescription(
170 name='elastic8',
171 source_terms=['mnn', 'mee', 'mdd', 'mne', 'mnd', 'med'],
172 ncomponents=8,
173 default_stored_quantity='displacement',
174 provided_components=[
175 'n', 'e', 'd']),
176 ComponentSchemeDescription(
177 name='elastic10',
178 source_terms=['mnn', 'mee', 'mdd', 'mne', 'mnd', 'med'],
179 ncomponents=10,
180 default_stored_quantity='displacement',
181 provided_components=[
182 'n', 'e', 'd']),
183 ComponentSchemeDescription(
184 name='elastic18',
185 source_terms=['mnn', 'mee', 'mdd', 'mne', 'mnd', 'med'],
186 ncomponents=18,
187 default_stored_quantity='displacement',
188 provided_components=[
189 'n', 'e', 'd']),
190 ComponentSchemeDescription(
191 name='elastic5',
192 source_terms=['fn', 'fe', 'fd'],
193 ncomponents=5,
194 default_stored_quantity='displacement',
195 provided_components=[
196 'n', 'e', 'd']),
197 ComponentSchemeDescription(
198 name='poroelastic10',
199 source_terms=['pore_pressure'],
200 ncomponents=10,
201 default_stored_quantity=None,
202 provided_components=[
203 'displacement.n', 'displacement.e', 'displacement.d',
204 'vertical_tilt.n', 'vertical_tilt.e',
205 'pore_pressure',
206 'darcy_velocity.n', 'darcy_velocity.e', 'darcy_velocity.d'])]
209# new names?
210# 'mt_to_displacement_1d'
211# 'mt_to_displacement_1d_ff_only'
212# 'mt_to_gravity_1d'
213# 'mt_to_stress_1d'
214# 'explosion_to_displacement_1d'
215# 'sf_to_displacement_1d'
216# 'mt_to_displacement_3d'
217# 'mt_to_gravity_3d'
218# 'mt_to_stress_3d'
219# 'pore_pressure_to_displacement_1d'
220# 'pore_pressure_to_vertical_tilt_1d'
221# 'pore_pressure_to_pore_pressure_1d'
222# 'pore_pressure_to_darcy_velocity_1d'
225component_schemes = [c.name for c in component_scheme_descriptions]
226component_scheme_to_description = dict(
227 (c.name, c) for c in component_scheme_descriptions)
230class ComponentScheme(StringChoice):
231 '''
232 Different Green's Function component schemes are available:
234 ================= =========================================================
235 Name Description
236 ================= =========================================================
237 ``elastic10`` Elastodynamic for
238 :py:class:`~pyrocko.gf.meta.ConfigTypeA` and
239 :py:class:`~pyrocko.gf.meta.ConfigTypeB` stores, MT
240 sources only
241 ``elastic8`` Elastodynamic for far-field only
242 :py:class:`~pyrocko.gf.meta.ConfigTypeA` and
243 :py:class:`~pyrocko.gf.meta.ConfigTypeB` stores,
244 MT sources only
245 ``elastic2`` Elastodynamic for
246 :py:class:`~pyrocko.gf.meta.ConfigTypeA` and
247 :py:class:`~pyrocko.gf.meta.ConfigTypeB` stores, purely
248 isotropic sources only
249 ``elastic5`` Elastodynamic for
250 :py:class:`~pyrocko.gf.meta.ConfigTypeA`
251 and :py:class:`~pyrocko.gf.meta.ConfigTypeB` stores, SF
252 sources only
253 ``elastic18`` Elastodynamic for
254 :py:class:`~pyrocko.gf.meta.ConfigTypeC` stores, MT
255 sources only
256 ``poroelastic10`` Poroelastic for :py:class:`~pyrocko.gf.meta.ConfigTypeA`
257 and :py:class:`~pyrocko.gf.meta.ConfigTypeB` stores
258 ================= =========================================================
259 '''
261 choices = component_schemes
264class Earthmodel1D(Object):
265 dummy_for = cake.LayeredModel
266 dummy_for_description = 'pyrocko.cake.LayeredModel'
268 class __T(TBase):
269 def regularize_extra(self, val):
270 if isinstance(val, str):
271 val = cake.LayeredModel.from_scanlines(
272 cake.read_nd_model_str(val))
274 return val
276 def to_save(self, val):
277 return literal(cake.write_nd_model_str(val))
280class StringID(StringPattern):
281 pattern = r'^[A-Za-z][A-Za-z0-9._]{0,64}$'
284class ScopeType(StringChoice):
285 choices = [
286 'global',
287 'regional',
288 'local',
289 ]
292class WaveType(StringChoice):
293 choices = [
294 'full waveform',
295 'bodywave',
296 'P wave',
297 'S wave',
298 'surface wave',
299 ]
302class NearfieldTermsType(StringChoice):
303 choices = [
304 'complete',
305 'incomplete',
306 'missing',
307 ]
310class QuantityType(StringChoice):
311 choices = [
312 'displacement',
313 'rotation',
314 'velocity',
315 'acceleration',
316 'pressure',
317 'tilt',
318 'pore_pressure',
319 'darcy_velocity',
320 'vertical_tilt']
323class Reference(Object):
324 id = StringID.T()
325 type = String.T()
326 title = Unicode.T()
327 journal = Unicode.T(optional=True)
328 volume = Unicode.T(optional=True)
329 number = Unicode.T(optional=True)
330 pages = Unicode.T(optional=True)
331 year = String.T()
332 note = Unicode.T(optional=True)
333 issn = String.T(optional=True)
334 doi = String.T(optional=True)
335 url = String.T(optional=True)
336 eprint = String.T(optional=True)
337 authors = List.T(Unicode.T())
338 publisher = Unicode.T(optional=True)
339 keywords = Unicode.T(optional=True)
340 note = Unicode.T(optional=True)
341 abstract = Unicode.T(optional=True)
343 @classmethod
344 def from_bibtex(cls, filename=None, stream=None):
346 from pybtex.database.input import bibtex
348 parser = bibtex.Parser()
350 if filename is not None:
351 bib_data = parser.parse_file(filename)
352 elif stream is not None:
353 bib_data = parser.parse_stream(stream)
355 references = []
357 for id_, entry in bib_data.entries.items():
358 d = {}
359 avail = entry.fields.keys()
360 for prop in cls.T.properties:
361 if prop.name == 'authors' or prop.name not in avail:
362 continue
364 d[prop.name] = entry.fields[prop.name]
366 if 'author' in entry.persons:
367 d['authors'] = []
368 for person in entry.persons['author']:
369 d['authors'].append(new_str(person))
371 c = Reference(id=id_, type=entry.type, **d)
372 references.append(c)
374 return references
377_fpat = r'[+-]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?'
378_spat = StringID.pattern[1:-1]
379_cake_pat = cake.PhaseDef.allowed_characters_pattern
380_iaspei_pat = cake.PhaseDef.allowed_characters_pattern_classic
382_ppat = r'(' \
383 r'cake:' + _cake_pat + \
384 r'|iaspei:' + _iaspei_pat + \
385 r'|vel_surface:' + _fpat + \
386 r'|vel:' + _fpat + \
387 r'|stored:' + _spat + \
388 r')'
391timing_regex_old = re.compile(
392 r'^((first|last)?\((' + _spat + r'(\|' + _spat + r')*)\)|(' +
393 _spat + r'))?(' + _fpat + ')?$')
396timing_regex = re.compile(
397 r'^((first|last)?\{(' + _ppat + r'(\|' + _ppat + r')*)\}|(' +
398 _ppat + r'))?(' + _fpat + '(S|%)?)?$')
401class PhaseSelect(StringChoice):
402 choices = ['', 'first', 'last']
405class InvalidTimingSpecification(ValidationError):
406 pass
409class TimingAttributeError(Exception):
410 pass
413class Timing(SObject):
414 '''
415 Definition of a time instant relative to one or more named phase arrivals.
417 Instances of this class can be used e.g. in cutting and tapering
418 operations. They can hold an absolute time or an offset to a named phase
419 arrival or group of such arrivals.
421 Timings can be instantiated from a simple string defintion i.e. with
422 ``Timing(str)`` where ``str`` is something like
423 ``'SELECT{PHASE_DEFS}[+-]OFFSET[S|%]'`` where ``'SELECT'`` is ``'first'``,
424 ``'last'`` or empty, ``'PHASE_DEFS'`` is a ``'|'``-separated list of phase
425 definitions, and ``'OFFSET'`` is the time offset in seconds. If a ``'%'``
426 is appended, it is interpreted as percent. If the an ``'S'`` is appended
427 to ``'OFFSET'``, it is interpreted as a surface slowness in [s/km].
429 Phase definitions can be specified in either of the following ways:
431 * ``'stored:PHASE_ID'`` - retrieves value from stored travel time table
432 * ``'cake:CAKE_PHASE_DEF'`` - evaluates first arrival of phase with cake
433 (see :py:class:`pyrocko.cake.PhaseDef`)
434 * ``'vel_surface:VELOCITY'`` - arrival according to surface distance /
435 velocity [km/s]
436 * ``'vel:VELOCITY'`` - arrival according to 3D-distance / velocity [km/s]
438 **Examples:**
440 * ``'100'`` : absolute time; 100 s
441 * ``'{stored:P}-100'`` : 100 s before arrival of P phase according to
442 stored travel time table named ``'P'``
443 * ``'{stored:P}-5.1S'`` : 10% before arrival of P phase according to
444 stored travel time table named ``'P'``
445 * ``'{stored:P}-10%'`` : 10% before arrival of P phase according to
446 stored travel time table named ``'P'``
447 * ``'{stored:A|stored:B}'`` : time instant of phase arrival A, or B if A is
448 undefined for a given geometry
449 * ``'first{stored:A|stored:B}'`` : as above, but the earlier arrival of A
450 and B is chosen, if both phases are defined for a given geometry
451 * ``'last{stored:A|stored:B}'`` : as above but the later arrival is chosen
452 * ``'first{stored:A|stored:B|stored:C}-100'`` : 100 s before first out of
453 arrivals A, B, and C
454 '''
456 def __init__(self, s=None, **kwargs):
458 if s is not None:
459 offset_is = None
460 s = re.sub(r'\s+', '', s)
461 try:
462 offset = float(s.rstrip('S'))
464 if s.endswith('S'):
465 offset_is = 'slowness'
467 phase_defs = []
468 select = ''
470 except ValueError:
471 matched = False
472 m = timing_regex.match(s)
473 if m:
474 if m.group(3):
475 phase_defs = m.group(3).split('|')
476 elif m.group(19):
477 phase_defs = [m.group(19)]
478 else:
479 phase_defs = []
481 select = m.group(2) or ''
483 offset = 0.0
484 soff = m.group(27)
485 if soff:
486 offset = float(soff.rstrip('S%'))
487 if soff.endswith('S'):
488 offset_is = 'slowness'
489 elif soff.endswith('%'):
490 offset_is = 'percent'
492 matched = True
494 else:
495 m = timing_regex_old.match(s)
496 if m:
497 if m.group(3):
498 phase_defs = m.group(3).split('|')
499 elif m.group(5):
500 phase_defs = [m.group(5)]
501 else:
502 phase_defs = []
504 select = m.group(2) or ''
506 offset = 0.0
507 if m.group(6):
508 offset = float(m.group(6))
510 phase_defs = [
511 'stored:' + phase_def for phase_def in phase_defs]
513 matched = True
515 if not matched:
516 raise InvalidTimingSpecification(s)
518 kwargs = dict(
519 phase_defs=phase_defs,
520 select=select,
521 offset=offset,
522 offset_is=offset_is)
524 SObject.__init__(self, **kwargs)
526 def __str__(self):
527 s = []
528 if self.phase_defs:
529 sphases = '|'.join(self.phase_defs)
530 # if len(self.phase_defs) > 1 or self.select:
531 sphases = '{%s}' % sphases
533 if self.select:
534 sphases = self.select + sphases
536 s.append(sphases)
538 if self.offset != 0.0 or not self.phase_defs:
539 s.append('%+g' % self.offset)
540 if self.offset_is == 'slowness':
541 s.append('S')
542 elif self.offset_is == 'percent':
543 s.append('%')
545 return ''.join(s)
547 def evaluate(self, get_phase, args, attributes=None):
548 try:
549 if self.offset_is == 'slowness' and self.offset != 0.0:
550 phase_offset = get_phase(
551 'vel_surface:%g' % (1.0/self.offset))
552 offset = phase_offset(args)
553 else:
554 offset = self.offset
556 if self.phase_defs:
557 extra_args = ()
558 if attributes:
559 extra_args = (attributes,)
561 phases = [
562 get_phase(phase_def, *extra_args)
563 for phase_def in self.phase_defs]
565 results = [phase(args) for phase in phases]
566 results = [
567 result if isinstance(result, tuple) else (result,)
568 for result in results]
570 results = [
571 result for result in results
572 if result[0] is not None]
574 if self.select == 'first':
575 results.sort(key=lambda result: result[0])
576 elif self.select == 'last':
577 results.sort(key=lambda result: -result[0])
579 if not results:
580 if attributes is not None:
581 return (None,) * (len(attributes) + 1)
582 else:
583 return None
585 else:
586 t, values = results[0][0], results[0][1:]
587 if self.offset_is == 'percent':
588 t = t*(1.+offset/100.)
589 else:
590 t = t + offset
592 if attributes is not None:
593 return (t, ) + values
594 else:
595 return t
597 else:
598 if attributes is not None:
599 raise TimingAttributeError(
600 'Cannot get phase attributes from offset-only '
601 'definition.')
602 return offset
604 except spit.OutOfBounds:
605 raise OutOfBounds(args)
607 phase_defs = List.T(String.T())
608 offset = Float.T(default=0.0)
609 offset_is = String.T(optional=True)
610 select = PhaseSelect.T(
611 default='',
612 help=("Can be either ``'%s'``, ``'%s'``, or ``'%s'``. " %
613 tuple(PhaseSelect.choices)))
616def mkdefs(s):
617 defs = []
618 for x in s.split(','):
619 try:
620 defs.append(float(x))
621 except ValueError:
622 if x.startswith('!'):
623 defs.extend(cake.PhaseDef.classic(x[1:]))
624 else:
625 defs.append(cake.PhaseDef(x))
627 return defs
630class TPDef(Object):
631 '''
632 Maps an arrival phase identifier to an arrival phase definition.
633 '''
635 id = StringID.T(
636 help='name used to identify the phase')
637 definition = String.T(
638 help='definition of the phase in either cake syntax as defined in '
639 ':py:class:`pyrocko.cake.PhaseDef`, or, if prepended with an '
640 '``!``, as a *classic phase name*, or, if it is a simple '
641 'number, as a constant horizontal velocity.')
643 @property
644 def phases(self):
645 return [x for x in mkdefs(self.definition)
646 if isinstance(x, cake.PhaseDef)]
648 @property
649 def horizontal_velocities(self):
650 return [x for x in mkdefs(self.definition) if isinstance(x, float)]
653class OutOfBounds(Exception):
654 def __init__(self, values=None, reason=None):
655 Exception.__init__(self)
656 self.values = values
657 self.reason = reason
658 self.context = None
660 def __str__(self):
661 scontext = ''
662 if self.context:
663 scontext = '\n%s' % str(self.context)
665 if self.reason:
666 scontext += '\n%s' % self.reason
668 if self.values:
669 return 'out of bounds: (%s)%s' % (
670 ', '.join('%g' % x for x in self.values), scontext)
671 else:
672 return 'out of bounds%s' % scontext
675class MultiLocation(Object):
676 '''
677 Unstructured grid of point coordinates.
678 '''
680 lats = Array.T(
681 optional=True, shape=(None,), dtype=float,
682 help='Latitudes of targets.')
683 lons = Array.T(
684 optional=True, shape=(None,), dtype=float,
685 help='Longitude of targets.')
686 north_shifts = Array.T(
687 optional=True, shape=(None,), dtype=float,
688 help='North shifts of targets.')
689 east_shifts = Array.T(
690 optional=True, shape=(None,), dtype=float,
691 help='East shifts of targets.')
692 elevation = Array.T(
693 optional=True, shape=(None,), dtype=float,
694 help='Elevations of targets.')
696 def __init__(self, *args, **kwargs):
697 self._coords5 = None
698 Object.__init__(self, *args, **kwargs)
700 @property
701 def coords5(self):
702 if self._coords5 is not None:
703 return self._coords5
704 props = [self.lats, self.lons, self.north_shifts, self.east_shifts,
705 self.elevation]
706 sizes = [p.size for p in props if p is not None]
707 if not sizes:
708 raise AttributeError('Empty StaticTarget')
709 if num.unique(sizes).size != 1:
710 raise AttributeError('Inconsistent coordinate sizes.')
712 self._coords5 = num.zeros((sizes[0], 5))
713 for idx, p in enumerate(props):
714 if p is not None:
715 self._coords5[:, idx] = p.flatten()
717 return self._coords5
719 @property
720 def ncoords(self):
721 if self.coords5.shape[0] is None:
722 return 0
723 return int(self.coords5.shape[0])
725 def get_latlon(self):
726 '''
727 Get all coordinates as lat/lon.
729 :returns: Coordinates in Latitude, Longitude
730 :rtype: :class:`numpy.ndarray`, (N, 2)
731 '''
732 latlons = num.empty((self.ncoords, 2))
733 for i in range(self.ncoords):
734 latlons[i, :] = orthodrome.ne_to_latlon(*self.coords5[i, :4])
735 return latlons
737 def get_corner_coords(self):
738 '''
739 Returns the corner coordinates of the multi-location object.
741 :returns: In lat/lon: lower left, upper left, upper right, lower right
742 :rtype: tuple
743 '''
744 latlon = self.get_latlon()
745 ll = (latlon[:, 0].min(), latlon[:, 1].min())
746 ur = (latlon[:, 0].max(), latlon[:, 1].max())
747 return (ll, (ll[0], ur[1]), ur, (ur[0], ll[1]))
750class Receiver(Location):
751 codes = Tuple.T(
752 3, String.T(),
753 optional=True,
754 help='network, station, and location codes')
756 def pyrocko_station(self):
757 from pyrocko import model
759 lat, lon = self.effective_latlon
761 return model.Station(
762 network=self.codes[0],
763 station=self.codes[1],
764 location=self.codes[2],
765 lat=lat,
766 lon=lon,
767 depth=self.depth)
770def g(x, d):
771 if x is None:
772 return d
773 else:
774 return x
777class UnavailableScheme(Exception):
778 '''
779 Raised when it is not possible to use the requested component scheme.
780 '''
781 pass
784class InvalidNComponents(Exception):
785 '''
786 Raised when ``ncomponents`` is incompatible with ``component_scheme``.
787 '''
788 pass
791class DiscretizedSource(Object):
792 '''
793 Base class for discretized sources.
795 To compute synthetic seismograms, the parameterized source models (see of
796 :py:class:`~pyrocko.gf.seismosizer.Source` derived classes) are first
797 discretized into a number of point sources. These spacio-temporal point
798 source distributions are represented by subclasses of the
799 :py:class:`DiscretizedSource`. For elastodynamic problems there is the
800 :py:class:`DiscretizedMTSource` for moment tensor point source
801 distributions and the :py:class:`DiscretizedExplosionSource` for pure
802 explosion/implosion type source distributions. The geometry calculations
803 are implemented in the base class. How Green's function components have to
804 be weighted and sumed is defined in the derived classes.
806 Like in the :py:class:`pyrocko.model.location.Location` class, the
807 positions of the point sources contained in the discretized source are
808 defined by a common reference point (:py:attr:`lat`, :py:attr:`lon`) and
809 cartesian offsets to this (:py:attr:`north_shifts`, :py:attr:`east_shifts`,
810 :py:attr:`depths`). Alternatively latitude and longitude of each contained
811 point source can be specified directly (:py:attr:`lats`, :py:attr:`lons`).
812 '''
814 times = Array.T(shape=(None,), dtype=float)
815 lats = Array.T(shape=(None,), dtype=float, optional=True)
816 lons = Array.T(shape=(None,), dtype=float, optional=True)
817 lat = Float.T(optional=True)
818 lon = Float.T(optional=True)
819 north_shifts = Array.T(shape=(None,), dtype=num.float64, optional=True)
820 east_shifts = Array.T(shape=(None,), dtype=num.float64, optional=True)
821 depths = Array.T(shape=(None,), dtype=num.float64)
822 dl = Float.T(optional=True)
823 dw = Float.T(optional=True)
824 nl = Float.T(optional=True)
825 nw = Float.T(optional=True)
827 @classmethod
828 def check_scheme(cls, scheme):
829 '''
830 Check if given GF component scheme is supported by the class.
832 Raises :py:class:`UnavailableScheme` if the given scheme is not
833 supported by this discretized source class.
834 '''
836 if scheme not in cls.provided_schemes:
837 raise UnavailableScheme(
838 'source type "%s" does not support GF component scheme "%s"' %
839 (cls.__name__, scheme))
841 def __init__(self, **kwargs):
842 Object.__init__(self, **kwargs)
843 self._latlons = None
845 def __setattr__(self, name, value):
846 if name in ('lat', 'lon', 'north_shifts', 'east_shifts',
847 'lats', 'lons'):
848 self.__dict__['_latlons'] = None
850 Object.__setattr__(self, name, value)
852 def get_source_terms(self, scheme):
853 raise NotImplementedError()
855 def make_weights(self, receiver, scheme):
856 raise NotImplementedError()
858 @property
859 def effective_latlons(self):
860 '''
861 Property holding the offest-corrected lats and lons of all points.
862 '''
864 if self._latlons is None:
865 if self.lats is not None and self.lons is not None:
866 if (self.north_shifts is not None and
867 self.east_shifts is not None):
868 self._latlons = orthodrome.ne_to_latlon(
869 self.lats, self.lons,
870 self.north_shifts, self.east_shifts)
871 else:
872 self._latlons = self.lats, self.lons
873 else:
874 lat = g(self.lat, 0.0)
875 lon = g(self.lon, 0.0)
876 self._latlons = orthodrome.ne_to_latlon(
877 lat, lon, self.north_shifts, self.east_shifts)
879 return self._latlons
881 @property
882 def effective_north_shifts(self):
883 if self.north_shifts is not None:
884 return self.north_shifts
885 else:
886 return num.zeros(self.times.size)
888 @property
889 def effective_east_shifts(self):
890 if self.east_shifts is not None:
891 return self.east_shifts
892 else:
893 return num.zeros(self.times.size)
895 def same_origin(self, receiver):
896 '''
897 Check if receiver has same reference point.
898 '''
900 return (g(self.lat, 0.0) == receiver.lat and
901 g(self.lon, 0.0) == receiver.lon and
902 self.lats is None and self.lons is None)
904 def azibazis_to(self, receiver):
905 '''
906 Compute azimuths and backaziumuths to/from receiver, for all contained
907 points.
908 '''
910 if self.same_origin(receiver):
911 azis = r2d * num.arctan2(receiver.east_shift - self.east_shifts,
912 receiver.north_shift - self.north_shifts)
913 bazis = azis + 180.
914 else:
915 slats, slons = self.effective_latlons
916 rlat, rlon = receiver.effective_latlon
917 azis = orthodrome.azimuth_numpy(slats, slons, rlat, rlon)
918 bazis = orthodrome.azimuth_numpy(rlat, rlon, slats, slons)
920 return azis, bazis
922 def distances_to(self, receiver):
923 '''
924 Compute distances to receiver for all contained points.
925 '''
926 if self.same_origin(receiver):
927 return num.sqrt((self.north_shifts - receiver.north_shift)**2 +
928 (self.east_shifts - receiver.east_shift)**2)
930 else:
931 slats, slons = self.effective_latlons
932 rlat, rlon = receiver.effective_latlon
933 return orthodrome.distance_accurate50m_numpy(slats, slons,
934 rlat, rlon)
936 def element_coords(self, i):
937 if self.lats is not None and self.lons is not None:
938 lat = float(self.lats[i])
939 lon = float(self.lons[i])
940 else:
941 lat = self.lat
942 lon = self.lon
944 if self.north_shifts is not None and self.east_shifts is not None:
945 north_shift = float(self.north_shifts[i])
946 east_shift = float(self.east_shifts[i])
948 else:
949 north_shift = east_shift = 0.0
951 return lat, lon, north_shift, east_shift
953 def coords5(self):
954 xs = num.zeros((self.nelements, 5))
956 if self.lats is not None and self.lons is not None:
957 xs[:, 0] = self.lats
958 xs[:, 1] = self.lons
959 else:
960 xs[:, 0] = g(self.lat, 0.0)
961 xs[:, 1] = g(self.lon, 0.0)
963 if self.north_shifts is not None and self.east_shifts is not None:
964 xs[:, 2] = self.north_shifts
965 xs[:, 3] = self.east_shifts
967 xs[:, 4] = self.depths
969 return xs
971 @property
972 def nelements(self):
973 return self.times.size
975 @classmethod
976 def combine(cls, sources, **kwargs):
977 '''
978 Combine several discretized source models.
980 Concatenenates all point sources in the given discretized ``sources``.
981 Care must be taken when using this function that the external amplitude
982 factors and reference times of the parameterized (undiscretized)
983 sources match or are accounted for.
984 '''
986 first = sources[0]
988 if not all(type(s) == type(first) for s in sources):
989 raise Exception('DiscretizedSource.combine must be called with '
990 'sources of same type.')
992 latlons = []
993 for s in sources:
994 latlons.append(s.effective_latlons)
996 lats, lons = num.hstack(latlons)
998 if all((s.lats is None and s.lons is None) for s in sources):
999 rlats = num.array([s.lat for s in sources], dtype=float)
1000 rlons = num.array([s.lon for s in sources], dtype=float)
1001 same_ref = num.all(
1002 rlats == rlats[0]) and num.all(rlons == rlons[0])
1003 else:
1004 same_ref = False
1006 cat = num.concatenate
1007 times = cat([s.times for s in sources])
1008 print(times)
1009 depths = cat([s.depths for s in sources])
1011 if same_ref:
1012 lat = first.lat
1013 lon = first.lon
1014 north_shifts = cat([s.effective_north_shifts for s in sources])
1015 east_shifts = cat([s.effective_east_shifts for s in sources])
1016 lats = None
1017 lons = None
1018 else:
1019 lat = None
1020 lon = None
1021 north_shifts = None
1022 east_shifts = None
1024 return cls(
1025 times=times, lat=lat, lon=lon, lats=lats, lons=lons,
1026 north_shifts=north_shifts, east_shifts=east_shifts,
1027 depths=depths, **kwargs)
1029 def centroid_position(self):
1030 moments = self.moments()
1031 norm = num.sum(moments)
1032 if norm != 0.0:
1033 w = moments / num.sum(moments)
1034 else:
1035 w = num.ones(moments.size)
1037 if self.lats is not None and self.lons is not None:
1038 lats, lons = self.effective_latlons
1039 rlat, rlon = num.mean(lats), num.mean(lons)
1040 n, e = orthodrome.latlon_to_ne_numpy(rlat, rlon, lats, lons)
1041 else:
1042 rlat, rlon = g(self.lat, 0.0), g(self.lon, 0.0)
1043 n, e = self.north_shifts, self.east_shifts
1045 cn = num.sum(n*w)
1046 ce = num.sum(e*w)
1047 clat, clon = orthodrome.ne_to_latlon(rlat, rlon, cn, ce)
1049 if self.lats is not None and self.lons is not None:
1050 lat = clat
1051 lon = clon
1052 north_shift = 0.
1053 east_shift = 0.
1054 else:
1055 lat = g(self.lat, 0.0)
1056 lon = g(self.lon, 0.0)
1057 north_shift = cn
1058 east_shift = ce
1060 depth = num.sum(self.depths*w)
1061 time = num.sum(self.times*w)
1062 return tuple(float(x) for x in
1063 (time, lat, lon, north_shift, east_shift, depth))
1066class DiscretizedExplosionSource(DiscretizedSource):
1067 m0s = Array.T(shape=(None,), dtype=float)
1069 provided_schemes = (
1070 'elastic2',
1071 'elastic8',
1072 'elastic10',
1073 )
1075 def get_source_terms(self, scheme):
1076 self.check_scheme(scheme)
1078 if scheme == 'elastic2':
1079 return self.m0s[:, num.newaxis].copy()
1081 elif scheme in ('elastic8', 'elastic10'):
1082 m6s = num.zeros((self.m0s.size, 6))
1083 m6s[:, 0:3] = self.m0s[:, num.newaxis]
1084 return m6s
1085 else:
1086 assert False
1088 def make_weights(self, receiver, scheme):
1089 self.check_scheme(scheme)
1091 azis, bazis = self.azibazis_to(receiver)
1093 sb = num.sin(bazis*d2r-num.pi)
1094 cb = num.cos(bazis*d2r-num.pi)
1096 m0s = self.m0s
1097 n = azis.size
1099 cat = num.concatenate
1100 rep = num.repeat
1102 if scheme == 'elastic2':
1103 w_n = cb*m0s
1104 g_n = filledi(0, n)
1105 w_e = sb*m0s
1106 g_e = filledi(0, n)
1107 w_d = m0s
1108 g_d = filledi(1, n)
1110 elif scheme == 'elastic8':
1111 w_n = cat((cb*m0s, cb*m0s))
1112 g_n = rep((0, 2), n)
1113 w_e = cat((sb*m0s, sb*m0s))
1114 g_e = rep((0, 2), n)
1115 w_d = cat((m0s, m0s))
1116 g_d = rep((5, 7), n)
1118 elif scheme == 'elastic10':
1119 w_n = cat((cb*m0s, cb*m0s, cb*m0s))
1120 g_n = rep((0, 2, 8), n)
1121 w_e = cat((sb*m0s, sb*m0s, sb*m0s))
1122 g_e = rep((0, 2, 8), n)
1123 w_d = cat((m0s, m0s, m0s))
1124 g_d = rep((5, 7, 9), n)
1126 else:
1127 assert False
1129 return (
1130 ('displacement.n', w_n, g_n),
1131 ('displacement.e', w_e, g_e),
1132 ('displacement.d', w_d, g_d),
1133 )
1135 def split(self):
1136 from pyrocko.gf.seismosizer import ExplosionSource
1137 sources = []
1138 for i in range(self.nelements):
1139 lat, lon, north_shift, east_shift = self.element_coords(i)
1140 sources.append(ExplosionSource(
1141 time=float(self.times[i]),
1142 lat=lat,
1143 lon=lon,
1144 north_shift=north_shift,
1145 east_shift=east_shift,
1146 depth=float(self.depths[i]),
1147 moment=float(self.m0s[i])))
1149 return sources
1151 def moments(self):
1152 return self.m0s
1154 def centroid(self):
1155 from pyrocko.gf.seismosizer import ExplosionSource
1156 time, lat, lon, north_shift, east_shift, depth = \
1157 self.centroid_position()
1159 return ExplosionSource(
1160 time=time,
1161 lat=lat,
1162 lon=lon,
1163 north_shift=north_shift,
1164 east_shift=east_shift,
1165 depth=depth,
1166 moment=float(num.sum(self.m0s)))
1168 @classmethod
1169 def combine(cls, sources, **kwargs):
1170 '''
1171 Combine several discretized source models.
1173 Concatenenates all point sources in the given discretized ``sources``.
1174 Care must be taken when using this function that the external amplitude
1175 factors and reference times of the parameterized (undiscretized)
1176 sources match or are accounted for.
1177 '''
1179 if 'm0s' not in kwargs:
1180 kwargs['m0s'] = num.concatenate([s.m0s for s in sources])
1182 return super(DiscretizedExplosionSource, cls).combine(sources,
1183 **kwargs)
1186class DiscretizedSFSource(DiscretizedSource):
1187 forces = Array.T(shape=(None, 3), dtype=float)
1189 provided_schemes = (
1190 'elastic5',
1191 )
1193 def get_source_terms(self, scheme):
1194 self.check_scheme(scheme)
1196 return self.forces
1198 def make_weights(self, receiver, scheme):
1199 self.check_scheme(scheme)
1201 azis, bazis = self.azibazis_to(receiver)
1203 sa = num.sin(azis*d2r)
1204 ca = num.cos(azis*d2r)
1205 sb = num.sin(bazis*d2r-num.pi)
1206 cb = num.cos(bazis*d2r-num.pi)
1208 forces = self.forces
1209 fn = forces[:, 0]
1210 fe = forces[:, 1]
1211 fd = forces[:, 2]
1213 f0 = fd
1214 f1 = ca * fn + sa * fe
1215 f2 = ca * fe - sa * fn
1217 n = azis.size
1219 if scheme == 'elastic5':
1220 ioff = 0
1222 cat = num.concatenate
1223 rep = num.repeat
1225 w_n = cat((cb*f0, cb*f1, -sb*f2))
1226 g_n = ioff + rep((0, 1, 2), n)
1227 w_e = cat((sb*f0, sb*f1, cb*f2))
1228 g_e = ioff + rep((0, 1, 2), n)
1229 w_d = cat((f0, f1))
1230 g_d = ioff + rep((3, 4), n)
1232 return (
1233 ('displacement.n', w_n, g_n),
1234 ('displacement.e', w_e, g_e),
1235 ('displacement.d', w_d, g_d),
1236 )
1238 @classmethod
1239 def combine(cls, sources, **kwargs):
1240 '''
1241 Combine several discretized source models.
1243 Concatenenates all point sources in the given discretized ``sources``.
1244 Care must be taken when using this function that the external amplitude
1245 factors and reference times of the parameterized (undiscretized)
1246 sources match or are accounted for.
1247 '''
1249 if 'forces' not in kwargs:
1250 kwargs['forces'] = num.vstack([s.forces for s in sources])
1252 return super(DiscretizedSFSource, cls).combine(sources, **kwargs)
1254 def moments(self):
1255 return num.sum(self.forces**2, axis=1)
1257 def centroid(self):
1258 from pyrocko.gf.seismosizer import SFSource
1259 time, lat, lon, north_shift, east_shift, depth = \
1260 self.centroid_position()
1262 fn, fe, fd = map(float, num.sum(self.forces, axis=0))
1263 return SFSource(
1264 time=time,
1265 lat=lat,
1266 lon=lon,
1267 north_shift=north_shift,
1268 east_shift=east_shift,
1269 depth=depth,
1270 fn=fn,
1271 fe=fe,
1272 fd=fd)
1275class DiscretizedMTSource(DiscretizedSource):
1276 m6s = Array.T(
1277 shape=(None, 6), dtype=float,
1278 help='rows with (m_nn, m_ee, m_dd, m_ne, m_nd, m_ed)')
1280 provided_schemes = (
1281 'elastic8',
1282 'elastic10',
1283 'elastic18',
1284 )
1286 def get_source_terms(self, scheme):
1287 self.check_scheme(scheme)
1288 return self.m6s
1290 def make_weights(self, receiver, scheme):
1291 self.check_scheme(scheme)
1293 m6s = self.m6s
1294 n = m6s.shape[0]
1295 rep = num.repeat
1297 if scheme == 'elastic18':
1298 w_n = m6s.flatten()
1299 g_n = num.tile(num.arange(0, 6), n)
1300 w_e = m6s.flatten()
1301 g_e = num.tile(num.arange(6, 12), n)
1302 w_d = m6s.flatten()
1303 g_d = num.tile(num.arange(12, 18), n)
1305 else:
1306 azis, bazis = self.azibazis_to(receiver)
1308 sa = num.sin(azis*d2r)
1309 ca = num.cos(azis*d2r)
1310 s2a = num.sin(2.*azis*d2r)
1311 c2a = num.cos(2.*azis*d2r)
1312 sb = num.sin(bazis*d2r-num.pi)
1313 cb = num.cos(bazis*d2r-num.pi)
1315 f0 = m6s[:, 0]*ca**2 + m6s[:, 1]*sa**2 + m6s[:, 3]*s2a
1316 f1 = m6s[:, 4]*ca + m6s[:, 5]*sa
1317 f2 = m6s[:, 2]
1318 f3 = 0.5*(m6s[:, 1]-m6s[:, 0])*s2a + m6s[:, 3]*c2a
1319 f4 = m6s[:, 5]*ca - m6s[:, 4]*sa
1320 f5 = m6s[:, 0]*sa**2 + m6s[:, 1]*ca**2 - m6s[:, 3]*s2a
1322 cat = num.concatenate
1324 if scheme == 'elastic8':
1325 w_n = cat((cb*f0, cb*f1, cb*f2, -sb*f3, -sb*f4))
1326 g_n = rep((0, 1, 2, 3, 4), n)
1327 w_e = cat((sb*f0, sb*f1, sb*f2, cb*f3, cb*f4))
1328 g_e = rep((0, 1, 2, 3, 4), n)
1329 w_d = cat((f0, f1, f2))
1330 g_d = rep((5, 6, 7), n)
1332 elif scheme == 'elastic10':
1333 w_n = cat((cb*f0, cb*f1, cb*f2, cb*f5, -sb*f3, -sb*f4))
1334 g_n = rep((0, 1, 2, 8, 3, 4), n)
1335 w_e = cat((sb*f0, sb*f1, sb*f2, sb*f5, cb*f3, cb*f4))
1336 g_e = rep((0, 1, 2, 8, 3, 4), n)
1337 w_d = cat((f0, f1, f2, f5))
1338 g_d = rep((5, 6, 7, 9), n)
1340 else:
1341 assert False
1343 return (
1344 ('displacement.n', w_n, g_n),
1345 ('displacement.e', w_e, g_e),
1346 ('displacement.d', w_d, g_d),
1347 )
1349 def split(self):
1350 from pyrocko.gf.seismosizer import MTSource
1351 sources = []
1352 for i in range(self.nelements):
1353 lat, lon, north_shift, east_shift = self.element_coords(i)
1354 sources.append(MTSource(
1355 time=float(self.times[i]),
1356 lat=lat,
1357 lon=lon,
1358 north_shift=north_shift,
1359 east_shift=east_shift,
1360 depth=float(self.depths[i]),
1361 m6=self.m6s[i]))
1363 return sources
1365 def moments(self):
1366 moments = num.array(
1367 [num.linalg.eigvalsh(moment_tensor.symmat6(*m6))
1368 for m6 in self.m6s])
1369 return num.linalg.norm(moments, axis=1) / num.sqrt(2.)
1371 def get_moment_rate(self, deltat=None):
1372 moments = self.moments()
1373 times = self.times
1374 times -= times.min()
1376 t_max = times.max()
1377 mom_times = num.arange(0, t_max + 2 * deltat, deltat) - deltat
1378 mom_times[mom_times > t_max] = t_max
1380 # Right open histrogram bins
1381 mom, _ = num.histogram(
1382 times,
1383 bins=mom_times,
1384 weights=moments)
1386 deltat = num.diff(mom_times)
1387 mom_rate = mom / deltat
1389 return mom_rate, mom_times[1:]
1391 def centroid(self):
1392 from pyrocko.gf.seismosizer import MTSource
1393 time, lat, lon, north_shift, east_shift, depth = \
1394 self.centroid_position()
1396 return MTSource(
1397 time=time,
1398 lat=lat,
1399 lon=lon,
1400 north_shift=north_shift,
1401 east_shift=east_shift,
1402 depth=depth,
1403 m6=num.sum(self.m6s, axis=0))
1405 @classmethod
1406 def combine(cls, sources, **kwargs):
1407 '''
1408 Combine several discretized source models.
1410 Concatenenates all point sources in the given discretized ``sources``.
1411 Care must be taken when using this function that the external amplitude
1412 factors and reference times of the parameterized (undiscretized)
1413 sources match or are accounted for.
1414 '''
1416 if 'm6s' not in kwargs:
1417 kwargs['m6s'] = num.vstack([s.m6s for s in sources])
1419 return super(DiscretizedMTSource, cls).combine(sources, **kwargs)
1422class DiscretizedPorePressureSource(DiscretizedSource):
1423 pp = Array.T(shape=(None,), dtype=float)
1425 provided_schemes = (
1426 'poroelastic10',
1427 )
1429 def get_source_terms(self, scheme):
1430 self.check_scheme(scheme)
1431 return self.pp[:, num.newaxis].copy()
1433 def make_weights(self, receiver, scheme):
1434 self.check_scheme(scheme)
1436 azis, bazis = self.azibazis_to(receiver)
1438 sb = num.sin(bazis*d2r-num.pi)
1439 cb = num.cos(bazis*d2r-num.pi)
1441 pp = self.pp
1442 n = bazis.size
1444 w_un = cb*pp
1445 g_un = filledi(1, n)
1446 w_ue = sb*pp
1447 g_ue = filledi(1, n)
1448 w_ud = pp
1449 g_ud = filledi(0, n)
1451 w_tn = cb*pp
1452 g_tn = filledi(6, n)
1453 w_te = sb*pp
1454 g_te = filledi(6, n)
1456 w_pp = pp
1457 g_pp = filledi(7, n)
1459 w_dvn = cb*pp
1460 g_dvn = filledi(9, n)
1461 w_dve = sb*pp
1462 g_dve = filledi(9, n)
1463 w_dvd = pp
1464 g_dvd = filledi(8, n)
1466 return (
1467 ('displacement.n', w_un, g_un),
1468 ('displacement.e', w_ue, g_ue),
1469 ('displacement.d', w_ud, g_ud),
1470 ('vertical_tilt.n', w_tn, g_tn),
1471 ('vertical_tilt.e', w_te, g_te),
1472 ('pore_pressure', w_pp, g_pp),
1473 ('darcy_velocity.n', w_dvn, g_dvn),
1474 ('darcy_velocity.e', w_dve, g_dve),
1475 ('darcy_velocity.d', w_dvd, g_dvd),
1476 )
1478 def moments(self):
1479 return self.pp
1481 def centroid(self):
1482 from pyrocko.gf.seismosizer import PorePressurePointSource
1483 time, lat, lon, north_shift, east_shift, depth = \
1484 self.centroid_position()
1486 return PorePressurePointSource(
1487 time=time,
1488 lat=lat,
1489 lon=lon,
1490 north_shift=north_shift,
1491 east_shift=east_shift,
1492 depth=depth,
1493 pp=float(num.sum(self.pp)))
1495 @classmethod
1496 def combine(cls, sources, **kwargs):
1497 '''
1498 Combine several discretized source models.
1500 Concatenenates all point sources in the given discretized ``sources``.
1501 Care must be taken when using this function that the external amplitude
1502 factors and reference times of the parameterized (undiscretized)
1503 sources match or are accounted for.
1504 '''
1506 if 'pp' not in kwargs:
1507 kwargs['pp'] = num.concatenate([s.pp for s in sources])
1509 return super(DiscretizedPorePressureSource, cls).combine(sources,
1510 **kwargs)
1513class Region(Object):
1514 name = String.T(optional=True)
1517class RectangularRegion(Region):
1518 lat_min = Float.T()
1519 lat_max = Float.T()
1520 lon_min = Float.T()
1521 lon_max = Float.T()
1524class CircularRegion(Region):
1525 lat = Float.T()
1526 lon = Float.T()
1527 radius = Float.T()
1530class Config(Object):
1531 '''
1532 Green's function store meta information.
1534 Currently implemented :py:class:`~pyrocko.gf.store.Store`
1535 configuration types are:
1537 * :py:class:`~pyrocko.gf.meta.ConfigTypeA` - cylindrical or
1538 spherical symmetry, 1D earth model, single receiver depth
1540 * Problem is invariant to horizontal translations and rotations around
1541 vertical axis.
1542 * All receivers must be at the same depth (e.g. at the surface)
1543 * High level index variables: ``(source_depth, receiver_distance,
1544 component)``
1546 * :py:class:`~pyrocko.gf.meta.ConfigTypeB` - cylindrical or
1547 spherical symmetry, 1D earth model, variable receiver depth
1549 * Symmetries like in Type A but has additional index for receiver depth
1550 * High level index variables: ``(source_depth, receiver_distance,
1551 receiver_depth, component)``
1553 * :py:class:`~pyrocko.gf.meta.ConfigTypeC` - no symmetrical
1554 constraints but fixed receiver positions
1556 * Cartesian source volume around a reference point
1557 * High level index variables: ``(ireceiver, source_depth,
1558 source_east_shift, source_north_shift, component)``
1559 '''
1561 id = StringID.T(
1562 help='Name of the store. May consist of upper and lower-case letters, '
1563 'digits, dots and underscores. The name must start with a '
1564 'letter.')
1566 derived_from_id = StringID.T(
1567 optional=True,
1568 help='Name of the original store, if this store has been derived from '
1569 'another one (e.g. extracted subset).')
1571 version = String.T(
1572 default='1.0',
1573 optional=True,
1574 help='User-defined version string. Use <major>.<minor> format.')
1576 modelling_code_id = StringID.T(
1577 optional=True,
1578 help='Identifier of the backend used to compute the store.')
1580 author = Unicode.T(
1581 optional=True,
1582 help='Comma-separated list of author names.')
1584 author_email = String.T(
1585 optional=True,
1586 help="Author's contact email address.")
1588 created_time = Timestamp.T(
1589 optional=True,
1590 help='Time of creation of the store.')
1592 regions = List.T(
1593 Region.T(),
1594 help='Geographical regions for which the store is representative.')
1596 scope_type = ScopeType.T(
1597 optional=True,
1598 help='Distance range scope of the store '
1599 '(%s).' % fmt_choices(ScopeType))
1601 waveform_type = WaveType.T(
1602 optional=True,
1603 help='Wave type stored (%s).' % fmt_choices(WaveType))
1605 nearfield_terms = NearfieldTermsType.T(
1606 optional=True,
1607 help='Information about the inclusion of near-field terms in the '
1608 'modelling (%s).' % fmt_choices(NearfieldTermsType))
1610 description = String.T(
1611 optional=True,
1612 help='Free form textual description of the GF store.')
1614 references = List.T(
1615 Reference.T(),
1616 help='Reference list to cite the modelling code, earth model or '
1617 'related work.')
1619 earthmodel_1d = Earthmodel1D.T(
1620 optional=True,
1621 help='Layered earth model in ND (named discontinuity) format.')
1623 earthmodel_receiver_1d = Earthmodel1D.T(
1624 optional=True,
1625 help='Receiver-side layered earth model in ND format.')
1627 can_interpolate_source = Bool.T(
1628 optional=True,
1629 help='Hint to indicate if the spatial sampling of the store is dense '
1630 'enough for multi-linear interpolation at the source.')
1632 can_interpolate_receiver = Bool.T(
1633 optional=True,
1634 help='Hint to indicate if the spatial sampling of the store is dense '
1635 'enough for multi-linear interpolation at the receiver.')
1637 frequency_min = Float.T(
1638 optional=True,
1639 help='Hint to indicate the lower bound of valid frequencies [Hz].')
1641 frequency_max = Float.T(
1642 optional=True,
1643 help='Hint to indicate the upper bound of valid frequencies [Hz].')
1645 sample_rate = Float.T(
1646 optional=True,
1647 help='Sample rate of the GF store [Hz].')
1649 factor = Float.T(
1650 default=1.0,
1651 help='Gain value, factored out of the stored GF samples. '
1652 '(may not work properly, keep at 1.0).',
1653 optional=True)
1655 component_scheme = ComponentScheme.T(
1656 default='elastic10',
1657 help='GF component scheme (%s).' % fmt_choices(ComponentScheme))
1659 stored_quantity = QuantityType.T(
1660 optional=True,
1661 help='Physical quantity of stored values (%s). If not given, a '
1662 'default is used based on the GF component scheme. The default '
1663 'for the ``"elastic*"`` family of component schemes is '
1664 '``"displacement"``.' % fmt_choices(QuantityType))
1666 tabulated_phases = List.T(
1667 TPDef.T(),
1668 help='Mapping of phase names to phase definitions, for which travel '
1669 'time tables are available in the GF store.')
1671 ncomponents = Int.T(
1672 optional=True,
1673 help='Number of GF components. Use :gattr:`component_scheme` instead.')
1675 uuid = String.T(
1676 optional=True,
1677 help='Heuristic hash value which can be used to uniquely identify the '
1678 'GF store for practical purposes.')
1680 reference = String.T(
1681 optional=True,
1682 help="Store reference name composed of the store's :gattr:`id` and "
1683 'the first six letters of its :gattr:`uuid`.')
1685 def __init__(self, **kwargs):
1686 self._do_auto_updates = False
1687 Object.__init__(self, **kwargs)
1688 self._index_function = None
1689 self._indices_function = None
1690 self._vicinity_function = None
1691 self.validate(regularize=True, depth=1)
1692 self._do_auto_updates = True
1693 self.update()
1695 def check_ncomponents(self):
1696 ncomponents = component_scheme_to_description[
1697 self.component_scheme].ncomponents
1699 if self.ncomponents is None:
1700 self.ncomponents = ncomponents
1701 elif ncomponents != self.ncomponents:
1702 raise InvalidNComponents(
1703 'ncomponents=%i incompatible with component_scheme="%s"' % (
1704 self.ncomponents, self.component_scheme))
1706 def __setattr__(self, name, value):
1707 Object.__setattr__(self, name, value)
1708 try:
1709 self.T.get_property(name)
1710 if self._do_auto_updates:
1711 self.update()
1713 except ValueError:
1714 pass
1716 def update(self):
1717 self.check_ncomponents()
1718 self._update()
1719 self._make_index_functions()
1721 def irecord(self, *args):
1722 return self._index_function(*args)
1724 def irecords(self, *args):
1725 return self._indices_function(*args)
1727 def vicinity(self, *args):
1728 return self._vicinity_function(*args)
1730 def vicinities(self, *args):
1731 return self._vicinities_function(*args)
1733 def grid_interpolation_coefficients(self, *args):
1734 return self._grid_interpolation_coefficients(*args)
1736 def nodes(self, level=None, minlevel=None):
1737 return nodes(self.coords[minlevel:level])
1739 def iter_nodes(self, level=None, minlevel=None):
1740 return nditer_outer(self.coords[minlevel:level])
1742 def iter_extraction(self, gdef, level=None):
1743 i = 0
1744 arrs = []
1745 ntotal = 1
1746 for mi, ma, inc in zip(self.mins, self.effective_maxs, self.deltas):
1747 if gdef and len(gdef) > i:
1748 sssn = gdef[i]
1749 else:
1750 sssn = (None,)*4
1752 arr = num.linspace(*start_stop_num(*(sssn + (mi, ma, inc))))
1753 ntotal *= len(arr)
1755 arrs.append(arr)
1756 i += 1
1758 arrs.append(self.coords[-1])
1759 return nditer_outer(arrs[:level])
1761 def make_sum_params(self, source, receiver, implementation='c',
1762 nthreads=0):
1763 assert implementation in ['c', 'python']
1765 out = []
1766 delays = source.times
1767 for comp, weights, icomponents in source.make_weights(
1768 receiver,
1769 self.component_scheme):
1771 weights *= self.factor
1773 args = self.make_indexing_args(source, receiver, icomponents)
1774 delays_expanded = num.tile(delays, icomponents.size//delays.size)
1775 out.append((comp, args, delays_expanded, weights))
1777 return out
1779 def short_info(self):
1780 raise NotImplementedError('should be implemented in subclass')
1782 def get_shear_moduli(self, lat, lon, points,
1783 interpolation=None):
1784 '''
1785 Get shear moduli at given points from contained velocity model.
1787 :param lat: surface origin for coordinate system of ``points``
1788 :param points: NumPy array of shape ``(N, 3)``, where each row is
1789 a point ``(north, east, depth)``, relative to origin at
1790 ``(lat, lon)``
1791 :param interpolation: interpolation method. Choose from
1792 ``('nearest_neighbor', 'multilinear')``
1793 :returns: NumPy array of length N with extracted shear moduli at each
1794 point
1796 The default implementation retrieves and interpolates the shear moduli
1797 from the contained 1D velocity profile.
1798 '''
1799 return self.get_material_property(lat, lon, points,
1800 parameter='shear_moduli',
1801 interpolation=interpolation)
1803 def get_lambda_moduli(self, lat, lon, points,
1804 interpolation=None):
1805 '''
1806 Get lambda moduli at given points from contained velocity model.
1808 :param lat: surface origin for coordinate system of ``points``
1809 :param points: NumPy array of shape ``(N, 3)``, where each row is
1810 a point ``(north, east, depth)``, relative to origin at
1811 ``(lat, lon)``
1812 :param interpolation: interpolation method. Choose from
1813 ``('nearest_neighbor', 'multilinear')``
1814 :returns: NumPy array of length N with extracted shear moduli at each
1815 point
1817 The default implementation retrieves and interpolates the lambda moduli
1818 from the contained 1D velocity profile.
1819 '''
1820 return self.get_material_property(lat, lon, points,
1821 parameter='lambda_moduli',
1822 interpolation=interpolation)
1824 def get_bulk_moduli(self, lat, lon, points,
1825 interpolation=None):
1826 '''
1827 Get bulk moduli at given points from contained velocity model.
1829 :param lat: surface origin for coordinate system of ``points``
1830 :param points: NumPy array of shape ``(N, 3)``, where each row is
1831 a point ``(north, east, depth)``, relative to origin at
1832 ``(lat, lon)``
1833 :param interpolation: interpolation method. Choose from
1834 ``('nearest_neighbor', 'multilinear')``
1835 :returns: NumPy array of length N with extracted shear moduli at each
1836 point
1838 The default implementation retrieves and interpolates the lambda moduli
1839 from the contained 1D velocity profile.
1840 '''
1841 lambda_moduli = self.get_material_property(
1842 lat, lon, points, parameter='lambda_moduli',
1843 interpolation=interpolation)
1844 shear_moduli = self.get_material_property(
1845 lat, lon, points, parameter='shear_moduli',
1846 interpolation=interpolation)
1847 return lambda_moduli + (2 / 3) * shear_moduli
1849 def get_vs(self, lat, lon, points, interpolation=None):
1850 '''
1851 Get Vs at given points from contained velocity model.
1853 :param lat: surface origin for coordinate system of ``points``
1854 :param points: NumPy array of shape ``(N, 3)``, where each row is
1855 a point ``(north, east, depth)``, relative to origin at
1856 ``(lat, lon)``
1857 :param interpolation: interpolation method. Choose from
1858 ``('nearest_neighbor', 'multilinear')``
1859 :returns: NumPy array of length N with extracted shear moduli at each
1860 point
1862 The default implementation retrieves and interpolates Vs
1863 from the contained 1D velocity profile.
1864 '''
1865 return self.get_material_property(lat, lon, points,
1866 parameter='vs',
1867 interpolation=interpolation)
1869 def get_vp(self, lat, lon, points, interpolation=None):
1870 '''
1871 Get Vp at given points from contained velocity model.
1873 :param lat: surface origin for coordinate system of ``points``
1874 :param points: NumPy array of shape ``(N, 3)``, where each row is
1875 a point ``(north, east, depth)``, relative to origin at
1876 ``(lat, lon)``
1877 :param interpolation: interpolation method. Choose from
1878 ``('nearest_neighbor', 'multilinear')``
1879 :returns: NumPy array of length N with extracted shear moduli at each
1880 point
1882 The default implementation retrieves and interpolates Vp
1883 from the contained 1D velocity profile.
1884 '''
1885 return self.get_material_property(lat, lon, points,
1886 parameter='vp',
1887 interpolation=interpolation)
1889 def get_rho(self, lat, lon, points, interpolation=None):
1890 '''
1891 Get rho at given points from contained velocity model.
1893 :param lat: surface origin for coordinate system of ``points``
1894 :param points: NumPy array of shape ``(N, 3)``, where each row is
1895 a point ``(north, east, depth)``, relative to origin at
1896 ``(lat, lon)``
1897 :param interpolation: interpolation method. Choose from
1898 ``('nearest_neighbor', 'multilinear')``
1899 :returns: NumPy array of length N with extracted shear moduli at each
1900 point
1902 The default implementation retrieves and interpolates rho
1903 from the contained 1D velocity profile.
1904 '''
1905 return self.get_material_property(lat, lon, points,
1906 parameter='rho',
1907 interpolation=interpolation)
1909 def get_material_property(self, lat, lon, points, parameter='vs',
1910 interpolation=None):
1912 if interpolation is None:
1913 raise TypeError('Interpolation method not defined! available: '
1914 'multilinear', 'nearest_neighbor')
1916 earthmod = self.earthmodel_1d
1917 store_depth_profile = self.get_source_depths()
1918 z_profile = earthmod.profile('z')
1920 if parameter == 'vs':
1921 vs_profile = earthmod.profile('vs')
1922 profile = num.interp(
1923 store_depth_profile, z_profile, vs_profile)
1925 elif parameter == 'vp':
1926 vp_profile = earthmod.profile('vp')
1927 profile = num.interp(
1928 store_depth_profile, z_profile, vp_profile)
1930 elif parameter == 'rho':
1931 rho_profile = earthmod.profile('rho')
1933 profile = num.interp(
1934 store_depth_profile, z_profile, rho_profile)
1936 elif parameter == 'shear_moduli':
1937 vs_profile = earthmod.profile('vs')
1938 rho_profile = earthmod.profile('rho')
1940 store_vs_profile = num.interp(
1941 store_depth_profile, z_profile, vs_profile)
1942 store_rho_profile = num.interp(
1943 store_depth_profile, z_profile, rho_profile)
1945 profile = num.power(store_vs_profile, 2) * store_rho_profile
1947 elif parameter == 'lambda_moduli':
1948 vs_profile = earthmod.profile('vs')
1949 vp_profile = earthmod.profile('vp')
1950 rho_profile = earthmod.profile('rho')
1952 store_vs_profile = num.interp(
1953 store_depth_profile, z_profile, vs_profile)
1954 store_vp_profile = num.interp(
1955 store_depth_profile, z_profile, vp_profile)
1956 store_rho_profile = num.interp(
1957 store_depth_profile, z_profile, rho_profile)
1959 profile = store_rho_profile * (
1960 num.power(store_vp_profile, 2) -
1961 num.power(store_vs_profile, 2) * 2)
1962 else:
1963 raise TypeError(
1964 'parameter %s not available' % parameter)
1966 if interpolation == 'multilinear':
1967 kind = 'linear'
1968 elif interpolation == 'nearest_neighbor':
1969 kind = 'nearest'
1970 else:
1971 raise TypeError(
1972 'Interpolation method %s not available' % interpolation)
1974 interpolator = interp1d(store_depth_profile, profile, kind=kind)
1976 try:
1977 return interpolator(points[:, 2])
1978 except ValueError:
1979 raise OutOfBounds()
1981 def is_static(self):
1982 for code in ('psgrn_pscmp', 'poel'):
1983 if self.modelling_code_id.startswith(code):
1984 return True
1985 return False
1987 def is_dynamic(self):
1988 return not self.is_static()
1990 def get_source_depths(self):
1991 raise NotImplementedError('must be implemented in subclass')
1993 def get_tabulated_phase(self, phase_id):
1994 '''
1995 Get tabulated phase definition.
1996 '''
1998 for pdef in self.tabulated_phases:
1999 if pdef.id == phase_id:
2000 return pdef
2002 raise StoreError('No such phase: %s' % phase_id)
2004 def fix_ttt_holes(self, sptree, mode):
2005 raise StoreError(
2006 'Cannot fix travel time table holes in GF stores of type %s.'
2007 % self.short_type)
2010class ConfigTypeA(Config):
2011 '''
2012 Cylindrical symmetry, 1D earth model, single receiver depth
2014 * Problem is invariant to horizontal translations and rotations around
2015 vertical axis.
2017 * All receivers must be at the same depth (e.g. at the surface)
2018 High level index variables: ``(source_depth, distance,
2019 component)``
2021 * The ``distance`` is the surface distance between source and receiver
2022 points.
2023 '''
2025 receiver_depth = Float.T(
2026 default=0.0,
2027 help='Fixed receiver depth [m].')
2029 source_depth_min = Float.T(
2030 help='Minimum source depth [m].')
2032 source_depth_max = Float.T(
2033 help='Maximum source depth [m].')
2035 source_depth_delta = Float.T(
2036 help='Grid spacing of source depths [m]')
2038 distance_min = Float.T(
2039 help='Minimum source-receiver surface distance [m].')
2041 distance_max = Float.T(
2042 help='Maximum source-receiver surface distance [m].')
2044 distance_delta = Float.T(
2045 help='Grid spacing of source-receiver surface distance [m].')
2047 short_type = 'A'
2049 provided_schemes = [
2050 'elastic2', 'elastic5', 'elastic8', 'elastic10', 'poroelastic10']
2052 def get_surface_distance(self, args):
2053 return args[1]
2055 def get_distance(self, args):
2056 return math.sqrt(args[0]**2 + args[1]**2)
2058 def get_source_depth(self, args):
2059 return args[0]
2061 def get_source_depths(self):
2062 return self.coords[0]
2064 def get_receiver_depth(self, args):
2065 return self.receiver_depth
2067 def _update(self):
2068 self.mins = num.array(
2069 [self.source_depth_min, self.distance_min], dtype=float)
2070 self.maxs = num.array(
2071 [self.source_depth_max, self.distance_max], dtype=float)
2072 self.deltas = num.array(
2073 [self.source_depth_delta, self.distance_delta],
2074 dtype=float)
2075 self.ns = num.floor((self.maxs - self.mins) / self.deltas +
2076 vicinity_eps).astype(int) + 1
2077 self.effective_maxs = self.mins + self.deltas * (self.ns - 1)
2078 self.deltat = 1.0/self.sample_rate
2079 self.nrecords = num.prod(self.ns) * self.ncomponents
2080 self.coords = tuple(num.linspace(mi, ma, n) for
2081 (mi, ma, n) in
2082 zip(self.mins, self.effective_maxs, self.ns)) + \
2083 (num.arange(self.ncomponents),)
2085 self.nsource_depths, self.ndistances = self.ns
2087 def _make_index_functions(self):
2089 amin, bmin = self.mins
2090 da, db = self.deltas
2091 na, nb = self.ns
2093 ng = self.ncomponents
2095 def index_function(a, b, ig):
2096 ia = int(round((a - amin) / da))
2097 ib = int(round((b - bmin) / db))
2098 try:
2099 return num.ravel_multi_index((ia, ib, ig), (na, nb, ng))
2100 except ValueError:
2101 raise OutOfBounds()
2103 def indices_function(a, b, ig):
2104 ia = num.round((a - amin) / da).astype(int)
2105 ib = num.round((b - bmin) / db).astype(int)
2106 try:
2107 return num.ravel_multi_index((ia, ib, ig), (na, nb, ng))
2108 except ValueError:
2109 for ia_, ib_, ig_ in zip(ia, ib, ig):
2110 try:
2111 num.ravel_multi_index((ia_, ib_, ig_), (na, nb, ng))
2112 except ValueError:
2113 raise OutOfBounds()
2115 def grid_interpolation_coefficients(a, b):
2116 ias = indi12((a - amin) / da, na)
2117 ibs = indi12((b - bmin) / db, nb)
2118 return ias, ibs
2120 def vicinity_function(a, b, ig):
2121 ias, ibs = grid_interpolation_coefficients(a, b)
2123 if not (0 <= ig < ng):
2124 raise OutOfBounds()
2126 indis = []
2127 weights = []
2128 for ia, va in ias:
2129 iia = ia*nb*ng
2130 for ib, vb in ibs:
2131 indis.append(iia + ib*ng + ig)
2132 weights.append(va*vb)
2134 return num.array(indis), num.array(weights)
2136 def vicinities_function(a, b, ig):
2138 xa = (a - amin) / da
2139 xb = (b - bmin) / db
2141 xa_fl = num.floor(xa)
2142 xa_ce = num.ceil(xa)
2143 xb_fl = num.floor(xb)
2144 xb_ce = num.ceil(xb)
2145 va_fl = 1.0 - (xa - xa_fl)
2146 va_ce = (1.0 - (xa_ce - xa)) * (xa_ce - xa_fl)
2147 vb_fl = 1.0 - (xb - xb_fl)
2148 vb_ce = (1.0 - (xb_ce - xb)) * (xb_ce - xb_fl)
2150 ia_fl = xa_fl.astype(int)
2151 ia_ce = xa_ce.astype(int)
2152 ib_fl = xb_fl.astype(int)
2153 ib_ce = xb_ce.astype(int)
2155 if num.any(ia_fl < 0) or num.any(ia_fl >= na):
2156 raise OutOfBounds()
2158 if num.any(ia_ce < 0) or num.any(ia_ce >= na):
2159 raise OutOfBounds()
2161 if num.any(ib_fl < 0) or num.any(ib_fl >= nb):
2162 raise OutOfBounds()
2164 if num.any(ib_ce < 0) or num.any(ib_ce >= nb):
2165 raise OutOfBounds()
2167 irecords = num.empty(a.size*4, dtype=int)
2168 irecords[0::4] = ia_fl*nb*ng + ib_fl*ng + ig
2169 irecords[1::4] = ia_ce*nb*ng + ib_fl*ng + ig
2170 irecords[2::4] = ia_fl*nb*ng + ib_ce*ng + ig
2171 irecords[3::4] = ia_ce*nb*ng + ib_ce*ng + ig
2173 weights = num.empty(a.size*4, dtype=float)
2174 weights[0::4] = va_fl * vb_fl
2175 weights[1::4] = va_ce * vb_fl
2176 weights[2::4] = va_fl * vb_ce
2177 weights[3::4] = va_ce * vb_ce
2179 return irecords, weights
2181 self._index_function = index_function
2182 self._indices_function = indices_function
2183 self._grid_interpolation_coefficients = grid_interpolation_coefficients
2184 self._vicinity_function = vicinity_function
2185 self._vicinities_function = vicinities_function
2187 def make_indexing_args(self, source, receiver, icomponents):
2188 nc = icomponents.size
2189 dists = source.distances_to(receiver)
2190 n = dists.size
2191 return (num.tile(source.depths, nc//n),
2192 num.tile(dists, nc//n),
2193 icomponents)
2195 def make_indexing_args1(self, source, receiver):
2196 return (source.depth, source.distance_to(receiver))
2198 @property
2199 def short_extent(self):
2200 return '%g:%g:%g x %g:%g:%g' % (
2201 self.source_depth_min/km,
2202 self.source_depth_max/km,
2203 self.source_depth_delta/km,
2204 self.distance_min/km,
2205 self.distance_max/km,
2206 self.distance_delta/km)
2208 def fix_ttt_holes(self, sptree, mode):
2209 from pyrocko import eikonal_ext, spit
2211 nodes = self.nodes(level=-1)
2213 delta = self.deltas[-1]
2214 assert num.all(delta == self.deltas)
2216 nsources, ndistances = self.ns
2218 points = num.zeros((nodes.shape[0], 3))
2219 points[:, 0] = nodes[:, 1]
2220 points[:, 2] = nodes[:, 0]
2222 speeds = self.get_material_property(
2223 0., 0., points,
2224 parameter='vp' if mode == cake.P else 'vs',
2225 interpolation='multilinear')
2227 speeds = speeds.reshape((nsources, ndistances))
2229 times = sptree.interpolate_many(nodes)
2231 times[num.isnan(times)] = -1.
2232 times = times.reshape(speeds.shape)
2234 try:
2235 eikonal_ext.eikonal_solver_fmm_cartesian(
2236 speeds, times, delta)
2237 except eikonal_ext.EikonalExtError as e:
2238 if str(e).endswith('please check results'):
2239 logger.debug(
2240 'Got a warning from eikonal solver '
2241 '- may be ok...')
2242 else:
2243 raise
2245 def func(x):
2246 ibs, ics = \
2247 self.grid_interpolation_coefficients(*x)
2249 t = 0
2250 for ib, vb in ibs:
2251 for ic, vc in ics:
2252 t += times[ib, ic] * vb * vc
2254 return t
2256 return spit.SPTree(
2257 f=func,
2258 ftol=sptree.ftol,
2259 xbounds=sptree.xbounds,
2260 xtols=sptree.xtols)
2263class ConfigTypeB(Config):
2264 '''
2265 Cylindrical symmetry, 1D earth model, variable receiver depth
2267 * Symmetries like in :py:class:`ConfigTypeA` but has additional index for
2268 receiver depth
2270 * High level index variables: ``(receiver_depth, source_depth,
2271 receiver_distance, component)``
2272 '''
2274 receiver_depth_min = Float.T(
2275 help='Minimum receiver depth [m].')
2277 receiver_depth_max = Float.T(
2278 help='Maximum receiver depth [m].')
2280 receiver_depth_delta = Float.T(
2281 help='Grid spacing of receiver depths [m]')
2283 source_depth_min = Float.T(
2284 help='Minimum source depth [m].')
2286 source_depth_max = Float.T(
2287 help='Maximum source depth [m].')
2289 source_depth_delta = Float.T(
2290 help='Grid spacing of source depths [m]')
2292 distance_min = Float.T(
2293 help='Minimum source-receiver surface distance [m].')
2295 distance_max = Float.T(
2296 help='Maximum source-receiver surface distance [m].')
2298 distance_delta = Float.T(
2299 help='Grid spacing of source-receiver surface distances [m].')
2301 short_type = 'B'
2303 provided_schemes = [
2304 'elastic2', 'elastic5', 'elastic8', 'elastic10', 'poroelastic10']
2306 def get_distance(self, args):
2307 return math.sqrt((args[1] - args[0])**2 + args[2]**2)
2309 def get_surface_distance(self, args):
2310 return args[2]
2312 def get_source_depth(self, args):
2313 return args[1]
2315 def get_receiver_depth(self, args):
2316 return args[0]
2318 def get_source_depths(self):
2319 return self.coords[1]
2321 def _update(self):
2322 self.mins = num.array([
2323 self.receiver_depth_min,
2324 self.source_depth_min,
2325 self.distance_min],
2326 dtype=float)
2328 self.maxs = num.array([
2329 self.receiver_depth_max,
2330 self.source_depth_max,
2331 self.distance_max],
2332 dtype=float)
2334 self.deltas = num.array([
2335 self.receiver_depth_delta,
2336 self.source_depth_delta,
2337 self.distance_delta],
2338 dtype=float)
2340 self.ns = num.floor((self.maxs - self.mins) / self.deltas +
2341 vicinity_eps).astype(int) + 1
2342 self.effective_maxs = self.mins + self.deltas * (self.ns - 1)
2343 self.deltat = 1.0/self.sample_rate
2344 self.nrecords = num.prod(self.ns) * self.ncomponents
2345 self.coords = tuple(num.linspace(mi, ma, n) for
2346 (mi, ma, n) in
2347 zip(self.mins, self.effective_maxs, self.ns)) + \
2348 (num.arange(self.ncomponents),)
2349 self.nreceiver_depths, self.nsource_depths, self.ndistances = self.ns
2351 def _make_index_functions(self):
2353 amin, bmin, cmin = self.mins
2354 da, db, dc = self.deltas
2355 na, nb, nc = self.ns
2356 ng = self.ncomponents
2358 def index_function(a, b, c, ig):
2359 ia = int(round((a - amin) / da))
2360 ib = int(round((b - bmin) / db))
2361 ic = int(round((c - cmin) / dc))
2362 try:
2363 return num.ravel_multi_index((ia, ib, ic, ig),
2364 (na, nb, nc, ng))
2365 except ValueError:
2366 raise OutOfBounds()
2368 def indices_function(a, b, c, ig):
2369 ia = num.round((a - amin) / da).astype(int)
2370 ib = num.round((b - bmin) / db).astype(int)
2371 ic = num.round((c - cmin) / dc).astype(int)
2372 try:
2373 return num.ravel_multi_index((ia, ib, ic, ig),
2374 (na, nb, nc, ng))
2375 except ValueError:
2376 raise OutOfBounds()
2378 def grid_interpolation_coefficients(a, b, c):
2379 ias = indi12((a - amin) / da, na)
2380 ibs = indi12((b - bmin) / db, nb)
2381 ics = indi12((c - cmin) / dc, nc)
2382 return ias, ibs, ics
2384 def vicinity_function(a, b, c, ig):
2385 ias, ibs, ics = grid_interpolation_coefficients(a, b, c)
2387 if not (0 <= ig < ng):
2388 raise OutOfBounds()
2390 indis = []
2391 weights = []
2392 for ia, va in ias:
2393 iia = ia*nb*nc*ng
2394 for ib, vb in ibs:
2395 iib = ib*nc*ng
2396 for ic, vc in ics:
2397 indis.append(iia + iib + ic*ng + ig)
2398 weights.append(va*vb*vc)
2400 return num.array(indis), num.array(weights)
2402 def vicinities_function(a, b, c, ig):
2404 xa = (a - amin) / da
2405 xb = (b - bmin) / db
2406 xc = (c - cmin) / dc
2408 xa_fl = num.floor(xa)
2409 xa_ce = num.ceil(xa)
2410 xb_fl = num.floor(xb)
2411 xb_ce = num.ceil(xb)
2412 xc_fl = num.floor(xc)
2413 xc_ce = num.ceil(xc)
2414 va_fl = 1.0 - (xa - xa_fl)
2415 va_ce = (1.0 - (xa_ce - xa)) * (xa_ce - xa_fl)
2416 vb_fl = 1.0 - (xb - xb_fl)
2417 vb_ce = (1.0 - (xb_ce - xb)) * (xb_ce - xb_fl)
2418 vc_fl = 1.0 - (xc - xc_fl)
2419 vc_ce = (1.0 - (xc_ce - xc)) * (xc_ce - xc_fl)
2421 ia_fl = xa_fl.astype(int)
2422 ia_ce = xa_ce.astype(int)
2423 ib_fl = xb_fl.astype(int)
2424 ib_ce = xb_ce.astype(int)
2425 ic_fl = xc_fl.astype(int)
2426 ic_ce = xc_ce.astype(int)
2428 if num.any(ia_fl < 0) or num.any(ia_fl >= na):
2429 raise OutOfBounds()
2431 if num.any(ia_ce < 0) or num.any(ia_ce >= na):
2432 raise OutOfBounds()
2434 if num.any(ib_fl < 0) or num.any(ib_fl >= nb):
2435 raise OutOfBounds()
2437 if num.any(ib_ce < 0) or num.any(ib_ce >= nb):
2438 raise OutOfBounds()
2440 if num.any(ic_fl < 0) or num.any(ic_fl >= nc):
2441 raise OutOfBounds()
2443 if num.any(ic_ce < 0) or num.any(ic_ce >= nc):
2444 raise OutOfBounds()
2446 irecords = num.empty(a.size*8, dtype=int)
2447 irecords[0::8] = ia_fl*nb*nc*ng + ib_fl*nc*ng + ic_fl*ng + ig
2448 irecords[1::8] = ia_ce*nb*nc*ng + ib_fl*nc*ng + ic_fl*ng + ig
2449 irecords[2::8] = ia_fl*nb*nc*ng + ib_ce*nc*ng + ic_fl*ng + ig
2450 irecords[3::8] = ia_ce*nb*nc*ng + ib_ce*nc*ng + ic_fl*ng + ig
2451 irecords[4::8] = ia_fl*nb*nc*ng + ib_fl*nc*ng + ic_ce*ng + ig
2452 irecords[5::8] = ia_ce*nb*nc*ng + ib_fl*nc*ng + ic_ce*ng + ig
2453 irecords[6::8] = ia_fl*nb*nc*ng + ib_ce*nc*ng + ic_ce*ng + ig
2454 irecords[7::8] = ia_ce*nb*nc*ng + ib_ce*nc*ng + ic_ce*ng + ig
2456 weights = num.empty(a.size*8, dtype=float)
2457 weights[0::8] = va_fl * vb_fl * vc_fl
2458 weights[1::8] = va_ce * vb_fl * vc_fl
2459 weights[2::8] = va_fl * vb_ce * vc_fl
2460 weights[3::8] = va_ce * vb_ce * vc_fl
2461 weights[4::8] = va_fl * vb_fl * vc_ce
2462 weights[5::8] = va_ce * vb_fl * vc_ce
2463 weights[6::8] = va_fl * vb_ce * vc_ce
2464 weights[7::8] = va_ce * vb_ce * vc_ce
2466 return irecords, weights
2468 self._index_function = index_function
2469 self._indices_function = indices_function
2470 self._grid_interpolation_coefficients = grid_interpolation_coefficients
2471 self._vicinity_function = vicinity_function
2472 self._vicinities_function = vicinities_function
2474 def make_indexing_args(self, source, receiver, icomponents):
2475 nc = icomponents.size
2476 dists = source.distances_to(receiver)
2477 n = dists.size
2478 receiver_depths = num.empty(nc)
2479 receiver_depths.fill(receiver.depth)
2480 return (receiver_depths,
2481 num.tile(source.depths, nc//n),
2482 num.tile(dists, nc//n),
2483 icomponents)
2485 def make_indexing_args1(self, source, receiver):
2486 return (receiver.depth,
2487 source.depth,
2488 source.distance_to(receiver))
2490 @property
2491 def short_extent(self):
2492 return '%g:%g:%g x %g:%g:%g x %g:%g:%g' % (
2493 self.receiver_depth_min/km,
2494 self.receiver_depth_max/km,
2495 self.receiver_depth_delta/km,
2496 self.source_depth_min/km,
2497 self.source_depth_max/km,
2498 self.source_depth_delta/km,
2499 self.distance_min/km,
2500 self.distance_max/km,
2501 self.distance_delta/km)
2503 def fix_ttt_holes(self, sptree, mode):
2504 from pyrocko import eikonal_ext, spit
2506 nodes_sr = self.nodes(minlevel=1, level=-1)
2508 delta = self.deltas[-1]
2509 assert num.all(delta == self.deltas[1:])
2511 nreceivers, nsources, ndistances = self.ns
2513 points = num.zeros((nodes_sr.shape[0], 3))
2514 points[:, 0] = nodes_sr[:, 1]
2515 points[:, 2] = nodes_sr[:, 0]
2517 speeds = self.get_material_property(
2518 0., 0., points,
2519 parameter='vp' if mode == cake.P else 'vs',
2520 interpolation='multilinear')
2522 speeds = speeds.reshape((nsources, ndistances))
2524 receiver_times = []
2525 for ireceiver in range(nreceivers):
2526 nodes = num.hstack([
2527 num_full(
2528 (nodes_sr.shape[0], 1),
2529 self.coords[0][ireceiver]),
2530 nodes_sr])
2532 times = sptree.interpolate_many(nodes)
2534 times[num.isnan(times)] = -1.
2536 times = times.reshape(speeds.shape)
2538 try:
2539 eikonal_ext.eikonal_solver_fmm_cartesian(
2540 speeds, times, delta)
2541 except eikonal_ext.EikonalExtError as e:
2542 if str(e).endswith('please check results'):
2543 logger.debug(
2544 'Got a warning from eikonal solver '
2545 '- may be ok...')
2546 else:
2547 raise
2549 receiver_times.append(times)
2551 def func(x):
2552 ias, ibs, ics = \
2553 self.grid_interpolation_coefficients(*x)
2555 t = 0
2556 for ia, va in ias:
2557 times = receiver_times[ia]
2558 for ib, vb in ibs:
2559 for ic, vc in ics:
2560 t += times[ib, ic] * va * vb * vc
2562 return t
2564 return spit.SPTree(
2565 f=func,
2566 ftol=sptree.ftol,
2567 xbounds=sptree.xbounds,
2568 xtols=sptree.xtols)
2571class ConfigTypeC(Config):
2572 '''
2573 No symmetrical constraints, one fixed receiver position.
2575 * Cartesian 3D source volume around a reference point
2577 * High level index variables: ``(source_depth,
2578 source_east_shift, source_north_shift, component)``
2579 '''
2581 receiver = Receiver.T(
2582 help='Receiver location')
2584 source_origin = Location.T(
2585 help='Origin of the source volume grid.')
2587 source_depth_min = Float.T(
2588 help='Minimum source depth [m].')
2590 source_depth_max = Float.T(
2591 help='Maximum source depth [m].')
2593 source_depth_delta = Float.T(
2594 help='Source depth grid spacing [m].')
2596 source_east_shift_min = Float.T(
2597 help='Minimum easting of source grid [m].')
2599 source_east_shift_max = Float.T(
2600 help='Maximum easting of source grid [m].')
2602 source_east_shift_delta = Float.T(
2603 help='Source volume grid spacing in east direction [m].')
2605 source_north_shift_min = Float.T(
2606 help='Minimum northing of source grid [m].')
2608 source_north_shift_max = Float.T(
2609 help='Maximum northing of source grid [m].')
2611 source_north_shift_delta = Float.T(
2612 help='Source volume grid spacing in north direction [m].')
2614 short_type = 'C'
2616 provided_schemes = ['elastic18']
2618 def get_surface_distance(self, args):
2619 _, source_east_shift, source_north_shift, _ = args
2620 sorig = self.source_origin
2621 sloc = Location(
2622 lat=sorig.lat,
2623 lon=sorig.lon,
2624 north_shift=sorig.north_shift + source_north_shift,
2625 east_shift=sorig.east_shift + source_east_shift)
2627 return self.receiver.distance_to(sloc)
2629 def get_distance(self, args):
2630 sdepth, source_east_shift, source_north_shift, _ = args
2631 sorig = self.source_origin
2632 sloc = Location(
2633 lat=sorig.lat,
2634 lon=sorig.lon,
2635 north_shift=sorig.north_shift + source_north_shift,
2636 east_shift=sorig.east_shift + source_east_shift)
2638 return self.receiver.distance_3d_to(sloc)
2640 def get_source_depth(self, args):
2641 return args[0]
2643 def get_receiver_depth(self, args):
2644 return self.receiver.depth
2646 def get_source_depths(self):
2647 return self.coords[0]
2649 def _update(self):
2650 self.mins = num.array([
2651 self.source_depth_min,
2652 self.source_east_shift_min,
2653 self.source_north_shift_min],
2654 dtype=float)
2656 self.maxs = num.array([
2657 self.source_depth_max,
2658 self.source_east_shift_max,
2659 self.source_north_shift_max],
2660 dtype=float)
2662 self.deltas = num.array([
2663 self.source_depth_delta,
2664 self.source_east_shift_delta,
2665 self.source_north_shift_delta],
2666 dtype=float)
2668 self.ns = num.floor((self.maxs - self.mins) / self.deltas +
2669 vicinity_eps).astype(int) + 1
2670 self.effective_maxs = self.mins + self.deltas * (self.ns - 1)
2671 self.deltat = 1.0/self.sample_rate
2672 self.nrecords = num.prod(self.ns) * self.ncomponents
2674 self.coords = tuple(
2675 num.linspace(mi, ma, n) for (mi, ma, n) in
2676 zip(self.mins, self.effective_maxs, self.ns)) + \
2677 (num.arange(self.ncomponents),)
2679 def _make_index_functions(self):
2681 amin, bmin, cmin = self.mins
2682 da, db, dc = self.deltas
2683 na, nb, nc = self.ns
2684 ng = self.ncomponents
2686 def index_function(a, b, c, ig):
2687 ia = int(round((a - amin) / da))
2688 ib = int(round((b - bmin) / db))
2689 ic = int(round((c - cmin) / dc))
2690 try:
2691 return num.ravel_multi_index((ia, ib, ic, ig),
2692 (na, nb, nc, ng))
2693 except ValueError:
2694 raise OutOfBounds(values=(a, b, c, ig))
2696 def indices_function(a, b, c, ig):
2697 ia = num.round((a - amin) / da).astype(int)
2698 ib = num.round((b - bmin) / db).astype(int)
2699 ic = num.round((c - cmin) / dc).astype(int)
2701 try:
2702 return num.ravel_multi_index((ia, ib, ic, ig),
2703 (na, nb, nc, ng))
2704 except ValueError:
2705 raise OutOfBounds()
2707 def vicinity_function(a, b, c, ig):
2708 ias = indi12((a - amin) / da, na)
2709 ibs = indi12((b - bmin) / db, nb)
2710 ics = indi12((c - cmin) / dc, nc)
2712 if not (0 <= ig < ng):
2713 raise OutOfBounds()
2715 indis = []
2716 weights = []
2717 for ia, va in ias:
2718 iia = ia*nb*nc*ng
2719 for ib, vb in ibs:
2720 iib = ib*nc*ng
2721 for ic, vc in ics:
2722 indis.append(iia + iib + ic*ng + ig)
2723 weights.append(va*vb*vc)
2725 return num.array(indis), num.array(weights)
2727 def vicinities_function(a, b, c, ig):
2729 xa = (a-amin) / da
2730 xb = (b-bmin) / db
2731 xc = (c-cmin) / dc
2733 xa_fl = num.floor(xa)
2734 xa_ce = num.ceil(xa)
2735 xb_fl = num.floor(xb)
2736 xb_ce = num.ceil(xb)
2737 xc_fl = num.floor(xc)
2738 xc_ce = num.ceil(xc)
2739 va_fl = 1.0 - (xa - xa_fl)
2740 va_ce = (1.0 - (xa_ce - xa)) * (xa_ce - xa_fl)
2741 vb_fl = 1.0 - (xb - xb_fl)
2742 vb_ce = (1.0 - (xb_ce - xb)) * (xb_ce - xb_fl)
2743 vc_fl = 1.0 - (xc - xc_fl)
2744 vc_ce = (1.0 - (xc_ce - xc)) * (xc_ce - xc_fl)
2746 ia_fl = xa_fl.astype(int)
2747 ia_ce = xa_ce.astype(int)
2748 ib_fl = xb_fl.astype(int)
2749 ib_ce = xb_ce.astype(int)
2750 ic_fl = xc_fl.astype(int)
2751 ic_ce = xc_ce.astype(int)
2753 if num.any(ia_fl < 0) or num.any(ia_fl >= na):
2754 raise OutOfBounds()
2756 if num.any(ia_ce < 0) or num.any(ia_ce >= na):
2757 raise OutOfBounds()
2759 if num.any(ib_fl < 0) or num.any(ib_fl >= nb):
2760 raise OutOfBounds()
2762 if num.any(ib_ce < 0) or num.any(ib_ce >= nb):
2763 raise OutOfBounds()
2765 if num.any(ic_fl < 0) or num.any(ic_fl >= nc):
2766 raise OutOfBounds()
2768 if num.any(ic_ce < 0) or num.any(ic_ce >= nc):
2769 raise OutOfBounds()
2771 irecords = num.empty(a.size*8, dtype=int)
2772 irecords[0::8] = ia_fl*nb*nc*ng + ib_fl*nc*ng + ic_fl*ng + ig
2773 irecords[1::8] = ia_ce*nb*nc*ng + ib_fl*nc*ng + ic_fl*ng + ig
2774 irecords[2::8] = ia_fl*nb*nc*ng + ib_ce*nc*ng + ic_fl*ng + ig
2775 irecords[3::8] = ia_ce*nb*nc*ng + ib_ce*nc*ng + ic_fl*ng + ig
2776 irecords[4::8] = ia_fl*nb*nc*ng + ib_fl*nc*ng + ic_ce*ng + ig
2777 irecords[5::8] = ia_ce*nb*nc*ng + ib_fl*nc*ng + ic_ce*ng + ig
2778 irecords[6::8] = ia_fl*nb*nc*ng + ib_ce*nc*ng + ic_ce*ng + ig
2779 irecords[7::8] = ia_ce*nb*nc*ng + ib_ce*nc*ng + ic_ce*ng + ig
2781 weights = num.empty(a.size*8, dtype=float)
2782 weights[0::8] = va_fl * vb_fl * vc_fl
2783 weights[1::8] = va_ce * vb_fl * vc_fl
2784 weights[2::8] = va_fl * vb_ce * vc_fl
2785 weights[3::8] = va_ce * vb_ce * vc_fl
2786 weights[4::8] = va_fl * vb_fl * vc_ce
2787 weights[5::8] = va_ce * vb_fl * vc_ce
2788 weights[6::8] = va_fl * vb_ce * vc_ce
2789 weights[7::8] = va_ce * vb_ce * vc_ce
2791 return irecords, weights
2793 self._index_function = index_function
2794 self._indices_function = indices_function
2795 self._vicinity_function = vicinity_function
2796 self._vicinities_function = vicinities_function
2798 def make_indexing_args(self, source, receiver, icomponents):
2799 nc = icomponents.size
2801 dists = source.distances_to(self.source_origin)
2802 azis, _ = source.azibazis_to(self.source_origin)
2804 source_north_shifts = - num.cos(d2r*azis) * dists
2805 source_east_shifts = - num.sin(d2r*azis) * dists
2806 source_depths = source.depths - self.source_origin.depth
2808 n = dists.size
2810 return (num.tile(source_depths, nc//n),
2811 num.tile(source_east_shifts, nc//n),
2812 num.tile(source_north_shifts, nc//n),
2813 icomponents)
2815 def make_indexing_args1(self, source, receiver):
2816 dist = source.distance_to(self.source_origin)
2817 azi, _ = source.azibazi_to(self.source_origin)
2819 source_north_shift = - num.cos(d2r*azi) * dist
2820 source_east_shift = - num.sin(d2r*azi) * dist
2821 source_depth = source.depth - self.source_origin.depth
2823 return (source_depth,
2824 source_east_shift,
2825 source_north_shift)
2827 @property
2828 def short_extent(self):
2829 return '%g:%g:%g x %g:%g:%g x %g:%g:%g' % (
2830 self.source_depth_min/km,
2831 self.source_depth_max/km,
2832 self.source_depth_delta/km,
2833 self.source_east_shift_min/km,
2834 self.source_east_shift_max/km,
2835 self.source_east_shift_delta/km,
2836 self.source_north_shift_min/km,
2837 self.source_north_shift_max/km,
2838 self.source_north_shift_delta/km)
2841class Weighting(Object):
2842 factor = Float.T(default=1.0)
2845class Taper(Object):
2846 tmin = Timing.T()
2847 tmax = Timing.T()
2848 tfade = Float.T(default=0.0)
2849 shape = StringChoice.T(
2850 choices=['cos', 'linear'],
2851 default='cos',
2852 optional=True)
2855class SimplePattern(SObject):
2857 _pool = {}
2859 def __init__(self, pattern):
2860 self._pattern = pattern
2861 SObject.__init__(self)
2863 def __str__(self):
2864 return self._pattern
2866 @property
2867 def regex(self):
2868 pool = SimplePattern._pool
2869 if self.pattern not in pool:
2870 rpat = '|'.join(fnmatch.translate(x) for
2871 x in self.pattern.split('|'))
2872 pool[self.pattern] = re.compile(rpat, re.I)
2874 return pool[self.pattern]
2876 def match(self, s):
2877 return self.regex.match(s)
2880class WaveformType(StringChoice):
2881 choices = ['dis', 'vel', 'acc',
2882 'amp_spec_dis', 'amp_spec_vel', 'amp_spec_acc',
2883 'envelope_dis', 'envelope_vel', 'envelope_acc']
2886class ChannelSelection(Object):
2887 pattern = SimplePattern.T(optional=True)
2888 min_sample_rate = Float.T(optional=True)
2889 max_sample_rate = Float.T(optional=True)
2892class StationSelection(Object):
2893 includes = SimplePattern.T()
2894 excludes = SimplePattern.T()
2895 distance_min = Float.T(optional=True)
2896 distance_max = Float.T(optional=True)
2897 azimuth_min = Float.T(optional=True)
2898 azimuth_max = Float.T(optional=True)
2901class WaveformSelection(Object):
2902 channel_selection = ChannelSelection.T(optional=True)
2903 station_selection = StationSelection.T(optional=True)
2904 taper = Taper.T()
2905 # filter = FrequencyResponse.T()
2906 waveform_type = WaveformType.T(default='dis')
2907 weighting = Weighting.T(optional=True)
2908 sample_rate = Float.T(optional=True)
2909 gf_store_id = StringID.T(optional=True)
2912def indi12(x, n):
2913 '''
2914 Get linear interpolation index and weight.
2915 '''
2917 r = round(x)
2918 if abs(r - x) < vicinity_eps:
2919 i = int(r)
2920 if not (0 <= i < n):
2921 raise OutOfBounds()
2923 return ((int(r), 1.),)
2924 else:
2925 f = math.floor(x)
2926 i = int(f)
2927 if not (0 <= i < n-1):
2928 raise OutOfBounds()
2930 v = x-f
2931 return ((i, 1.-v), (i + 1, v))
2934def float_or_none(s):
2935 units = {
2936 'k': 1e3,
2937 'M': 1e6,
2938 }
2940 factor = 1.0
2941 if s and s[-1] in units:
2942 factor = units[s[-1]]
2943 s = s[:-1]
2944 if not s:
2945 raise ValueError("unit without a number: '%s'" % s)
2947 if s:
2948 return float(s) * factor
2949 else:
2950 return None
2953class GridSpecError(Exception):
2954 def __init__(self, s):
2955 Exception.__init__(self, 'invalid grid specification: %s' % s)
2958def parse_grid_spec(spec):
2959 try:
2960 result = []
2961 for dspec in spec.split(','):
2962 t = dspec.split('@')
2963 num = start = stop = step = None
2964 if len(t) == 2:
2965 num = int(t[1])
2966 if num <= 0:
2967 raise GridSpecError(spec)
2969 elif len(t) > 2:
2970 raise GridSpecError(spec)
2972 s = t[0]
2973 v = [float_or_none(x) for x in s.split(':')]
2974 if len(v) == 1:
2975 start = stop = v[0]
2976 if len(v) >= 2:
2977 start, stop = v[0:2]
2978 if len(v) == 3:
2979 step = v[2]
2981 if len(v) > 3 or (len(v) > 2 and num is not None):
2982 raise GridSpecError(spec)
2984 if step == 0.0:
2985 raise GridSpecError(spec)
2987 result.append((start, stop, step, num))
2989 except ValueError:
2990 raise GridSpecError(spec)
2992 return result
2995def start_stop_num(start, stop, step, num, mi, ma, inc, eps=1e-5):
2996 swap = step is not None and step < 0.
2997 if start is None:
2998 start = ma if swap else mi
2999 if stop is None:
3000 stop = mi if swap else ma
3001 if step is None:
3002 step = -inc if ma < mi else inc
3003 if num is None:
3004 if (step < 0) != (stop-start < 0):
3005 raise GridSpecError()
3007 num = int(round((stop-start)/step))+1
3008 stop2 = start + (num-1)*step
3009 if abs(stop-stop2) > eps:
3010 num = int(math.floor((stop-start)/step))+1
3011 stop = start + (num-1)*step
3012 else:
3013 stop = stop2
3015 if start == stop:
3016 num = 1
3018 return start, stop, num
3021def nditer_outer(x):
3022 return num.nditer(
3023 x, op_axes=(num.identity(len(x), dtype=int)-1).tolist())
3026def nodes(xs):
3027 ns = [x.size for x in xs]
3028 nnodes = num.prod(ns)
3029 ndim = len(xs)
3030 nodes = num.empty((nnodes, ndim), dtype=xs[0].dtype)
3031 for idim in range(ndim-1, -1, -1):
3032 x = xs[idim]
3033 nrepeat = num.prod(ns[idim+1:], dtype=int)
3034 ntile = num.prod(ns[:idim], dtype=int)
3035 nodes[:, idim] = num.repeat(num.tile(x, ntile), nrepeat)
3037 return nodes
3040def filledi(x, n):
3041 a = num.empty(n, dtype=int)
3042 a.fill(x)
3043 return a
3046config_type_classes = [ConfigTypeA, ConfigTypeB, ConfigTypeC]
3048discretized_source_classes = [
3049 DiscretizedExplosionSource,
3050 DiscretizedSFSource,
3051 DiscretizedMTSource,
3052 DiscretizedPorePressureSource]
3055__all__ = '''
3056Earthmodel1D
3057StringID
3058ScopeType
3059WaveformType
3060QuantityType
3061NearfieldTermsType
3062Reference
3063Region
3064CircularRegion
3065RectangularRegion
3066PhaseSelect
3067InvalidTimingSpecification
3068Timing
3069TPDef
3070OutOfBounds
3071Location
3072Receiver
3073'''.split() + [
3074 S.__name__ for S in discretized_source_classes + config_type_classes] + '''
3075ComponentScheme
3076component_scheme_to_description
3077component_schemes
3078Config
3079GridSpecError
3080Weighting
3081Taper
3082SimplePattern
3083WaveformType
3084ChannelSelection
3085StationSelection
3086WaveformSelection
3087nditer_outer
3088dump
3089load
3090discretized_source_classes
3091config_type_classes
3092UnavailableScheme
3093InterpolationMethod
3094SeismosizerTrace
3095SeismosizerResult
3096Result
3097StaticResult
3098TimingAttributeError
3099'''.split()