Exemple #1
0
class NoiseAnalyserConfig(AnalyserConfig):
    """Configuration parameters for the pre-event noise analysis."""

    nwindows = Int.T(default=1, help='number of windows for trace splitting')

    pre_event_noise_duration = Float.T(
        default=0., help='Total length of noise trace in the analysis')

    phase_def = String.T(
        default='P', help='Onset of phase_def used for upper limit of window')

    check_events = Bool.T(default=False,
                          help='check the GlobalCMT for M>5 earthquakes'
                          ' that produce phase arrivals'
                          ' contaminating and affecting the noise analysis')

    statistic = StringChoice.T(
        choices=('var', 'std'),
        default='var',
        help='Set weight to inverse of noise variance (var) or standard '
        'deviation (std).')

    mode = StringChoice.T(
        choices=('weighting', 'weeding'),
        default='weighting',
        help='Generate weights based on inverse of noise measure (weighting), '
        'or discrete on/off style in combination with cutoff value '
        '(weeding).')

    cutoff = Float.T(
        optional=True,
        help='Set weight to zero, when noise level exceeds median by the '
        'given cutoff factor.')

    cutoff_exception_on_high_snr = Float.T(
        optional=True,
        help='Exclude from cutoff when max amplitude exceeds standard '
        'deviation times this factor.')

    def get_analyser(self):
        return NoiseAnalyser(
            nwindows=self.nwindows,
            pre_event_noise_duration=self.pre_event_noise_duration,
            check_events=self.check_events,
            phase_def=self.phase_def,
            statistic=self.statistic,
            mode=self.mode,
            cutoff=self.cutoff,
            cutoff_exception_on_high_snr=self.cutoff_exception_on_high_snr)
Exemple #2
0
class GeodeticConfig(Object):
    """
    Config for geodetic data optimization related parameters.
    """

    datadir = String.T(default='./')
    names = List.T(String.T(), default=['Data prefix filenames here ...'])
    blacklist = List.T(String.T(),
                       optional=True,
                       default=['placeholder'],
                       help='Station name for station to be thrown out.')
    types = List.T(String.T(),
                   default=['SAR'],
                   help='Types of geodetic data, i.e. SAR, GPS, ...')
    calc_data_cov = Bool.T(
        default=True,
        help='Flag for calculating the data covariance matrix based on the'
        ' pre P arrival data trace noise.')
    interpolation = StringChoice.T(
        choices=['nearest_neighbor', 'multilinear'],
        default='multilinear',
        help='GF interpolation scheme during synthetics generation')
    fit_plane = Bool.T(
        default=False,
        help='Flag for inverting for additional plane parameters on each'
        ' SAR datatype')
    gf_config = GFConfig.T(default=GeodeticGFConfig.D())

    def get_hypernames(self):
        return ['_'.join(('h', typ)) for typ in self.types]
Exemple #3
0
class GNSSComponent(Object):
    ''' Component of a GNSSStation
    '''
    unit = StringChoice.T(choices=['mm', 'cm', 'm'],
                          help='Unit of displacement',
                          default='m')

    shift = Float.T(default=0., help='Component\'s shift in unit')

    sigma = Float.T(default=0.,
                    help='One sigma uncertainty of the measurement')

    def __add__(self, other):
        if not isinstance(other, self.__class__):
            raise AttributeError('Other has to be of instance %s' %
                                 self.__class__)
        comp = self.__class__()
        comp.shift = self.shift + other.shift
        comp.sigma = math.sqrt(self.sigma**2 + other.sigma**2)
        return comp

    def __iadd__(self, other):
        self.shift += other.shift
        self.sigma = math.sqrt(self.sigma**2 + other.sigma**2)
        return self
Exemple #4
0
class CMTProblemConfig(ProblemConfig):

    ranges = Dict.T(String.T(), gf.Range.T())
    distance_min = Float.T(default=0.0)
    mt_type = StringChoice.T(choices=['full', 'deviatoric'])

    def get_problem(self, event, target_groups, targets):
        if event.depth is None:
            event.depth = 0.

        base_source = gf.MTSource.from_pyrocko_event(event)
        base_source.stf = gf.HalfSinusoidSTF(duration=event.duration or 0.0)

        subs = dict(event_name=event.name,
                    event_time=util.time_to_str(event.time))

        problem = CMTProblem(name=expand_template(self.name_template, subs),
                             base_source=base_source,
                             target_groups=target_groups,
                             targets=targets,
                             ranges=self.ranges,
                             distance_min=self.distance_min,
                             mt_type=self.mt_type,
                             norm_exponent=self.norm_exponent)

        return problem
Exemple #5
0
class SeismicNoiseAnalyserConfig(Object):

    structure = StringChoice.T(
        choices=['identity', 'exponential', 'import', 'non-toeplitz'],
        default='identity',
        help='Determines data-covariance matrix structure.')
    pre_arrival_time = Float.T(
        default=5.,
        help='Time [s] before synthetic P-wave arrival until '
        'variance is estimated')
Exemple #6
0
class WaveformFitConfig(Object):
    """
    Config for specific parameters that are applied to post-process
    a specific type of waveform and calculate the misfit.
    """
    include = Bool.T(default=True,
                     help='Flag to include waveform into optimization.')
    name = String.T('any_P')
    channels = List.T(String.T(), default=['Z'])
    filterer = Filter.T(default=Filter.D())
    distances = Tuple.T(2, Float.T(), default=(30., 90.))
    interpolation = StringChoice.T(choices=['nearest_neighbor', 'multilinear'],
                                   default='multilinear',
                                   help='GF interpolation sceme')
    arrival_taper = trace.Taper.T(
        default=ArrivalTaper.D(),
        help='Taper a,b/c,d time [s] before/after wave arrival')
Exemple #7
0
class Meta(Object):
    """Meta configuration for ``Scene``."""

    scene_title = String.T(default="Unnamed Scene", help="Scene title")
    scene_id = String.T(default="None", help="Scene identification")
    satellite_name = String.T(
        default="Undefined Mission", help="Satellite mission name"
    )
    wavelength = Float.T(optional=True, help="Wavelength in [m]")
    orbital_node = StringChoice.T(
        choices=["Ascending", "Descending", "Undefined"],
        default="Undefined",
        help="Orbital direction, ascending/descending",
    )
    time_master = Timestamp.T(
        default=1481116161.930574, help="Timestamp for master acquisition"
    )
    time_slave = Timestamp.T(
        default=1482239325.482, help="Timestamp for slave acquisition"
    )
    extra = Dict.T(default={}, help="Extra header information")
    filename = String.T(optional=True)

    def __init__(self, *args, **kwargs):
        self.old_import = False

        mapping = {"orbit_direction": "orbital_node"}

        for old, new in mapping.items():
            if old in kwargs.keys():
                kwargs[new] = kwargs.pop(old, None)
                self.old_import = True

        Object.__init__(self, *args, **kwargs)

    @property
    def time_separation(self):
        """
        :getter: Absolute time difference between ``time_master``
                 and ``time_slave``
        :type: timedelta
        """
        return dt.fromtimestamp(self.time_slave) - dt.fromtimestamp(self.time_master)
Exemple #8
0
class FrameConfig(Object):
    """Config object holding :class:`kite.scene.Scene` configuration"""

    llLat = Float.T(default=0.0, help="Scene latitude of lower left corner")
    llLon = Float.T(default=0.0, help="Scene longitude of lower left corner")
    dN = Float.T(default=25.0, help="Scene pixel spacing in north, give [m] or [deg]")
    dE = Float.T(default=25.0, help="Scene pixel spacing in east, give [m] or [deg]")
    spacing = StringChoice.T(
        choices=("degree", "meter"), default="meter", help="Unit of pixel space"
    )

    def __init__(self, *args, **kwargs):
        self.old_import = False
        mapping = {"dE": "dLon", "dN": "dLat"}

        for new, old in mapping.items():
            if old in kwargs:
                kwargs[new] = kwargs.pop(old)
                kwargs["spacing"] = "degree"
                self.old_import = True

        Object.__init__(self, *args, **kwargs)
Exemple #9
0
class FrameConfig(Object):
    """Config object holding :class:`kite.scene.Scene` configuration """
    llLat = Float.T(default=0., help='Scene latitude of lower left corner')
    llLon = Float.T(default=0., help='Scene longitude of lower left corner')
    dN = Float.T(default=25.,
                 help='Scene pixel spacing in north, give [m] or [deg]')
    dE = Float.T(default=25.,
                 help='Scene pixel spacing in east, give [m] or [deg]')
    spacing = StringChoice.T(choices=('degree', 'meter'),
                             default='meter',
                             help='Unit of pixel space')

    def __init__(self, *args, **kwargs):
        self.old_import = False
        mapping = {'dE': 'dLon', 'dN': 'dLat'}

        for new, old in mapping.items():
            if old in kwargs:
                kwargs[new] = kwargs.pop(old)
                kwargs['spacing'] = 'degree'
                self.old_import = True

        Object.__init__(self, *args, **kwargs)
Exemple #10
0
class GeodeticConfig(Object):
    """
    Config for geodetic data optimization related parameters.
    """

    datadir = String.T(default='./')
    names = List.T(String.T(), default=['Data prefix filenames here ...'])
    blacklist = List.T(String.T(),
                       optional=True,
                       default=[],
                       help='GPS station name or scene name to be thrown out.')
    types = List.T(String.T(),
                   default=['SAR'],
                   help='Types of geodetic data, i.e. SAR, GPS, ...')
    calc_data_cov = Bool.T(
        default=True,
        help='Flag for calculating the data covariance matrix, '
        'outsourced to "kite"')
    interpolation = StringChoice.T(
        choices=['nearest_neighbor', 'multilinear'],
        default='multilinear',
        help='GF interpolation scheme during synthetics generation')
    fit_plane = Bool.T(
        default=False,
        help='Flag for inverting for additional plane parameters on each'
        ' SAR datatype')
    gf_config = GFConfig.T(default=GeodeticGFConfig.D())

    def get_hypernames(self):
        return ['_'.join(('h', typ)) for typ in self.types]

    def get_hierarchical_names(self):
        if self.fit_plane:
            return [name + '_ramp' for name in self.names]
        else:
            return []
Exemple #11
0
class GNSSStation(Location):
    ''' Representation of a GNSS station during a campaign measurement

    For more information see
    http://kb.unavco.org/kb/assets/660/UNAVCO_Campaign_GPS_GNSS_Handbook.pdf
    '''

    code = String.T(help='Four letter station code', optional=True)

    style = StringChoice.T(choices=['static', 'rapid_static', 'kinematic'],
                           default='static')

    survey_start = DateTimestamp.T(optional=True)

    survey_end = DateTimestamp.T(optional=True)

    north = GNSSComponent.T(default=GNSSComponent.D())

    east = GNSSComponent.T(default=GNSSComponent.D())

    up = GNSSComponent.T(default=GNSSComponent.D())

    def __init__(self, *args, **kwargs):
        Location.__init__(self, *args, **kwargs)
Exemple #12
0
 class X1(Object):
     xmltagname = 'root'
     m = Union.T(members=[Int.T(), StringChoice.T(['small', 'large'])])
Exemple #13
0
 class U(Union):
     members = [Int.T(), StringChoice.T(['small', 'large'])]
Exemple #14
0
 class X(Object):
     m = StringChoice.T(['a', 'b'])
Exemple #15
0
 class X(Object):
     m = StringChoice.T(['a', 'b'], ignore_case=True)
Exemple #16
0
class ScenePatch(Object):
    lat_center = Float.T(help='Center latitude anchor.')
    lon_center = Float.T(help='center longitude anchor.')
    time_master = Timestamp.T(help='Timestamp of the master.')
    time_slave = Timestamp.T(help='Timestamp of the slave.')
    inclination = Float.T(
        help='Orbital inclination towards the equatorial plane [deg].')
    apogee = Float.T(help='Apogee of the satellite in [m].')
    swath_width = Float.T(default=250 * km, help='Swath width in [m].')
    track_length = Float.T(help='Track length in [m].')
    incident_angle = Float.T(help='Ground incident angle in [deg].')
    resolution = Tuple.T(help='Resolution of raster in east x north [px].')
    orbital_node = StringChoice.T(['Ascending', 'Descending'],
                                  help='Orbit heading.')
    mask_water = Bool.T(default=True, help='Mask water bodies.')

    class SatelliteGeneratorTarget(gf.SatelliteTarget):
        def __init__(self, scene_patch, *args, **kwargs):
            gf.SatelliteTarget.__init__(self, *args, **kwargs)

            self.scene_patch = scene_patch

        def post_process(self, *args, **kwargs):
            resp = gf.SatelliteTarget.post_process(self, *args, **kwargs)

            from kite import Scene
            from kite.scene import SceneConfig, FrameConfig, Meta

            patch = self.scene_patch

            grid, _ = patch.get_grid()

            displacement = num.empty_like(grid)
            displacement.fill(num.nan)
            displacement[patch.get_mask()] = resp.result['displacement.los']

            theta, phi = patch.get_incident_angles()

            llLat, llLon = patch.get_ll_anchor()
            urLat, urLon = patch.get_ur_anchor()
            dLon = num.abs(llLon - urLon) / patch.resolution[0]
            dLat = num.abs(llLat - urLat) / patch.resolution[1]

            scene_config = SceneConfig(meta=Meta(
                scene_title='Pyrocko Scenario Generator - {orbit} ({time})'.
                format(orbit=self.scene_patch.orbital_node,
                       time=datetime.now()),
                orbital_node=patch.orbital_node,
                scene_id='pyrocko_scenario_%s' % self.scene_patch.orbital_node,
                satellite_name='Sentinel-1 (Scenario)'),
                                       frame=FrameConfig(llLat=float(llLat),
                                                         llLon=float(llLon),
                                                         dN=float(dLat),
                                                         dE=float(dLon),
                                                         spacing='degree'))

            scene = Scene(displacement=displacement,
                          theta=theta,
                          phi=phi,
                          config=scene_config)

            resp.scene = scene

            return resp

    def __init__(self, *args, **kwargs):
        Object.__init__(self, *args, **kwargs)
        self._mask_water = None

    @property
    def width(self):
        track_shift = num.abs(
            num.cos(self.inclination * d2r) * self.track_length)

        return self.swath_width + track_shift

    def get_ll_anchor(self):
        return od.ne_to_latlon(self.lat_center, self.lon_center,
                               -self.track_length / 2, -self.width / 2)

    def get_ur_anchor(self):
        return od.ne_to_latlon(self.lat_center, self.lon_center,
                               self.track_length / 2, self.width / 2)

    def get_ul_anchor(self):
        return od.ne_to_latlon(self.lat_center, self.lon_center,
                               self.track_length / 2, -self.width / 2)

    def get_lr_anchor(self):
        return od.ne_to_latlon(self.lat_center, self.lon_center,
                               -self.track_length / 2, self.width / 2)

    def get_corner_coordinates(self):
        inc = self.inclination

        llLat, llLon = self.get_ll_anchor()
        urLat, urLon = self.get_ur_anchor()

        if self.orbital_node == 'Ascending':

            ulLat, ulLon = od.ne_to_latlon(
                self.lat_center, self.lon_center, self.track_length / 2,
                -num.tan(inc * d2r) * self.width / 2)
            lrLat, lrLon = od.ne_to_latlon(self.lat_center, self.lon_center,
                                           -self.track_length / 2,
                                           num.tan(inc * d2r) * self.width / 2)

        elif self.orbital_node == 'Descending':
            urLat, urLon = od.ne_to_latlon(self.lat_center, self.lon_center,
                                           self.track_length / 2,
                                           num.tan(inc * d2r) * self.width / 2)
            llLat, llLon = od.ne_to_latlon(
                self.lat_center, self.lon_center, -self.track_length / 2,
                -num.tan(inc * d2r) * self.width / 2)

        return ((llLat, llLon), (ulLat, ulLon), (urLat, urLon), (lrLat, lrLon))

    def get_grid(self):
        '''Return relative positions of scatterer.

        :param track: Acquisition track, from `'asc'` or `'dsc'`.
        :type track: string
        '''
        easts = num.linspace(0, self.width, self.resolution[0])
        norths = num.linspace(0, self.track_length, self.resolution[1])

        return num.meshgrid(easts, norths)

    def get_mask_track(self):
        east_shifts, north_shifts = self.get_grid()
        norths = north_shifts[:, 0]
        track = num.abs(num.cos(self.inclination * d2r)) * norths

        track_mask = num.logical_and(
            east_shifts > track[:, num.newaxis], east_shifts <
            (track + self.swath_width)[:, num.newaxis])

        if self.orbital_node == 'Ascending':
            track_mask = num.fliplr(track_mask)

        return track_mask

    def get_mask_water(self):
        if self._mask_water is None:
            east_shifts, north_shifts = self.get_grid()

            east_shifts -= east_shifts[0, -1] / 2
            north_shifts -= north_shifts[-1, -1] / 2

            latlon = od.ne_to_latlon(self.lat_center, self.lon_center,
                                     north_shifts.ravel(), east_shifts.ravel())
            points = num.array(latlon).T
            self._mask_water = get_gsshg().get_land_mask(points)\
                .reshape(*east_shifts.shape)

        return self._mask_water

    def get_mask(self):
        mask_track = self.get_mask_track()
        if self.mask_water:
            mask_water = self.get_mask_water()
            return num.logical_and(mask_track, mask_water)
        return mask_track

    def get_incident_angles(self):
        # theta: elevation angle towards satellite from horizon in radians.
        # phi:  Horizontal angle towards satellite' :abbr:`line of sight (LOS)`
        #       in [rad] from East.
        east_shifts, _ = self.get_grid()

        phi = num.empty_like(east_shifts)
        theta = num.empty_like(east_shifts)

        east_shifts += num.tan(self.incident_angle * d2r) * self.apogee
        theta = num.arctan(east_shifts / self.apogee)

        if self.orbital_node == 'Ascending':
            phi.fill(self.inclination * d2r + num.pi / 2)
        elif self.orbital_node == 'Descending':
            phi.fill(2 * num.pi - (self.inclination * d2r + 3 / 2 * num.pi))
            theta = num.fliplr(theta)
        else:
            raise AttributeError('Orbital node %s not defined!' %
                                 self.orbital_node)

        return theta, phi

    def get_target(self):
        gE, gN = self.get_grid()
        mask = self.get_mask()

        east_shifts = gE[mask].ravel()
        north_shifts = gN[mask].ravel()
        llLat, llLon = self.get_ll_anchor()

        ncoords = east_shifts.size

        theta, phi = self.get_incident_angles()

        theta = theta[mask].ravel()
        phi = phi[mask].ravel()

        if ncoords == 0:
            logger.warning('InSAR taget has no valid points,'
                           ' maybe it\'s all water?')
            return

        return self.SatelliteGeneratorTarget(scene_patch=self,
                                             lats=num_full(ncoords,
                                                           fill_value=llLat),
                                             lons=num_full(ncoords,
                                                           fill_value=llLon),
                                             east_shifts=east_shifts,
                                             north_shifts=north_shifts,
                                             theta=theta,
                                             phi=phi)
Exemple #17
0
class WaveformGenerator(TargetGenerator):

    station_generator = StationGenerator.T(
        default=RandomStationGenerator.D(),
        help='The StationGenerator for creating the stations.')

    noise_generator = WaveformNoiseGenerator.T(
        default=WhiteNoiseGenerator.D(),
        help='Add Synthetic noise on the waveforms.')

    store_id = gf.StringID.T(
        default=DEFAULT_STORE_ID,
        help='The GF store to use for forward-calculations.')

    seismogram_quantity = StringChoice.T(
        choices=['displacement', 'velocity', 'acceleration', 'counts'],
        default='displacement')

    vmin_cut = Float.T(
        default=2000.,
        help='Minimum velocity to seismic velicty to consider in the model.')
    vmax_cut = Float.T(
        default=8000.,
        help='Maximum velocity to seismic velicty to consider in the model.')

    fmin = Float.T(
        default=0.01,
        help='Minimum frequency/wavelength to resolve in the'
             ' synthetic waveforms.')

    tabulated_phases = List.T(
        gf.meta.TPDef.T(), optional=True,
        help='Define seismic phases to be calculated.')

    tabulated_phases_from_store = Bool.T(
        default=False,
        help='Calculate seismic phase arrivals for all travel-time tables '
             'defined in GF store.')

    tabulated_phases_noise_scale = Float.T(
        default=0.0,
        help='Standard deviation of normally distributed noise added to '
             'calculated phase arrivals.')

    taper = trace.Taper.T(
        optional=True,
        help='Time domain taper applied to synthetic waveforms.')

    compensate_synthetic_offsets = Bool.T(
        default=False,
        help='Center synthetic trace amplitudes using mean of waveform tips.')

    tinc = Float.T(
        optional=True,
        help='Time increment of waveforms.')

    continuous = Bool.T(
        default=True,
        help='Only produce traces that intersect with events.')

    def __init__(self, *args, **kwargs):
        super(WaveformGenerator, self).__init__(*args, **kwargs)
        self._targets = []
        self._piles = {}

    def _get_pile(self, path):
        apath = op.abspath(path)
        assert op.isdir(apath)

        if apath not in self._piles:
            fns = util.select_files(
                [apath], show_progress=False)

            p = pile.Pile()
            if fns:
                p.load_files(fns, fileformat='mseed', show_progress=False)

            self._piles[apath] = p

        return self._piles[apath]

    def get_stations(self):
        return self.station_generator.get_stations()

    def get_targets(self):
        if self._targets:
            return self._targets

        for station in self.get_stations():
            channel_data = []
            channels = station.get_channels()
            if channels:
                for channel in channels:
                    channel_data.append([
                        channel.name,
                        channel.azimuth,
                        channel.dip])

            else:
                for c_name in ['BHZ', 'BHE', 'BHN']:
                    channel_data.append([
                        c_name,
                        model.guess_azimuth_from_name(c_name),
                        model.guess_dip_from_name(c_name)])

            for c_name, c_azi, c_dip in channel_data:

                target = gf.Target(
                    codes=(
                        station.network,
                        station.station,
                        station.location,
                        c_name),
                    quantity='displacement',
                    lat=station.lat,
                    lon=station.lon,
                    north_shift=station.north_shift,
                    east_shift=station.east_shift,
                    depth=station.depth,
                    store_id=self.store_id,
                    optimization='enable',
                    interpolation='nearest_neighbor',
                    azimuth=c_azi,
                    dip=c_dip)

                self._targets.append(target)

        return self._targets

    def get_time_range(self, sources):
        dmin, dmax = self.station_generator.get_distance_range(sources)

        times = num.array([source.time for source in sources],
                          dtype=num.float)

        tmin_events = num.min(times)
        tmax_events = num.max(times)

        tmin = tmin_events + dmin / self.vmax_cut - 10.0 / self.fmin
        tmax = tmax_events + dmax / self.vmin_cut + 10.0 / self.fmin

        return tmin, tmax

    def get_codes_to_deltat(self, engine, sources):
        deltats = {}

        targets = self.get_targets()
        for source in sources:
            for target in targets:
                deltats[target.codes] = engine.get_store(
                    target.store_id).config.deltat

        return deltats

    def get_useful_time_increment(self, engine, sources):
        _, dmax = self.station_generator.get_distance_range(sources)
        tinc = dmax / self.vmin_cut + 2.0 / self.fmin

        deltats = set(self.get_codes_to_deltat(engine, sources).values())
        deltat = reduce(util.lcm, deltats)
        tinc = int(round(tinc / deltat)) * deltat
        return tinc

    def get_relevant_sources(self, sources, tmin, tmax):
        dmin, dmax = self.station_generator.get_distance_range(sources)
        trange = tmax - tmin
        tmax_pad = trange + tmax + dmin / self.vmax_cut
        tmin_pad = tmin - (dmax / self.vmin_cut + trange)

        return [s for s in sources if s.time < tmax_pad and s.time > tmin_pad]

    def get_waveforms(self, engine, sources, tmin, tmax):

        sources_relevant = self.get_relevant_sources(sources, tmin, tmax)
        if not (self.continuous or sources_relevant):
            return []

        trs = {}
        tts = util.time_to_str

        for nslc, deltat in self.get_codes_to_deltat(engine, sources).items():
            tr_tmin = int(round(tmin / deltat)) * deltat
            tr_tmax = (int(round(tmax / deltat))-1) * deltat
            nsamples = int(round((tr_tmax - tr_tmin) / deltat)) + 1

            tr = trace.Trace(
                *nslc,
                tmin=tr_tmin,
                ydata=num.zeros(nsamples),
                deltat=deltat)

            self.noise_generator.add_noise(tr)

            trs[nslc] = tr

        logger.debug('Forward modelling waveforms between %s - %s...'
                     % (tts(tmin, format='%Y-%m-%d_%H-%M-%S'),
                        tts(tmax, format='%Y-%m-%d_%H-%M-%S')))

        if not sources_relevant:
            return list(trs.values())

        targets = self.get_targets()
        response = engine.process(sources_relevant, targets)
        for source, target, res in response.iter_results(
                get='results'):

            if isinstance(res, gf.SeismosizerError):
                logger.warning(
                    'Out of bounds! \nTarget: %s\nSource: %s\n' % (
                        '.'.join(target.codes)), source)
                continue

            tr = res.trace.pyrocko_trace()

            candidate = trs[target.codes]
            if not candidate.overlaps(tr.tmin, tr.tmax):
                continue

            if self.compensate_synthetic_offsets:
                tr.ydata -= (num.mean(tr.ydata[-3:-1]) +
                             num.mean(tr.ydata[1:3])) / 2.

            if self.taper:
                tr.taper(self.taper)

            resp = self.get_transfer_function(target.codes)
            if resp:
                tr = tr.transfer(transfer_function=resp)

            candidate.add(tr)
            trs[target.codes] = candidate

        return list(trs.values())

    def get_onsets(self, engine, sources, *args, **kwargs):

        targets = {t.codes[:3]: t for t in self.get_targets()}

        markers = []
        for source in sources:
            ev = source.pyrocko_event()
            markers.append(EventMarker(ev))
            for nsl, target in targets.items():
                store = engine.get_store(target.store_id)
                if self.tabulated_phases:
                    tabulated_phases = self.tabulated_phases

                elif self.tabulated_phases_from_store:
                    tabulated_phases = store.config.tabulated_phases
                else:
                    tabulated_phases = []

                for phase in tabulated_phases:
                    t = store.t(phase.id, source, target)
                    if not t:
                        continue

                    noise_scale = self.tabulated_phases_noise_scale
                    if noise_scale != 0.0:
                        t += num.random.normal(scale=noise_scale)

                    t += source.time
                    markers.append(
                        PhaseMarker(
                            phasename=phase.id,
                            tmin=t,
                            tmax=t,
                            event=source.pyrocko_event(),
                            nslc_ids=(nsl+('*',),)
                            )
                        )
        return markers

    def get_transfer_function(self, codes):
        if self.seismogram_quantity == 'displacement':
            return None
        elif self.seismogram_quantity == 'velocity':
            return trace.DifferentiationResponse(1)
        elif self.seismogram_quantity == 'acceleration':
            return trace.DifferentiationResponse(2)
        elif self.seismogram_quantity == 'counts':
            raise NotImplementedError()

    def ensure_data(self, engine, sources, path, tmin=None, tmax=None):
        self.ensure_waveforms(engine, sources, path, tmin, tmax)
        self.ensure_responses(path)

    def ensure_waveforms(self, engine, sources, path, tmin=None, tmax=None):

        path_waveforms = op.join(path, 'waveforms')
        util.ensuredir(path_waveforms)

        p = self._get_pile(path_waveforms)

        nslc_ids = set(target.codes for target in self.get_targets())

        def have_waveforms(tmin, tmax):
            trs_have = p.all(
                tmin=tmin, tmax=tmax,
                load_data=False, degap=False,
                trace_selector=lambda tr: tr.nslc_id in nslc_ids)

            return any(tr.data_len() > 0 for tr in trs_have)

        def add_files(paths):
            p.load_files(paths, fileformat='mseed', show_progress=False)

        path_traces = op.join(
            path_waveforms,
            '%(wmin_year)s',
            '%(wmin_month)s',
            '%(wmin_day)s',
            'waveform_%(network)s_%(station)s_' +
            '%(location)s_%(channel)s_%(tmin)s_%(tmax)s.mseed')

        tmin_all, tmax_all = self.get_time_range(sources)
        tmin = tmin if tmin is not None else tmin_all
        tmax = tmax if tmax is not None else tmax_all
        tts = util.time_to_str

        tinc = self.tinc or self.get_useful_time_increment(engine, sources)
        tmin = math.floor(tmin / tinc) * tinc
        tmax = math.ceil(tmax / tinc) * tinc

        nwin = int(round((tmax - tmin) / tinc))

        pbar = None
        for iwin in range(nwin):
            tmin_win = tmin + iwin*tinc
            tmax_win = tmin + (iwin+1)*tinc

            if have_waveforms(tmin_win, tmax_win):
                continue

            if pbar is None:
                pbar = util.progressbar('Generating waveforms', (nwin-iwin))

            pbar.update(iwin)

            trs = self.get_waveforms(engine, sources, tmin_win, tmax_win)

            try:
                wpaths = io.save(
                    trs, path_traces,
                    additional=dict(
                        wmin_year=tts(tmin_win, format='%Y'),
                        wmin_month=tts(tmin_win, format='%m'),
                        wmin_day=tts(tmin_win, format='%d'),
                        wmin=tts(tmin_win, format='%Y-%m-%d_%H-%M-%S'),
                        wmax_year=tts(tmax_win, format='%Y'),
                        wmax_month=tts(tmax_win, format='%m'),
                        wmax_day=tts(tmax_win, format='%d'),
                        wmax=tts(tmax_win, format='%Y-%m-%d_%H-%M-%S')))

                for wpath in wpaths:
                    logger.debug('Generated file: %s' % wpath)

                add_files(wpaths)

            except FileSaveError as e:
                raise ScenarioError(str(e))

        if pbar is not None:
            pbar.finish()

    def ensure_responses(self, path):
        from pyrocko.io import stationxml

        path_responses = op.join(path, 'meta')
        util.ensuredir(path_responses)

        fn_stationxml = op.join(path_responses, 'stations.xml')
        if op.exists(fn_stationxml):
            return

        logger.debug('Writing waveform meta information to StationXML...')

        stations = self.station_generator.get_stations()
        sxml = stationxml.FDSNStationXML.from_pyrocko_stations(stations)

        sunit = {
            'displacement': 'M',
            'velocity': 'M/S',
            'acceleration': 'M/S**2',
            'counts': 'COUNTS'}[self.seismogram_quantity]

        response = stationxml.Response(
            instrument_sensitivity=stationxml.Sensitivity(
                value=1.,
                frequency=1.,
                input_units=stationxml.Units(sunit),
                output_units=stationxml.Units('COUNTS')),
            stage_list=[])

        for net, station, channel in sxml.iter_network_station_channels():
            channel.response = response

        sxml.dump_xml(filename=fn_stationxml)

    def add_map_artists(self, engine, sources, automap):
        automap.add_stations(self.get_stations())
Exemple #18
0
class HistogramPlot(PlotConfig):
    '''
    Histograms or Gaussian kernel densities (default) of all parameters
    (marginal distributions of model parameters).

    The histograms (by default shown as Gaussian kernel densities) show (red
    curved solid line) the distributions of the parameters (marginals) along
    with some characteristics: The red solid vertical line gives the median of
    the distribution and the dashed red vertical line the mean value. Dark gray
    vertical lines show reference values (given in the event.txt file). The
    overlapping red-shaded areas show the 68% confidence intervals (innermost
    area), the 90% confidence intervals (middle area) and the minimum and
    maximum values (widest area). The plot ranges are defined by the given
    parameter bounds and show the model space. Well resolved model parameters
    show peaked distributions.
    '''

    name = 'histogram'
    size_cm = Tuple.T(2, Float.T(), default=(12.5, 7.5))
    exclude = List.T(String.T())
    include = List.T(String.T())
    method = StringChoice.T(
        choices=['gaussian_kde', 'histogram'],
        default='gaussian_kde')
    show_reference = Bool.T(default=True)

    def make(self, environ):
        cm = environ.get_plot_collection_manager()
        history = environ.get_history(subset='harvest')

        mpl_init(fontsize=self.font_size)
        cm.create_group_mpl(
            self,
            self.draw_figures(history),
            title=u'Histogram',
            section='solution',
            feather_icon='bar-chart-2',
            description=u'''
Distribution of the problem's parameters.

The histograms are shown either as Gaussian kernel densities (red curved solid
line) or as bar plots the distributions of the parameters (marginals) along
with some characteristics:

The red solid vertical line gives the median of the distribution and the dashed
red vertical line the mean value. Dark gray vertical lines show reference
parameter values if given in the event.txt file. The overlapping red-shaded
areas show the 68% confidence intervals (innermost area), the 90% confidence
intervals (middle area) and the minimum and maximum values (widest area). The
plot ranges are defined by the given parameter bounds and show the model
space.
''')

    def draw_figures(self, history):

        import scipy.stats
        from grond.core import make_stats

        exclude = self.exclude
        include = self.include
        figsize = self.size_inch
        fontsize = self.font_size
        method = self.method
        ref_color = mpl_color('aluminium6')
        stats_color = mpl_color('scarletred2')
        bar_color = mpl_color('scarletred1')
        stats_color3 = mpl_color('scarletred3')

        problem = history.problem

        models = history.models

        bounds = problem.get_combined_bounds()
        exclude = list(exclude)
        for ipar in range(problem.ncombined):
            par = problem.combined[ipar]
            vmin, vmax = bounds[ipar]
            if vmin == vmax:
                exclude.append(par.name)

        xref = problem.get_reference_model()

        smap = {}
        iselected = 0
        for ipar in range(problem.ncombined):
            par = problem.combined[ipar]
            if exclude and par.name in exclude or \
                    include and par.name not in include:
                continue

            smap[iselected] = ipar
            iselected += 1

        nselected = iselected
        del iselected

        pnames = [
            problem.combined[smap[iselected]].name
            for iselected in range(nselected)]

        rstats = make_stats(problem, models,
                            history.get_primary_chain_misfits(),
                            pnames=pnames)

        for iselected in range(nselected):
            ipar = smap[iselected]
            par = problem.combined[ipar]
            vs = problem.extract(models, ipar)
            vmin, vmax = bounds[ipar]

            fig = plt.figure(figsize=figsize)
            labelpos = mpl_margins(
                fig, nw=1, nh=1, w=7., bottom=5., top=1, units=fontsize)

            axes = fig.add_subplot(1, 1, 1)
            labelpos(axes, 2.5, 2.0)
            axes.set_xlabel(par.get_label())
            axes.set_ylabel('PDF')
            axes.set_xlim(*fixlim(*par.scaled((vmin, vmax))))

            if method == 'gaussian_kde':
                try:
                    kde = scipy.stats.gaussian_kde(vs)
                except Exception:
                    logger.warn(
                        'Cannot create plot histogram with gaussian_kde: '
                        'possibly all samples have the same value.')
                    continue

                vps = num.linspace(vmin, vmax, 600)
                pps = kde(vps)

                axes.plot(
                    par.scaled(vps), par.inv_scaled(pps), color=stats_color)

            elif method == 'histogram':
                pps, edges = num.histogram(
                    vs,
                    bins=num.linspace(vmin, vmax, num=40),
                    density=True)
                vps = 0.5 * (edges[:-1] + edges[1:])

                axes.bar(par.scaled(vps), par.inv_scaled(pps),
                         par.scaled(2.*(vps - edges[:-1])),
                         color=bar_color)

            pstats = rstats.parameter_stats_list[iselected]

            axes.axvspan(
                par.scaled(pstats.minimum),
                par.scaled(pstats.maximum),
                color=stats_color, alpha=0.1)
            axes.axvspan(
                par.scaled(pstats.percentile16),
                par.scaled(pstats.percentile84),
                color=stats_color, alpha=0.1)
            axes.axvspan(
                par.scaled(pstats.percentile5),
                par.scaled(pstats.percentile95),
                color=stats_color, alpha=0.1)

            axes.axvline(
                par.scaled(pstats.median),
                color=stats_color3, alpha=0.5)
            axes.axvline(
                par.scaled(pstats.mean),
                color=stats_color3, ls=':', alpha=0.5)

            if self.show_reference:
                axes.axvline(
                    par.scaled(problem.extract(xref, ipar)),
                    color=ref_color)

            item = PlotItem(name=par.name)
            item.attributes['parameters'] = [par.name]
            yield item, fig
Exemple #19
0
class GNSSStation(Location):
    ''' Representation of a GNSS station during a campaign measurement

    For more information see
    http://kb.unavco.org/kb/assets/660/UNAVCO_Campaign_GPS_GNSS_Handbook.pdf
    '''

    code = String.T(help='Four letter station code', optional=True)

    style = StringChoice.T(choices=['static', 'rapid_static', 'kinematic'],
                           default='static')

    survey_start = DateTimestamp.T(optional=True, help='Survey start time')

    survey_end = DateTimestamp.T(optional=True, help='Survey end time')

    correlation_ne = Float.T(default=0.,
                             help='North-East component correlation')

    correlation_eu = Float.T(default=0., help='East-Up component correlation')

    correlation_nu = Float.T(default=0., help='North-Up component correlation')

    north = GNSSComponent.T(optional=True)

    east = GNSSComponent.T(optional=True)

    up = GNSSComponent.T(optional=True)

    def __eq__(self, other):
        try:
            return self.code == other.code
        except AttributeError:
            return False

    def get_covariance_matrix(self):
        components = self.components.values()
        ncomponents = self.ncomponents

        covar = num.zeros((ncomponents, ncomponents))
        for ic1, comp1 in enumerate(components):
            for ic2, comp2 in enumerate(components):
                corr = self._get_comp_correlation(comp1, comp2)
                covar[ic1, ic2] = corr * comp1.sigma * comp2.sigma

        # This floating point operation is inaccurate:
        # corr * comp1.sigma * comp2.sigma != corr * comp2.sigma * comp1.sigma
        #
        # Hence this identity
        covar[num.tril_indices_from(covar, k=-1)] = \
            covar[num.triu_indices_from(covar, k=1)]

        return covar

    def get_correlation_matrix(self):
        components = self.components.values()
        ncomponents = self.ncomponents

        corr = num.zeros((ncomponents, ncomponents))
        corr[num.diag_indices_from(corr)] = num.array(
            [c.sigma for c in components])

        for ic1, comp1 in enumerate(components):
            for ic2, comp2 in enumerate(components):
                if comp1 is comp2:
                    continue
                corr[ic1, ic2] = self._get_comp_correlation(comp1, comp2)

        # See comment at get_covariance_matrix
        corr[num.tril_indices_from(corr, k=-1)] = \
            corr[num.triu_indices_from(corr, k=1)]

        return corr

    def get_displacement_data(self):
        return num.array([c.shift for c in self.components.values()])

    def get_component_mask(self):
        return num.array([
            False if self.__getattribute__(name) is None else True
            for name in ('north', 'east', 'up')
        ],
                         dtype=num.bool)

    @property
    def components(self):
        return OrderedDict([(name, self.__getattribute__(name))
                            for name in ('north', 'east', 'up')
                            if self.__getattribute__(name) is not None])

    @property
    def ncomponents(self):
        return len(self.components)

    def _get_comp_correlation(self, comp1, comp2):
        if comp1 is comp2:
            return 1.

        s = self

        correlation_map = {
            (s.north, s.east): s.correlation_ne,
            (s.east, s.up): s.correlation_eu,
            (s.north, s.up): s.correlation_nu
        }

        return correlation_map.get((comp1, comp2),
                                   correlation_map.get((comp2, comp1), False))
Exemple #20
0
class ProblemConfig(Object):
    """
    Config for optimization problem to setup.
    """
    mode = StringChoice.T(
        choices=['geometry', 'ffi', 'interseismic'],
        default='geometry',
        help='Problem to solve: "geometry", "ffi",'
        ' "interseismic"',
    )
    mode_config = ModeConfig.T(
        optional=True, help='Global optimization mode specific parameters.')
    source_type = StringChoice.T(
        default='RectangularSource',
        choices=source_names,
        help='Source type to optimize for. Options: %s' %
        (', '.join(name for name in source_names)))
    stf_type = StringChoice.T(
        default='HalfSinusoid',
        choices=stf_names,
        help='Source time function type to use. Options: %s' %
        (', '.join(name for name in stf_names)))
    decimation_factors = Dict.T(
        default=None,
        optional=True,
        help='Determines the reduction of discretization of an extended'
        ' source.')
    n_sources = Int.T(default=1, help='Number of Sub-sources to solve for')
    datatypes = List.T(default=['geodetic'])
    hyperparameters = Dict.T(
        help='Hyperparameters to weight different types of datatypes.')
    priors = Dict.T(help='Priors of the variables in question.')

    def __init__(self, **kwargs):

        mode = 'mode'
        mode_config = 'mode_config'
        if mode in kwargs:
            omode = kwargs[mode]

            if omode == 'ffi':
                if mode_config not in kwargs:
                    kwargs[mode_config] = FFIConfig()

        Object.__init__(self, **kwargs)

    def init_vars(self, variables=None):
        """
        Initiate priors based on the problem mode and datatypes.

        Parameters
        ----------
        variables : list
            of str of variable names to initialise
        """
        if variables is None:
            variables = self.select_variables()

        self.priors = OrderedDict()
        for variable in variables:
            if variable in block_vars:
                nvars = 1
            else:
                nvars = self.n_sources

            lower = default_bounds[variable][0]
            upper = default_bounds[variable][1]
            self.priors[variable] = \
                Parameter(
                    name=variable,
                    lower=num.ones(
                        nvars,
                        dtype=tconfig.floatX) * lower,
                    upper=num.ones(
                        nvars,
                        dtype=tconfig.floatX) * upper,
                    testvalue=num.ones(
                        nvars,
                        dtype=tconfig.floatX) * (lower + (upper / 5.)))

    def set_vars(self, bounds_dict):
        """
        Set variable bounds to given bounds.
        """
        for variable, bounds in bounds_dict.items():
            if variable in self.priors.keys():
                param = self.priors[variable]
                param.lower = num.atleast_1d(bounds[0])
                param.upper = num.atleast_1d(bounds[1])
                param.testvalue = num.atleast_1d(num.mean(bounds))
            else:
                logger.warning('Prior for variable %s does not exist!'
                               ' Bounds not updated!' % variable)

    def select_variables(self):
        """
        Return model variables depending on problem config.
        """

        if self.mode not in modes_catalog.keys():
            raise ValueError('Problem mode %s not implemented' % self.mode)

        vars_catalog = modes_catalog[self.mode]

        variables = []
        for datatype in self.datatypes:
            if datatype in vars_catalog.keys():
                if self.mode == 'geometry':
                    if self.source_type in vars_catalog[datatype].keys():
                        source = vars_catalog[datatype][self.source_type]
                        svars = set(source.keys())

                        if isinstance(source(),
                                      (PyrockoRS, gf.ExplosionSource)):
                            svars.discard('magnitude')

                        variables += utility.weed_input_rvs(
                            svars, self.mode, datatype)
                    else:
                        raise ValueError('Source Type not supported for type'
                                         ' of problem, and datatype!')

                    if datatype == 'seismic':
                        if self.stf_type in stf_catalog.keys():
                            stf = stf_catalog[self.stf_type]
                            variables += utility.weed_input_rvs(
                                set(stf.keys()), self.mode, datatype)
                else:
                    variables += vars_catalog[datatype]
            else:
                raise ValueError(
                    'Datatype %s not supported for type of'
                    ' problem! Supported datatype are: %s' %
                    (datatype, ', '.join('"%s"' % d
                                         for d in vars_catalog.keys())))

        unique_variables = utility.unique_list(variables)

        if len(unique_variables) == 0:
            raise Exception('Mode and datatype combination not implemented'
                            ' or not resolvable with given datatypes.')

        return unique_variables

    def get_slip_variables(self):
        """
        Return a list of slip variable names defined in the ProblemConfig.
        """
        if self.mode == 'ffi':
            return [
                var for var in static_dist_vars if var in self.priors.keys()
            ]
        elif self.mode == 'geometry':
            return [
                var for var in ['slip', 'magnitude']
                if var in self.priors.keys()
            ]
        elif self.mode == 'interseismic':
            return ['bl_amplitude']

    def set_decimation_factor(self):
        """
        Determines the reduction of discretization of an extended source.
        Influences yet only the RectangularSource.
        """
        if self.source_type == 'RectangularSource':
            self.decimation_factors = {}
            for datatype in self.datatypes:
                self.decimation_factors[datatype] = \
                    default_decimation_factors[datatype]
        else:
            self.decimation_factors = None

    def validate_priors(self):
        """
        Check if priors and their test values do not contradict!
        """
        for param in self.priors.itervalues():
            param.validate_bounds()

        logger.info('All parameter-priors ok!')

    def validate_hypers(self):
        """
        Check if hyperparameters and their test values do not contradict!
        """
        if self.hyperparameters is not None:
            for hp in self.hyperparameters.itervalues():
                hp.validate_bounds()

            logger.info('All hyper-parameters ok!')

        else:
            logger.info('No hyper-parameters defined!')
Exemple #21
0
class SatelliteTargetDisplacement(PlotConfig):
    ''' Maps showing surface displacements from satellite and modelled data '''

    name = 'satellite'
    dpi = Int.T(
        default=250)
    size_cm = Tuple.T(
        2, Float.T(),
        default=(22., 12.))
    colormap = String.T(
        default='RdBu',
        help='Colormap for the surface displacements')
    relative_coordinates = Bool.T(
        default=False,
        help='Show relative coordinates, initial location centered at 0N, 0E')
    fit = StringChoice.T(
        default='best', choices=['best', 'mean'],
        help='Show the \'best\' or \'mean\' fits and source model from the'
             ' ensamble.')

    show_topo = Bool.T(
        default=True,
        help='Drape displacements over the topography.')
    displacement_unit = StringChoice.T(
        default='m',
        choices=['m', 'mm', 'cm', 'rad'],
        help="Show results in 'm', 'cm', 'mm' or 'rad' for radians.")
    show_leaf_centres = Bool.T(
        default=True,
        help='show the center points of Quadtree leaves')
    source_outline_color = String.T(
        default='grey',
        help='Choose color of source outline from named matplotlib Colors')
    common_color_scale = Bool.T(
        default=True,
        help='Results shown with common color scale for all satellite '
             'data sets (based on the data)')
    map_limits = Tuple.T(
        4, Float.T(),
        optional=True,
        help='Overwrite map limits in native coordinates. '
             'Use (xmin, xmax, ymin, ymax)')
    nticks_x = Int.T(
        optional=True,
        help='Number of ticks on the x-axis.')

    def make(self, environ):
        cm = environ.get_plot_collection_manager()
        history = environ.get_history(subset='harvest')
        optimiser = environ.get_optimiser()
        ds = environ.get_dataset()

        environ.setup_modelling()

        cm.create_group_mpl(
            self,
            self.draw_static_fits(ds, history, optimiser),
            title=u'InSAR Displacements',
            section='fits',
            feather_icon='navigation',
            description=u'''
Maps showing subsampled surface displacements as observed, modelled and the
residual (observed minus modelled).

The displacement values predicted by the orbit-ambiguity ramps are added to the
modelled displacements (middle panels). The color shows the LOS displacement
values associated with, and the extent of, every quadtree box. The light grey
dots show the focal point of pixels combined in the quadtree box. This point
corresponds to the position of the modelled data point.

The large dark grey dot shows the reference source position. The grey filled
box shows the surface projection of the modelled source, with the thick-lined
edge marking the upper fault edge. Complete data extent is shown.
''')

    def draw_static_fits(self, ds, history, optimiser, closeup=False):
        from pyrocko.orthodrome import latlon_to_ne_numpy
        problem = history.problem

        sat_targets = problem.satellite_targets
        for target in sat_targets:
            target.set_dataset(ds)

        if self.fit == 'best':
            source = history.get_best_source()
            model = history.get_best_model()
        elif self.fit == 'mean':
            source = history.get_mean_source()
            model = history.get_mean_model()

        results = problem.evaluate(model, targets=sat_targets)

        def init_axes(ax, scene, title, last_axes=False):
            ax.set_title(title, fontsize=self.font_size)
            ax.tick_params(length=2)

            if scene.frame.isMeter():
                import utm
                ax.set_xlabel('Easting [km]', fontsize=self.font_size)
                scale_x = dict(scale=1./km)
                scale_y = dict(scale=1./km)
                utm_E, utm_N, utm_zone, utm_zone_letter =\
                    utm.from_latlon(source.effective_lat,
                                    source.effective_lon)
                scale_x['offset'] = utm_E
                scale_y['offset'] = utm_N

                if last_axes:
                    ax.text(0.975, 0.025,
                            'UTM Zone %d%s' % (utm_zone, utm_zone_letter),
                            va='bottom', ha='right',
                            fontsize=8, alpha=.7,
                            transform=ax.transAxes)
                ax.set_aspect('equal')

            elif scene.frame.isDegree():
                scale_x = dict(scale=1., suffix='°')
                scale_y = dict(scale=1., suffix='°')
                scale_x['offset'] = source.effective_lon
                scale_y['offset'] = source.effective_lat

                ax.set_aspect(1./num.cos(source.effective_lat*d2r))

            if self.relative_coordinates:
                scale_x['offset'] = 0.
                scale_y['offset'] = 0.

            nticks_x = 4 if abs(scene.frame.llLon) >= 100 else 5

            ax.xaxis.set_major_locator(MaxNLocator(self.nticks_x or nticks_x))
            ax.yaxis.set_major_locator(MaxNLocator(5))

            ax.scale_x = scale_x
            ax.scale_y = scale_y

            scale_axes(ax.get_xaxis(), **scale_x)
            scale_axes(ax.get_yaxis(), **scale_y)

        def draw_source(ax, scene):
            if scene.frame.isMeter():
                fn, fe = source.outline(cs='xy').T
                fn -= fn.mean()
                fe -= fe.mean()
            elif scene.frame.isDegree():
                fn, fe = source.outline(cs='latlon').T
                fn -= source.effective_lat
                fe -= source.effective_lon

            # source is centered
            ax.scatter(0., 0., color='black', s=3, alpha=.5, marker='o')
            ax.fill(fe, fn,
                    edgecolor=(0., 0., 0.),
                    facecolor=self.source_outline_color,
                    alpha=0.7)
            ax.plot(fe[0:2], fn[0:2], 'k', linewidth=1.3)

        def get_displacement_rgba(displacements, scene, mappable):
            arr = num.full_like(scene.displacement, fill_value=num.nan)
            qt = scene.quadtree

            for syn_v, leaf in zip(displacements, qt.leaves):
                arr[leaf._slice_rows, leaf._slice_cols] = syn_v

            arr[scene.displacement_mask] = num.nan

            if not self.common_color_scale \
                    and not self.displacement_unit == 'rad':
                abs_displ = num.abs(displacements).max()
                mappable.set_clim(-abs_displ, abs_displ)

            if self.show_topo:
                try:
                    elevation = scene.get_elevation()
                    return drape_displacements(arr, elevation, mappable)
                except Exception as e:
                    logger.warning('could not plot hillshaded topo')
                    logger.exception(e)

            return mappable.to_rgba(arr)

        def draw_leaves(ax, scene, offset_e=0., offset_n=0.):
            rects = scene.quadtree.getMPLRectangles()
            for r in rects:
                r.set_edgecolor((.4, .4, .4))
                r.set_linewidth(.5)
                r.set_facecolor('none')
                r.set_x(r.get_x() - offset_e)
                r.set_y(r.get_y() - offset_n)
            map(ax.add_artist, rects)

            if self.show_leaf_centres:
                ax.scatter(scene.quadtree.leaf_coordinates[:, 0] - offset_e,
                           scene.quadtree.leaf_coordinates[:, 1] - offset_n,
                           s=.25, c='black', alpha=.1)

        def add_arrow(ax, scene):
            phi = num.nanmean(scene.phi)
            los_dx = num.cos(phi + num.pi) * .0625
            los_dy = num.sin(phi + num.pi) * .0625

            az_dx = num.cos(phi - num.pi/2) * .125
            az_dy = num.sin(phi - num.pi/2) * .125

            anchor_x = .9 if los_dx < 0 else .1
            anchor_y = .85 if los_dx < 0 else .975

            az_arrow = patches.FancyArrow(
                x=anchor_x-az_dx, y=anchor_y-az_dy,
                dx=az_dx, dy=az_dy,
                head_width=.025,
                alpha=.5, fc='k',
                head_starts_at_zero=False,
                length_includes_head=True,
                transform=ax.transAxes)

            los_arrow = patches.FancyArrow(
                x=anchor_x-az_dx/2, y=anchor_y-az_dy/2,
                dx=los_dx, dy=los_dy,
                head_width=.02,
                alpha=.5, fc='k',
                head_starts_at_zero=False,
                length_includes_head=True,
                transform=ax.transAxes)

            ax.add_artist(az_arrow)
            ax.add_artist(los_arrow)

        urE, urN, llE, llN = (0., 0., 0., 0.)
        for target in sat_targets:

            if target.scene.frame.isMeter():
                off_n, off_e = map(float, latlon_to_ne_numpy(
                    target.scene.frame.llLat, target.scene.frame.llLon,
                    source.effective_lat, source.effective_lon))
            if target.scene.frame.isDegree():
                off_n = source.effective_lat - target.scene.frame.llLat
                off_e = source.effective_lon - target.scene.frame.llLon

            turE, turN, tllE, tllN = zip(
                *[(leaf.gridE.max()-off_e,
                   leaf.gridN.max()-off_n,
                   leaf.gridE.min()-off_e,
                   leaf.gridN.min()-off_n)
                  for leaf in target.scene.quadtree.leaves])

            turE, turN = map(max, (turE, turN))
            tllE, tllN = map(min, (tllE, tllN))
            urE, urN = map(max, ((turE, urE), (urN, turN)))
            llE, llN = map(min, ((tllE, llE), (llN, tllN)))

        def generate_plot(sat_target, result, ifig):

            scene = sat_target.scene

            fig = plt.figure()
            fig.set_size_inches(*self.size_inch)
            gs = gridspec.GridSpec(
                2, 3,
                wspace=.15, hspace=.2,
                left=.1, right=.975, top=.95,
                height_ratios=[12, 1])

            item = PlotItem(
                name='fig_%i' % ifig,
                attributes={'targets': [sat_target.path]},
                title=u'Satellite Surface Displacements - %s'
                      % scene.meta.scene_title,
                description=u'''
Surface displacements derived from satellite data.
(Left) the input data, (center) the modelled
data and (right) the model residual.
''')

            stat_obs = result.statics_obs['displacement.los']
            stat_syn = result.statics_syn['displacement.los']
            res = stat_obs - stat_syn

            if scene.frame.isMeter():
                offset_n, offset_e = map(float, latlon_to_ne_numpy(
                    scene.frame.llLat, scene.frame.llLon,
                    source.effective_lat, source.effective_lon))
            elif scene.frame.isDegree():
                offset_n = source.effective_lat - scene.frame.llLat
                offset_e = source.effective_lon - scene.frame.llLon

            im_extent = (
                scene.frame.E.min() - offset_e,
                scene.frame.E.max() - offset_e,
                scene.frame.N.min() - offset_n,
                scene.frame.N.max() - offset_n)

            if self.displacement_unit == 'rad':
                wavelength = scene.meta.wavelength
                if wavelength is None:
                    raise AttributeError(
                        'The satellite\'s wavelength is not set')

                stat_obs = displ2rad(stat_obs, wavelength)
                stat_syn = displ2rad(stat_syn, wavelength)
                res = displ2rad(res, wavelength)

                self.colormap = 'hsv'
                data_range = (0., num.pi)

            else:
                abs_displ = num.abs([stat_obs.min(), stat_obs.max(),
                                     stat_syn.min(), stat_syn.max(),
                                     res.min(), res.max()]).max()
                data_range = (-abs_displ, abs_displ)

            cmw = cm.ScalarMappable(cmap=self.colormap)
            cmw.set_clim(*data_range)
            cmw.set_array(stat_obs)

            axes = [fig.add_subplot(gs[0, 0]),
                    fig.add_subplot(gs[0, 1]),
                    fig.add_subplot(gs[0, 2])]

            ax = axes[0]
            ax.imshow(
                get_displacement_rgba(stat_obs, scene, cmw),
                extent=im_extent, origin='lower')
            draw_leaves(ax, scene, offset_e, offset_n)
            draw_source(ax, scene)
            add_arrow(ax, scene)
            init_axes(ax, scene, 'Observed')

            ax.text(.025, .025, 'Scene ID: %s' % scene.meta.scene_id,
                    fontsize=8, alpha=.7,
                    va='bottom', transform=ax.transAxes)
            if scene.frame.isMeter():
                ax.set_ylabel('Northing [km]', fontsize=self.font_size)

            ax = axes[1]
            ax.imshow(
                get_displacement_rgba(stat_syn, scene, cmw),
                extent=im_extent, origin='lower')
            draw_leaves(ax, scene, offset_e, offset_n)
            draw_source(ax, scene)
            add_arrow(ax, scene)
            init_axes(ax, scene, 'Model')
            ax.get_yaxis().set_visible(False)

            ax = axes[2]
            ax.imshow(
                get_displacement_rgba(res, scene, cmw),
                extent=im_extent, origin='lower')

            draw_leaves(ax, scene, offset_e, offset_n)
            draw_source(ax, scene)
            add_arrow(ax, scene)
            init_axes(ax, scene, 'Residual', last_axes=True)
            ax.get_yaxis().set_visible(False)

            for ax in axes:
                ax.set_xlim(*im_extent[:2])
                ax.set_ylim(*im_extent[2:])

            if closeup:
                if scene.frame.isMeter():
                    fn, fe = source.outline(cs='xy').T
                elif scene.frame.isDegree():
                    fn, fe = source.outline(cs='latlon').T
                    fn -= source.effective_lat
                    fe -= source.effective_lon

                if fn.size > 1:
                    off_n = (fn[0] + fn[1]) / 2
                    off_e = (fe[0] + fe[1]) / 2
                else:
                    off_n = fn[0]
                    off_e = fe[0]

                fault_size = 2*num.sqrt(max(abs(fn-off_n))**2
                                        + max(abs(fe-off_e))**2)
                fault_size *= self.map_scale
                if fault_size == 0.0:
                    extent = (scene.frame.N[-1] + scene.frame.E[-1]) / 2
                    fault_size = extent * .25

                for ax in axes:
                    ax.set_xlim(-fault_size/2 + off_e, fault_size/2 + off_e)
                    ax.set_ylim(-fault_size/2 + off_n, fault_size/2 + off_n)

            if self.map_limits is not None:
                xmin, xmax, ymin, ymax = self.map_limits
                assert xmin < xmax, 'bad map_limits xmin > xmax'
                assert ymin < ymax, 'bad map_limits ymin > ymax'

                for ax in axes:
                    ax.set_xlim(
                        xmin/ax.scale_x['scale'] - ax.scale_x['offset'],
                        xmax/ax.scale_x['scale'] - ax.scale_x['offset'],)
                    ax.set_ylim(
                        ymin/ax.scale_y['scale'] - ax.scale_y['offset'],
                        ymax/ax.scale_y['scale'] - ax.scale_y['offset'])

            if self.displacement_unit == 'm':
                def cfmt(x, p):
                    return '%.2f' % x
            elif self.displacement_unit == 'cm':
                def cfmt(x, p):
                    return '%.1f' % (x * 1e2)
            elif self.displacement_unit == 'mm':
                def cfmt(x, p):
                    return '%.0f' % (x * 1e3)
            elif self.displacement_unit == 'rad':
                def cfmt(x, p):
                    return '%.2f' % x
            else:
                raise AttributeError(
                    'unknown displacement unit %s' % self.displacement_unit)

            cbar_args = dict(
                orientation='horizontal',
                format=FuncFormatter(cfmt),
                use_gridspec=True)
            cbar_label = 'LOS Displacement [%s]' % self.displacement_unit

            if self.common_color_scale:
                cax = fig.add_subplot(gs[1, 1])
                cax.set_aspect(.05)
                cbar = fig.colorbar(cmw, cax=cax, **cbar_args)
                cbar.set_label(cbar_label)
            else:
                for idata, data in enumerate((stat_syn, stat_obs, res)):
                    cax = fig.add_subplot(gs[1, idata])
                    cax.set_aspect(.05)

                    if not self.displacement_unit == 'rad':
                        abs_displ = num.abs(data).max()
                        cmw.set_clim(-abs_displ, abs_displ)

                    cbar = fig.colorbar(cmw, cax=cax, **cbar_args)
                    cbar.set_label(cbar_label)

            return (item, fig)

        for ifig, (sat_target, result) in enumerate(zip(sat_targets, results)):
            yield generate_plot(sat_target, result, ifig)
Exemple #22
0
class TemplateMatchingIFC(IFC):

    template_event_path = common.Path.T(
        help='Event parameters of the template')

    template_markers_path = common.Path.T(
        optional=False,
        help='File with markers defining the template')

    sum_square = Bool.T(
        default=False,
        help='Sum square of correlation')

    normalization = StringChoice.T(
        default='gliding',
        choices=['off', 'normal', 'gliding'])

    downsample_rate = Float.T(
        optional=True,
        help='If set, downsample to this sampling rate before processing [Hz]')

    use_fft = Bool.T(
        optional=True,
        default=False,
        help='If set, correlate traces in the spectral domain')

    def get_tpad(self):
        tmin_masters = min(tr.tmin for tr in self.masters.values())
        tmax_masters = max(tr.tmax for tr in self.masters.values())
        tmaster = tmax_masters - tmin_masters
        return tmaster

    def get_template_origin(self):

        event = model.load_one_event(self.expand_path(
            self.template_event_path))

        origin = geo.Point(
            lat=event.lat,
            lon=event.lon,
            z=event.depth)

        return origin

    def get_table(self, grid, receivers):
        origin = self.get_template_origin()
        return self.shifter.get_offset_table(grid, receivers, origin)

    def extract_template(self, p):

        markers = gui_util.load_markers(self.expand_path(
            self.template_markers_path))

        def trace_selector_global(tr):
            return True

        period_highpass = 1./self.fmin
        tpad = 2 * period_highpass

        master_traces = []
        for marker in markers:
            if marker.tmin == marker.tmax:
                logger.warn('tmin == tmax in template marker %s' % marker)

            if not marker.nslc_ids:
                trace_selector = trace_selector_global
            else:
                def trace_selector(tr):
                    return (
                        marker.match_nslc(tr.nslc_id) and
                        trace_selector_global(tr))

            master_traces.extend(p.all(
                tmin=marker.tmin,
                tmax=marker.tmax,
                trace_selector=trace_selector,
                tpad=tpad))

        masters = {}
        for xtr in master_traces:
            tr = xtr.copy()
            if self.downsample_rate is not None:
                downsample(tr, 1./self.downsample_rate)

            tr.highpass(4, self.fmin, demean=False)
            tr.lowpass(4, self.fmax, demean=False)
            smin = round(xtr.wmin / tr.deltat) * tr.deltat
            smax = round(xtr.wmax / tr.deltat) * tr.deltat
            tr.chop(smin, smax)
            if tr.nslc_id in masters:
                raise common.LassieError(
                    'more than one waveform selected on trace with id "%s"'
                    % '.'.join(tr.nslc_id))

            masters[tr.nslc_id] = tr

        return masters

    def prescan(self, p):
        self.masters = self.extract_template(p)

    def deltat_cf_is_available(self, deltat_cf):
        return False

    def preprocess(self, trs, wmin, wmax, tpad_new, deltat_cf):

        tmin_masters = min(tr.tmin for tr in self.masters.values())
        tmax_masters = max(tr.tmax for tr in self.masters.values())
        tmaster = tmax_masters - tmin_masters
        tref = tmin_masters

        nsl_to_traces = defaultdict(list)
        for orig_b in trs:

            b = orig_b.copy()

            nslc = b.nslc_id
            a = self.masters.get(nslc, False)
            if not a:
                continue

            if self.downsample_rate is not None:
                downsample(b, 1./self.downsample_rate)

            b.highpass(4, self.fmin, demean=False)
            b.lowpass(4, self.fmax, demean=False)
            smin = round((wmin - tmaster) / b.deltat) * b.deltat
            smax = round((wmax + tmaster) / b.deltat) * b.deltat
            b.chop(smin, smax)

            normalization = self.normalization
            if normalization == 'off':
                normalization = None

            c = trace.correlate(
                a, b, mode='valid', normalization=normalization,
                use_fft=self.use_fft)

            c.shift(-c.tmin + b.tmin - (a.tmin - tref))
            c.meta = {'tabu': True}
            if self.sum_square:
                c.ydata = c.ydata**2

            c.chop(wmin - tpad_new, wmax + tpad_new)

            nsl_to_traces[nslc[:3]].append(c)

        dataset = []
        for nsl, cs in nsl_to_traces.items():
            csum = cs[0]
            for c in cs[1:]:
                csum.add(c)

            dataset.append((nsl, csum))

        return dataset
Exemple #23
0
class GNSSStation(Location):
    ''' Representation of a GNSS station during a campaign measurement

    For more information see
    http://kb.unavco.org/kb/assets/660/UNAVCO_Campaign_GPS_GNSS_Handbook.pdf
    '''

    code = String.T(
        help='Four letter station code',
        optional=True)

    style = StringChoice.T(
        choices=['static', 'rapid_static', 'kinematic'],
        default='static')

    survey_start = DateTimestamp.T(
        optional=True,
        help='Survey start time')

    survey_end = DateTimestamp.T(
        optional=True,
        help='Survey end time')

    correlation_ne = Float.T(
        optional=True,
        help='North-East component correlation')

    correlation_eu = Float.T(
        optional=True,
        help='East-Up component correlation')

    correlation_nu = Float.T(
        optional=True,
        help='North-Up component correlation')

    north = GNSSComponent.T(
        default=GNSSComponent.D())

    east = GNSSComponent.T(
        default=GNSSComponent.D())

    up = GNSSComponent.T(
        default=GNSSComponent.D())

    def __init__(self, *args, **kwargs):
        Location.__init__(self, *args, **kwargs)

    def get_covariance_matrix(self, full=True):
        s = self

        covar = num.zeros((3, 3))
        covar[num.diag_indices_from(covar)] = num.array(
            [c.sigma**2 for c in (s.north, s.east, s.up)])

        if s.correlation_ne is not None:
            covar[0, 1] = s.correlation_ne * s.north.sigma * s.east.sigma
        if s.correlation_nu is not None:
            covar[0, 2] = s.correlation_nu * s.north.sigma * s.up.sigma
        if s.correlation_eu is not None:
            covar[1, 2] = s.correlation_eu * s.east.sigma * s.up.sigma

        if full:
            covar[num.tril_indices_from(covar, k=-1)] = \
                covar[num.triu_indices_from(covar, k=1)]

        return covar

    def get_correlation_matrix(self, full=True):
        s = self

        corr = num.zeros((3, 3))
        corr[num.diag_indices_from(corr)] = num.array(
            [c.sigma for c in (s.north, s.east, s.up)])

        if s.correlation_ne is not None:
            corr[0, 1] = s.correlation_ne
        if s.correlation_nu is not None:
            corr[0, 2] = s.correlation_nu
        if s.correlation_eu is not None:
            corr[1, 2] = s.correlation_eu

        if full:
            corr[num.tril_indices_from(corr, k=-1)] = \
                corr[num.triu_indices_from(corr, k=1)]

        return corr
Exemple #24
0
class SequencePlot(PlotConfig):
    '''
    Draws all parameter values evaluated during the optimisation

    The sequence of all the parameter values is either a function of the
    optimisation in progress or of the misfit from high to low. This plot can
    be used to check on convergence or see if model parameters push the given
    bounds. The color always shows the relative misfit. Relatively high misfits
    are in cold blue colors and relatively low misfits in red. The last panel
    gives the corresponding misfit values.
    '''

    name = 'sequence'
    size_cm = Tuple.T(2, Float.T(), default=(14., 6.))
    misfit_cutoff = Float.T(optional=True)
    ibootstrap = Int.T(optional=True)
    sort_by = StringChoice.T(choices=['iteration', 'misfit'],
                             default='iteration')
    subplot_layout = Tuple.T(2, Int.T(), default=(1, 1))
    marker_size = Float.T(default=1.5)
    show_reference = Bool.T(default=True)

    def make(self, environ):
        cm = environ.get_plot_collection_manager()
        history = environ.get_history()
        optimiser = environ.get_optimiser()

        mpl_init(fontsize=self.font_size)
        cm.create_group_mpl(self,
                            self.draw_figures(history, optimiser),
                            title=u'Sequence Plots',
                            section='optimiser',
                            description=u'''
Sequence plots for all parameters of the optimisation.

The sequence of all the parameter values is either a function of the
optimisation in progress or of the misfit from high to low. This plot can be
used to check on convergence or to see if model parameters push the given
bounds.

The color always shows the relative misfit. Relatively high misfits are in
cold blue colors and relatively low misfits in red. The last panel gives the
corresponding misfit values.
''',
                            feather_icon='fast-forward')

    def draw_figures(self, history, optimiser):
        misfit_cutoff = self.misfit_cutoff
        sort_by = self.sort_by

        problem = history.problem
        models = history.models

        npar = problem.nparameters
        ndep = problem.ndependants
        fontsize = self.font_size
        nfx, nfy = self.subplot_layout

        imodels = num.arange(history.nmodels)
        bounds = problem.get_combined_bounds()

        xref = problem.get_reference_model()

        gms = history.get_primary_chain_misfits()
        gms_softclip = num.where(gms > 1.0, 0.2 * num.log10(gms) + 1.0, gms)

        isort = num.argsort(gms)[::-1]

        if sort_by == 'iteration':
            imodels = imodels[isort]
        elif sort_by == 'misfit':
            imodels = num.arange(imodels.size)
        else:
            assert False

        gms = gms[isort]
        gms_softclip = gms_softclip[isort]
        models = models[isort, :]

        iorder = num.empty_like(isort)
        iorder = num.arange(iorder.size)

        if misfit_cutoff is None:
            ibest = num.ones(gms.size, dtype=num.bool)
        else:
            ibest = gms < misfit_cutoff

        def config_axes(axes, nfx, nfy, impl, iplot, nplots):
            if (impl - 1) % nfx != nfx - 1:
                axes.get_yaxis().tick_left()

            if (impl - 1) >= (nfx * (nfy - 1)) or iplot >= nplots - nfx:
                axes.set_xlabel('Iteration')
                if not (impl - 1) // nfx == 0:
                    axes.get_xaxis().tick_bottom()
            elif (impl - 1) // nfx == 0:
                axes.get_xaxis().tick_top()
                axes.set_xticklabels([])
            else:
                axes.get_xaxis().set_visible(False)

        # nfz = (npar + ndep + 1 - 1) / (nfx*nfy) + 1
        cmap = cm.YlOrRd
        cmap = cm.jet
        msize = self.marker_size
        axes = None
        fig = None
        item_fig = None
        nfigs = 0
        alpha = 0.5
        for ipar in range(npar):
            impl = ipar % (nfx * nfy) + 1

            if impl == 1:
                if item_fig:
                    yield item_fig
                    nfigs += 1

                fig = plt.figure(figsize=self.size_inch)
                labelpos = mpl_margins(fig,
                                       nw=nfx,
                                       nh=nfy,
                                       left=7.,
                                       right=2.,
                                       top=1.,
                                       bottom=5.,
                                       wspace=7.,
                                       hspace=2.,
                                       units=fontsize)

                item = PlotItem(name='fig_%i' % (nfigs + 1))
                item.attributes['parameters'] = []
                item_fig = (item, fig)

            par = problem.parameters[ipar]

            item_fig[0].attributes['parameters'].append(par.name)

            axes = fig.add_subplot(nfy, nfx, impl)
            labelpos(axes, 2.5, 2.0)

            axes.set_ylabel(par.get_label())
            axes.get_yaxis().set_major_locator(plt.MaxNLocator(4))

            config_axes(axes, nfx, nfy, impl, ipar, npar + ndep + 1)

            axes.set_ylim(*fixlim(*par.scaled(bounds[ipar])))
            axes.set_xlim(0, history.nmodels)

            axes.scatter(imodels[ibest],
                         par.scaled(models[ibest, ipar]),
                         s=msize,
                         c=iorder[ibest],
                         edgecolors='none',
                         cmap=cmap,
                         alpha=alpha,
                         rasterized=True)

            if self.show_reference:
                axes.axhline(par.scaled(xref[ipar]), color='black', alpha=0.3)

        for idep in range(ndep):
            # ifz, ify, ifx = num.unravel_index(ipar, (nfz, nfy, nfx))
            impl = (npar + idep) % (nfx * nfy) + 1

            if impl == 1:
                if item_fig:
                    yield item_fig
                    nfigs += 1

                fig = plt.figure(figsize=self.size_inch)
                labelpos = mpl_margins(fig,
                                       nw=nfx,
                                       nh=nfy,
                                       left=7.,
                                       right=2.,
                                       top=1.,
                                       bottom=5.,
                                       wspace=7.,
                                       hspace=2.,
                                       units=fontsize)

                item = PlotItem(name='fig_%i' % (nfigs + 1))
                item.attributes['parameters'] = []

                item_fig = (item, fig)

            par = problem.dependants[idep]
            item_fig[0].attributes['parameters'].append(par.name)

            axes = fig.add_subplot(nfy, nfx, impl)
            labelpos(axes, 2.5, 2.0)

            axes.set_ylabel(par.get_label())
            axes.get_yaxis().set_major_locator(plt.MaxNLocator(4))

            config_axes(axes, nfx, nfy, impl, npar + idep, npar + ndep + 1)

            axes.set_ylim(*fixlim(*par.scaled(bounds[npar + idep])))
            axes.set_xlim(0, history.nmodels)

            ys = problem.make_dependant(models[ibest, :], par.name)
            axes.scatter(imodels[ibest],
                         par.scaled(ys),
                         s=msize,
                         c=iorder[ibest],
                         edgecolors='none',
                         cmap=cmap,
                         alpha=alpha,
                         rasterized=True)

            if self.show_reference:
                y = problem.make_dependant(xref, par.name)
                axes.axhline(par.scaled(y), color='black', alpha=0.3)

        impl = (npar + ndep) % (nfx * nfy) + 1
        if impl == 1:
            if item_fig:
                yield item_fig
                nfigs += 1

            fig = plt.figure(figsize=self.size_inch)
            labelpos = mpl_margins(fig,
                                   nw=nfx,
                                   nh=nfy,
                                   left=7.,
                                   right=2.,
                                   top=1.,
                                   bottom=5.,
                                   wspace=7.,
                                   hspace=2.,
                                   units=fontsize)

            item = PlotItem(name='fig_%i' % (nfigs + 1))
            item.attributes['parameters'] = []

            item_fig = (item, fig)

        axes = fig.add_subplot(nfy, nfx, impl)
        labelpos(axes, 2.5, 2.0)

        config_axes(axes, nfx, nfy, impl, npar + ndep, npar + ndep + 1)

        axes.set_ylim(0., 1.5)
        axes.set_yticks([0., 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4])
        axes.set_yticklabels(
            ['0.0', '0.2', '0.4', '0.6', '0.8', '1', '10', '100'])

        axes.scatter(imodels[ibest],
                     gms_softclip[ibest],
                     c=iorder[ibest],
                     s=msize,
                     edgecolors='none',
                     cmap=cmap,
                     alpha=alpha)

        axes.axhspan(1.0, 1.5, color=(0.8, 0.8, 0.8), alpha=0.2)
        axes.axhline(1.0, color=(0.5, 0.5, 0.5), zorder=2)

        axes.set_xlim(0, history.nmodels)
        axes.set_xlabel('Iteration')

        axes.set_ylabel('Misfit')

        yield item_fig
        nfigs += 1
Exemple #25
0
class WaveformGenerator(TargetGenerator):

    station_generator = StationGenerator.T(
        default=RandomStationGenerator.D(),
        help='The StationGenerator for creating the stations.')

    noise_generator = WaveformNoiseGenerator.T(
        default=WhiteNoiseGenerator.D(),
        help='Add Synthetic noise on the waveforms.')

    store_id = gf.StringID.T(
        default=DEFAULT_STORE_ID,
        help='The GF store to use for forward-calculations.')

    seismogram_quantity = StringChoice.T(
        choices=['displacement', 'velocity', 'acceleration', 'counts'],
        default='displacement')

    vmin_cut = Float.T(
        default=2000.,
        help='Minimum velocity to seismic velicty to consider in the model.')
    vmax_cut = Float.T(
        default=8000.,
        help='Maximum velocity to seismic velicty to consider in the model.')

    fmin = Float.T(default=0.01,
                   help='Minimum frequency/wavelength to resolve in the'
                   ' synthetic waveforms.')

    def get_stations(self):
        return self.station_generator.get_stations()

    def get_targets(self):
        targets = []
        for station in self.get_stations():
            channel_data = []
            channels = station.get_channels()
            if channels:
                for channel in channels:
                    channel_data.append(
                        [channel.name, channel.azimuth, channel.dip])

            else:
                for c_name in ['BHZ', 'BHE', 'BHN']:
                    channel_data.append([
                        c_name,
                        model.guess_azimuth_from_name(c_name),
                        model.guess_dip_from_name(c_name)
                    ])

            for c_name, c_azi, c_dip in channel_data:

                target = gf.Target(codes=(station.network, station.station,
                                          station.location, c_name),
                                   quantity='displacement',
                                   lat=station.lat,
                                   lon=station.lon,
                                   depth=station.depth,
                                   store_id=self.store_id,
                                   optimization='enable',
                                   interpolation='nearest_neighbor',
                                   azimuth=c_azi,
                                   dip=c_dip)

                targets.append(target)

        return targets

    def get_time_range(self, sources):
        dmin, dmax = self.station_generator.get_distance_range(sources)

        times = num.array([source.time for source in sources], dtype=num.float)

        tmin_events = num.min(times)
        tmax_events = num.max(times)

        tmin = tmin_events + dmin / self.vmax_cut - 10.0 / self.fmin
        tmax = tmax_events + dmax / self.vmin_cut + 10.0 / self.fmin

        return tmin, tmax

    def get_codes_to_deltat(self, engine, sources):
        deltats = {}
        for source in sources:
            for target in self.get_targets():
                deltats[target.codes] = engine.get_store(
                    target.store_id).config.deltat

        return deltats

    def get_useful_time_increment(self, engine, sources):
        _, dmax = self.station_generator.get_distance_range(sources)
        tinc = dmax / self.vmin_cut + 2.0 / self.fmin

        deltats = set(self.get_codes_to_deltat(engine, sources).values())

        deltat = reduce(util.lcm, deltats)
        tinc = int(round(tinc / deltat)) * deltat
        return tinc

    def get_waveforms(self, engine, sources, tmin=None, tmax=None):
        trs = {}

        tmin_all, tmax_all = self.get_time_range(sources)
        tmin = tmin if tmin is not None else tmin_all
        tmax = tmax if tmax is not None else tmax_all
        tts = util.time_to_str

        for nslc, deltat in self.get_codes_to_deltat(engine, sources).items():
            tr_tmin = int(round(tmin / deltat)) * deltat
            tr_tmax = (int(round(tmax / deltat)) - 1) * deltat
            nsamples = int(round((tr_tmax - tr_tmin) / deltat)) + 1

            tr = trace.Trace(*nslc,
                             tmin=tr_tmin,
                             ydata=num.zeros(nsamples),
                             deltat=deltat)

            self.noise_generator.add_noise(tr)

            trs[nslc] = tr

        logger.debug('Calculating waveforms between %s - %s...' %
                     (tts(tmin, format='%Y-%m-%d_%H-%M-%S'),
                      tts(tmax, format='%Y-%m-%d_%H-%M-%S')))

        for source in sources:
            targets = self.get_targets()
            resp = engine.process(source, targets)

            for _, target, tr in resp.iter_results():
                resp = self.get_transfer_function(target.codes)
                if resp:
                    tr = tr.transfer(transfer_function=resp)

                trs[target.codes].add(tr)

        return list(trs.values())

    def get_transfer_function(self, codes):
        if self.seismogram_quantity == 'displacement':
            return None
        elif self.seismogram_quantity == 'velocity':
            return trace.DifferentiationResponse(1)
        elif self.seismogram_quantity == 'acceleration':
            return trace.DifferentiationResponse(2)
        elif self.seismogram_quantity == 'counts':
            raise NotImplemented()

    def dump_data(self,
                  engine,
                  sources,
                  path,
                  tmin=None,
                  tmax=None,
                  overwrite=False):
        fns = []
        fns.extend(
            self.dump_waveforms(engine, sources, path, tmin, tmax, overwrite))
        fns.extend(self.dump_responses(path))
        return fns

    def dump_waveforms(self,
                       engine,
                       sources,
                       path,
                       tmin=None,
                       tmax=None,
                       overwrite=False):
        path_waveforms = op.join(path, 'waveforms')
        util.ensuredir(path_waveforms)

        path_traces = op.join(
            path_waveforms, '%(wmin_year)s', '%(wmin_month)s', '%(wmin_day)s',
            'waveform_%(network)s_%(station)s_' +
            '%(location)s_%(channel)s_%(tmin)s_%(tmax)s.mseed')

        tmin_all, tmax_all = self.get_time_range(sources)
        tmin = tmin if tmin is not None else tmin_all
        tmax = tmax if tmax is not None else tmax_all
        tts = util.time_to_str

        tinc = self.get_useful_time_increment(engine, sources)
        tmin = math.floor(tmin / tinc) * tinc
        tmax = math.ceil(tmax / tinc) * tinc

        nwin = int(round((tmax - tmin) / tinc))

        for iwin in range(nwin):
            tmin_win = max(tmin, tmin + iwin * tinc)
            tmax_win = min(tmax, tmin + (iwin + 1) * tinc)

            if tmax_win <= tmin_win:
                continue

            trs = self.get_waveforms(engine, sources, tmin_win, tmax_win)

            try:
                io.save(trs,
                        path_traces,
                        additional=dict(wmin_year=tts(tmin_win, format='%Y'),
                                        wmin_month=tts(tmin_win, format='%m'),
                                        wmin_day=tts(tmin_win, format='%d'),
                                        wmin=tts(tmin_win,
                                                 format='%Y-%m-%d_%H-%M-%S'),
                                        wmax_year=tts(tmax_win, format='%Y'),
                                        wmax_month=tts(tmax_win, format='%m'),
                                        wmax_day=tts(tmax_win, format='%d'),
                                        wmax=tts(tmax_win,
                                                 format='%Y-%m-%d_%H-%M-%S')),
                        overwrite=overwrite)
            except FileSaveError as e:
                logger.debug('Waveform exists %s' % e)

        return [path_waveforms]

    def dump_responses(self, path):
        from pyrocko.io import stationxml

        logger.debug('Writing out StationXML...')

        path_responses = op.join(path, 'meta')
        util.ensuredir(path_responses)
        fn_stationxml = op.join(path_responses, 'stations.xml')

        stations = self.station_generator.get_stations()
        sxml = stationxml.FDSNStationXML.from_pyrocko_stations(stations)

        sunit = {
            'displacement': 'M',
            'velocity': 'M/S',
            'acceleration': 'M/S**2',
            'counts': 'COUNTS'
        }[self.seismogram_quantity]

        response = stationxml.Response(
            instrument_sensitivity=stationxml.Sensitivity(
                value=1.,
                frequency=1.,
                input_units=stationxml.Units(sunit),
                output_units=stationxml.Units('COUNTS')),
            stage_list=[])

        for net, station, channel in sxml.iter_network_station_channels():
            channel.response = response

        sxml.dump_xml(filename=fn_stationxml)

        return [path_responses]

    def add_map_artists(self, engine, sources, automap):
        automap.add_stations(self.get_stations())
Exemple #26
0
class MTLocationPlot(SectionPlotConfig):
    ''' MT location plot of the best solutions in three cross-sections. '''
    name = 'location_mt'
    beachball_type = StringChoice.T(
        choices=['full', 'deviatoric', 'dc'],
        default='dc')
    normalisation_gamma = Float.T(
        default=3.,
        help='Normalisation of colors and alpha as :math:`x^\\gamma`.'
             'A linear colormap/alpha with :math:`\\gamma=1`.')

    def make(self, environ):
        environ.setup_modelling()
        cm = environ.get_plot_collection_manager()
        history = environ.get_history(subset='harvest')
        mpl_init(fontsize=self.font_size)
        self._to_be_closed = []
        cm.create_group_mpl(
            self,
            self.draw_figures(history),
            title=u'MT Location',
            section='solution',
            feather_icon='target',
            description=u'''
Location plot of the ensemble of best solutions in three cross-sections.

The coordinate range is defined by the search space given in the config file.
Symbols show best double-couple mechanisms, and colors indicate low (red) and
high (blue) misfit.
''')
        for obj in self._to_be_closed:
            obj.close()

    def draw_figures(self, history, color_p_axis=False):
        from matplotlib import colors

        color = 'black'
        fontsize = self.font_size
        markersize = fontsize * 1.5
        beachballsize_small = markersize * 0.5
        beachball_type = self.beachball_type

        problem = history.problem
        sp = SectionPlot(config=self)
        self._to_be_closed.append(sp)

        fig = sp.fig
        axes_en = sp.axes_xy
        axes_dn = sp.axes_zy
        axes_ed = sp.axes_xz

        bounds = problem.get_combined_bounds()

        models = history.get_sorted_primary_models()[::-1]

        iorder = num.arange(history.nmodels)

        for parname, set_label, set_lim in [
                ['east_shift', sp.set_xlabel, sp.set_xlim],
                ['north_shift', sp.set_ylabel, sp.set_ylim],
                ['depth', sp.set_zlabel, sp.set_zlim]]:

            ipar = problem.name_to_index(parname)
            par = problem.combined[ipar]
            set_label(par.get_label())
            xmin, xmax = fixlim(*par.scaled(bounds[ipar]))
            set_lim(xmin, xmax)

        if 'volume_change' in problem.parameter_names:
            volumes = models[:, problem.name_to_index('volume_change')]
            volume_max = volumes.max()
            volume_min = volumes.min()

        def scale_size(source):
            if not hasattr(source, 'volume_change'):
                return beachballsize_small

            volume_change = source.volume_change
            fac = (volume_change - volume_min) / (volume_max - volume_min)
            return markersize * .25 + markersize * .5 * fac

        for axes, xparname, yparname in [
                (axes_en, 'east_shift', 'north_shift'),
                (axes_dn, 'depth', 'north_shift'),
                (axes_ed, 'east_shift', 'depth')]:

            ixpar = problem.name_to_index(xparname)
            iypar = problem.name_to_index(yparname)

            xpar = problem.combined[ixpar]
            ypar = problem.combined[iypar]

            xmin, xmax = fixlim(*xpar.scaled(bounds[ixpar]))
            ymin, ymax = fixlim(*ypar.scaled(bounds[iypar]))

            try:
                axes.set_facecolor(mpl_color('aluminium1'))
            except AttributeError:
                axes.patch.set_facecolor(mpl_color('aluminium1'))

            rect = patches.Rectangle(
                (xmin, ymin), xmax-xmin, ymax-ymin,
                facecolor=mpl_color('white'),
                edgecolor=mpl_color('aluminium2'))

            axes.add_patch(rect)

            # fxs = xpar.scaled(problem.extract(models, ixpar))
            # fys = ypar.scaled(problem.extract(models, iypar))

            # axes.set_xlim(*fixlim(num.min(fxs), num.max(fxs)))
            # axes.set_ylim(*fixlim(num.min(fys), num.max(fys)))

            cmap = cm.ScalarMappable(
                norm=colors.PowerNorm(
                    gamma=self.normalisation_gamma,
                    vmin=iorder.min(),
                    vmax=iorder.max()),

                cmap=plt.get_cmap('coolwarm'))

            for ix, x in enumerate(models):

                source = problem.get_source(x)
                mt = source.pyrocko_moment_tensor(
                    store=problem.get_gf_store(problem.targets[0]),
                    target=problem.targets[0])
                fx = problem.extract(x, ixpar)
                fy = problem.extract(x, iypar)
                sx, sy = xpar.scaled(fx), ypar.scaled(fy)

                # TODO: Add rotation in cross-sections
                color = cmap.to_rgba(iorder[ix])

                alpha = (iorder[ix] - iorder.min()) / \
                    float(iorder.max() - iorder.min())
                alpha = alpha**self.normalisation_gamma

                try:
                    beachball.plot_beachball_mpl(
                        mt, axes,
                        beachball_type=beachball_type,
                        position=(sx, sy),
                        size=scale_size(source),
                        color_t=color,
                        color_p=color if color_p_axis else 'white',
                        alpha=alpha,
                        zorder=1,
                        linewidth=0.25)

                except beachball.BeachballError as e:
                    logger.warn(str(e))

        item = PlotItem(name='main')
        return [[item, fig]]
Exemple #27
0
class MTLocationPlot(PlotConfig):
    ''' Map of moment tensor location results '''
    name = 'location_mt'
    size_cm = Tuple.T(2, Float.T(), default=(17.5, 17.5 * (3. / 4.)))
    beachball_type = StringChoice.T(choices=['full', 'deviatoric', 'dc'],
                                    default='dc')

    def make(self, environ):
        cm = environ.get_plot_collection_manager()
        history = environ.get_history(subset='harvest')
        mpl_init(fontsize=self.font_size)
        cm.create_group_mpl(
            self,
            self.draw_figures(history),
            title=u'Moment Tensor Location',
            section='solution',
            feather_icon='target',
            description=u'Location plots of the best ensemble of solutions.')

    def draw_figures(self, history):
        from matplotlib import colors

        color = 'black'
        fontsize = self.font_size
        markersize = fontsize * 1.5
        beachballsize_small = markersize * 0.5
        beachball_type = self.beachball_type

        problem = history.problem

        fig = plt.figure(figsize=self.size_inch)
        axes_en = fig.add_subplot(2, 2, 1)
        axes_dn = fig.add_subplot(2, 2, 2)
        axes_ed = fig.add_subplot(2, 2, 3)

        bounds = problem.get_combined_bounds()

        gms = problem.combine_misfits(history.misfits)

        isort = num.argsort(gms)[::-1]

        gms = gms[isort]
        models = history.models[isort, :]

        iorder = num.arange(history.nmodels)

        for axes, xparname, yparname in [(axes_en, 'east_shift',
                                          'north_shift'),
                                         (axes_dn, 'depth', 'north_shift'),
                                         (axes_ed, 'east_shift', 'depth')]:

            ixpar = problem.name_to_index(xparname)
            iypar = problem.name_to_index(yparname)

            xpar = problem.combined[ixpar]
            ypar = problem.combined[iypar]

            axes.set_xlabel(xpar.get_label())
            axes.set_ylabel(ypar.get_label())

            xmin, xmax = fixlim(*xpar.scaled(bounds[ixpar]))
            ymin, ymax = fixlim(*ypar.scaled(bounds[iypar]))

            axes.set_aspect(1.0)
            axes.set_xlim(xmin, xmax)
            axes.set_ylim(ymin, ymax)

            # fxs = xpar.scaled(problem.extract(models, ixpar))
            # fys = ypar.scaled(problem.extract(models, iypar))

            # axes.set_xlim(*fixlim(num.min(fxs), num.max(fxs)))
            # axes.set_ylim(*fixlim(num.min(fys), num.max(fys)))

            cmap = cm.ScalarMappable(norm=colors.Normalize(
                vmin=num.min(iorder), vmax=num.max(iorder)),
                                     cmap=plt.get_cmap('coolwarm'))

            for ix, x in enumerate(models):
                source = problem.get_source(x)
                mt = source.pyrocko_moment_tensor()
                fx = problem.extract(x, ixpar)
                fy = problem.extract(x, iypar)
                sx, sy = xpar.scaled(fx), ypar.scaled(fy)

                color = cmap.to_rgba(iorder[ix])

                alpha = (iorder[ix] - num.min(iorder)) / \
                    float(num.max(iorder) - num.min(iorder))

                try:
                    beachball.plot_beachball_mpl(mt,
                                                 axes,
                                                 beachball_type=beachball_type,
                                                 position=(sx, sy),
                                                 size=beachballsize_small,
                                                 color_t=color,
                                                 alpha=alpha,
                                                 zorder=1,
                                                 linewidth=0.25)

                except beachball.BeachballError as e:
                    logger.warn(str(e))

        item = PlotItem(name='main', title='Moment Tensor Location')
        return [[item, fig]]
Exemple #28
0
class HudsonPlot(PlotConfig):

    '''
    Illustration of the solution distribution of decomposed moment tensor.
    '''

    name = 'hudson'
    size_cm = Tuple.T(2, Float.T(), default=(17.5, 17.5*(3./4.)))
    beachball_type = StringChoice.T(
        choices=['full', 'deviatoric', 'dc'],
        default='dc')
    show_reference = Bool.T(default=True)

    def make(self, environ):
        cm = environ.get_plot_collection_manager()
        history = environ.get_history(subset='harvest')
        mpl_init(fontsize=self.font_size)
        cm.create_group_mpl(
            self,
            self.draw_figures(history),
            title=u'Hudson Plot',
            section='solution',
            feather_icon='box',
            description=u'''
Hudson's source type plot with the ensemble of bootstrap solutions.

For about 10% of the solutions (randomly chosen), the focal mechanism is
depicted, others are represented as dots. The square marks the global best
fitting solution.
''')

    def draw_figures(self, history):

        color = 'black'
        fontsize = self.font_size
        markersize = fontsize * 1.5
        markersize_small = markersize * 0.2
        beachballsize = markersize
        beachballsize_small = beachballsize * 0.5
        beachball_type = self.beachball_type

        problem = history.problem
        best_source = history.get_best_source()
        mean_source = history.get_mean_source()

        fig = plt.figure(figsize=self.size_inch)
        axes = fig.add_subplot(1, 1, 1)

        data = []
        for ix, x in enumerate(history.models):
            source = problem.get_source(x)
            mt = source.pyrocko_moment_tensor()
            u, v = hudson.project(mt)

            if random.random() < 0.1:
                try:
                    beachball.plot_beachball_mpl(
                        mt, axes,
                        beachball_type=beachball_type,
                        position=(u, v),
                        size=beachballsize_small,
                        color_t=color,
                        alpha=0.5,
                        zorder=1,
                        linewidth=0.25)
                except beachball.BeachballError as e:
                    logger.warn(str(e))

            else:
                data.append((u, v))

        if data:
            u, v = num.array(data).T
            axes.plot(
                u, v, 'o',
                color=color,
                ms=markersize_small,
                mec='none',
                mew=0,
                alpha=0.25,
                zorder=0)

        hudson.draw_axes(axes)

        mt = mean_source.pyrocko_moment_tensor()
        u, v = hudson.project(mt)

        try:
            beachball.plot_beachball_mpl(
                mt, axes,
                beachball_type=beachball_type,
                position=(u, v),
                size=beachballsize,
                color_t=color,
                zorder=2,
                linewidth=0.5)
        except beachball.BeachballError as e:
            logger.warn(str(e))

        mt = best_source.pyrocko_moment_tensor()
        u, v = hudson.project(mt)

        axes.plot(
            u, v, 's',
            markersize=markersize,
            mew=1,
            mec='black',
            mfc='none',
            zorder=-2)

        if self.show_reference:
            mt = problem.base_source.pyrocko_moment_tensor()
            u, v = hudson.project(mt)

            try:
                beachball.plot_beachball_mpl(
                    mt, axes,
                    beachball_type=beachball_type,
                    position=(u, v),
                    size=beachballsize,
                    color_t='red',
                    zorder=2,
                    linewidth=0.5)
            except beachball.BeachballError as e:
                logger.warn(str(e))

        item = PlotItem(
            name='main')
        return [[item, fig]]
Exemple #29
0
class FFIConfig(ModeConfig):

    regularization = StringChoice.T(
        default='none',
        choices=['laplacian', 'trans-dimensional', 'none'],
        help='Flag for regularization in distributed slip-optimization.')
Exemple #30
0
class CMTProblem(Problem):

    problem_parameters = [
        Parameter('time', 's', label='Time'),
        Parameter('north_shift', 'm', label='Northing', **as_km),
        Parameter('east_shift', 'm', label='Easting', **as_km),
        Parameter('depth', 'm', label='Depth', **as_km),
        Parameter('magnitude', label='Magnitude'),
        Parameter('rmnn', label='$m_{nn} / M_0$'),
        Parameter('rmee', label='$m_{ee} / M_0$'),
        Parameter('rmdd', label='$m_{dd} / M_0$'),
        Parameter('rmne', label='$m_{ne} / M_0$'),
        Parameter('rmnd', label='$m_{nd} / M_0$'),
        Parameter('rmed', label='$m_{ed} / M_0$'),
        Parameter('duration', 's', label='Duration')
    ]

    dependants = [
        Parameter('strike1', u'\u00b0', label='Strike 1'),
        Parameter('dip1', u'\u00b0', label='Dip 1'),
        Parameter('rake1', u'\u00b0', label='Rake 1'),
        Parameter('strike2', u'\u00b0', label='Strike 2'),
        Parameter('dip2', u'\u00b0', label='Dip 2'),
        Parameter('rake2', u'\u00b0', label='Rake 2'),
        Parameter('rel_moment_iso', label='$M_{0}^{ISO}/M_{0}$'),
        Parameter('rel_moment_clvd', label='$M_{0}^{CLVD}/M_{0}$')
    ]

    distance_min = Float.T(default=0.0)
    mt_type = StringChoice.T(default='full',
                             choices=['full', 'deviatoric', 'dc'])

    def __init__(self, **kwargs):
        Problem.__init__(self, **kwargs)
        self.deps_cache = {}

    def get_source(self, x):
        d = self.get_parameter_dict(x)
        rm6 = num.array([d.rmnn, d.rmee, d.rmdd, d.rmne, d.rmnd, d.rmed],
                        dtype=num.float)

        m0 = mtm.magnitude_to_moment(d.magnitude)
        m6 = rm6 * m0

        p = {}
        for k in self.base_source.keys():
            if k in d:
                p[k] = float(self.ranges[k].make_relative(
                    self.base_source[k], d[k]))

        stf = gf.HalfSinusoidSTF(duration=float(d.duration))

        source = self.base_source.clone(m6=m6, stf=stf, **p)
        return source

    def make_dependant(self, xs, pname):
        cache = self.deps_cache
        if xs.ndim == 1:
            return self.make_dependant(xs[num.newaxis, :], pname)[0]

        if pname not in self.dependant_names:
            raise KeyError(pname)

        mt = self.base_source.pyrocko_moment_tensor()

        sdrs_ref = mt.both_strike_dip_rake()

        y = num.zeros(xs.shape[0])
        for i, x in enumerate(xs):
            k = tuple(x.tolist())
            if k not in cache:
                source = self.get_source(x)
                mt = source.pyrocko_moment_tensor()
                res = mt.standard_decomposition()
                sdrs = mt.both_strike_dip_rake()
                if sdrs_ref:
                    sdrs = mtm.order_like(sdrs, sdrs_ref)

                cache[k] = mt, res, sdrs

            mt, res, sdrs = cache[k]

            if pname == 'rel_moment_iso':
                ratio_iso, m_iso = res[0][1:3]
                y[i] = ratio_iso * num.sign(m_iso[0, 0])
            elif pname == 'rel_moment_clvd':
                ratio_clvd, m_clvd = res[2][1:3]
                evals, evecs = mtm.eigh_check(m_clvd)
                ii = num.argmax(num.abs(evals))
                y[i] = ratio_clvd * num.sign(evals[ii])
            else:
                isdr = {'strike': 0, 'dip': 1, 'rake': 2}[pname[:-1]]
                y[i] = sdrs[int(pname[-1]) - 1][isdr]

        return y

    def pack(self, source):
        m6 = source.m6
        mt = source.pyrocko_moment_tensor()
        rm6 = m6 / mt.scalar_moment()

        x = num.array([
            source.time - self.base_source.time,
            source.north_shift,
            source.east_shift,
            source.depth,
            mt.moment_magnitude(),
        ] + rm6.tolist() + [source.stf.duration],
                      dtype=num.float)

        return x

    def random_uniform(self, xbounds):
        x = num.zeros(self.nparameters)
        for i in range(self.nparameters):
            x[i] = num.random.uniform(xbounds[i, 0], xbounds[i, 1])

        x[5:11] = mtm.random_m6()

        return x.tolist()

    def preconstrain(self, x):
        d = self.get_parameter_dict(x)
        m6 = num.array([d.rmnn, d.rmee, d.rmdd, d.rmne, d.rmnd, d.rmed],
                       dtype=num.float)

        m9 = mtm.symmat6(*m6)
        if self.mt_type == 'deviatoric':
            trace_m = num.trace(m9)
            m_iso = num.diag([trace_m / 3., trace_m / 3., trace_m / 3.])
            m9 -= m_iso

        elif self.mt_type == 'dc':
            mt = mtm.MomentTensor(m=m9)
            m9 = mt.standard_decomposition()[1][2]

        m0_unscaled = math.sqrt(num.sum(m9.A**2)) / math.sqrt(2.)

        m9 /= m0_unscaled
        m6 = mtm.to6(m9)
        d.rmnn, d.rmee, d.rmdd, d.rmne, d.rmnd, d.rmed = m6
        x = self.get_parameter_array(d)

        source = self.get_source(x)
        for t in self.targets:
            if (self.distance_min > num.asarray(t.distance_to(source))).any():
                raise Forbidden()

        return x

    def get_dependant_bounds(self):
        out = [(0., 360.), (0., 90.), (-180., 180.), (0., 360.), (0., 90.),
               (-180., 180.), (-1., 1.), (-1., 1.)]

        return out

    @classmethod
    def get_plot_classes(cls):
        from .. import plot
        plots = super(CMTProblem, cls).get_plot_classes()
        plots.extend([
            plot.HudsonPlot, plot.MTDecompositionPlot, plot.MTLocationPlot,
            plot.MTFuzzyPlot
        ])
        return plots