Ejemplo n.º 1
0
class VLVDProblem(Problem):

    problem_parameters = [
        Parameter('north_shift', 'm', label='Northing', **as_km),
        Parameter('east_shift', 'm', label='Easting', **as_km),
        Parameter('depth', 'm', label='Depth', **as_km),
        Parameter('dip', 'deg', label='Dip'),
        Parameter('azimuth', 'deg', label='Azimuth'),
        Parameter('clvd_moment', 'Nm', label='CLVD Moment'),
        Parameter('volume_change', 'm^3', label='Volume Change', **as_km3),
    ]

    problem_waveform_parameters = [
        Parameter('time', 's', label='Time'),
        Parameter('duration', 's', label='Duration')
    ]

    distance_min = Float.T(default=0.0)

    def pack(self, source):
        arr = self.get_parameter_array(source)
        for ip, p in enumerate(self.parameters):
            if p.name == 'time':
                arr[ip] -= self.base_source.time
        return arr

    def get_source(self, x):
        d = self.get_parameter_dict(x)

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

        stf = None
        if self.has_waveforms:
            stf = gf.HalfSinusoidSTF(duration=float(d.duration))

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

    @classmethod
    def get_plot_classes(cls):
        from . import plot
        plots = super(VLVDProblem, cls).get_plot_classes()
        plots.extend([plot.VLVDLocationPlot])
        return plots
Ejemplo n.º 2
0
class Problem(Object):
    '''
    Base class for objective function setup.

    Defines the *problem* to be solved by the optimiser.
    '''
    name = String.T()
    ranges = Dict.T(String.T(), gf.Range.T())
    dependants = List.T(Parameter.T())
    norm_exponent = Int.T(default=2)
    base_source = gf.Source.T(optional=True)
    targets = List.T(MisfitTarget.T())
    target_groups = List.T(TargetGroup.T())
    grond_version = String.T(optional=True)
    nthreads = Int.T(default=1)

    def __init__(self, **kwargs):
        Object.__init__(self, **kwargs)

        if self.grond_version is None:
            self.grond_version = __version__

        self._target_weights = None
        self._engine = None
        self._family_mask = None

        if hasattr(self, 'problem_waveform_parameters') and self.has_waveforms:
            self.problem_parameters =\
                self.problem_parameters + self.problem_waveform_parameters

        self.check()

    @classmethod
    def get_plot_classes(cls):
        from . import plot
        return plot.get_plot_classes()

    def check(self):
        paths = set()
        for grp in self.target_groups:
            if grp.path == 'all':
                continue
            if grp.path in paths:
                raise ValueError('Path %s defined more than once! In %s' %
                                 (grp.path, grp.__class__.__name__))
            paths.add(grp.path)
        logger.debug('TargetGroup check OK.')

    def copy(self):
        o = copy.copy(self)
        o._target_weights = None
        return o

    def set_target_parameter_values(self, x):
        nprob = len(self.problem_parameters)
        for target in self.targets:
            target.set_parameter_values(x[nprob:nprob + target.nparameters])
            nprob += target.nparameters

    def get_parameter_dict(self, model, group=None):
        params = []
        for ip, p in enumerate(self.parameters):
            if group in p.groups or group is None:
                params.append((p.name, model[ip]))
        return ADict(params)

    def get_parameter_array(self, d):
        arr = num.zeros(self.nparameters, dtype=num.float)
        for ip, p in enumerate(self.parameters):
            if p.name in d.keys():
                arr[ip] = d[p.name]
        return arr

    def dump_problem_info(self, dirname):
        fn = op.join(dirname, 'problem.yaml')
        util.ensuredirs(fn)
        guts.dump(self, filename=fn)

    def dump_problem_data(self,
                          dirname,
                          x,
                          misfits,
                          bootstraps=None,
                          sampler_context=None):

        fn = op.join(dirname, 'models')
        if not isinstance(x, num.ndarray):
            x = num.array(x)
        with open(fn, 'ab') as f:
            x.astype('<f8').tofile(f)

        fn = op.join(dirname, 'misfits')
        with open(fn, 'ab') as f:
            misfits.astype('<f8').tofile(f)

        if bootstraps is not None:
            fn = op.join(dirname, 'bootstraps')
            with open(fn, 'ab') as f:
                bootstraps.astype('<f8').tofile(f)

        if sampler_context is not None:
            fn = op.join(dirname, 'choices')
            with open(fn, 'ab') as f:
                num.array(sampler_context, dtype='<i8').tofile(f)

    def name_to_index(self, name):
        pnames = [p.name for p in self.combined]
        return pnames.index(name)

    @property
    def parameters(self):
        target_parameters = []
        for target in self.targets:
            target_parameters.extend(target.target_parameters)
        return self.problem_parameters + target_parameters

    @property
    def parameter_names(self):
        return [p.name for p in self.combined]

    @property
    def dependant_names(self):
        return [p.name for p in self.dependants]

    @property
    def nparameters(self):
        return len(self.parameters)

    @property
    def ntargets(self):
        return len(self.targets)

    @property
    def nwaveform_targets(self):
        return len(self.waveform_targets)

    @property
    def nsatellite_targets(self):
        return len(self.satellite_targets)

    @property
    def ngnss_targets(self):
        return len(self.gnss_targets)

    @property
    def nmisfits(self):
        nmisfits = 0
        for target in self.targets:
            nmisfits += target.nmisfits
        return nmisfits

    @property
    def ndependants(self):
        return len(self.dependants)

    @property
    def ncombined(self):
        return len(self.parameters) + len(self.dependants)

    @property
    def combined(self):
        return self.parameters + self.dependants

    @property
    def satellite_targets(self):
        return [
            t for t in self.targets if isinstance(t, SatelliteMisfitTarget)
        ]

    @property
    def gnss_targets(self):
        return [
            t for t in self.targets if isinstance(t, GNSSCampaignMisfitTarget)
        ]

    @property
    def waveform_targets(self):
        return [t for t in self.targets if isinstance(t, WaveformMisfitTarget)]

    @property
    def has_satellite(self):
        if self.satellite_targets:
            return True
        return False

    @property
    def has_waveforms(self):
        if self.waveform_targets:
            return True
        return False

    def set_engine(self, engine):
        self._engine = engine

    def get_engine(self):
        return self._engine

    def get_gf_store(self, target):
        if self.get_engine() is None:
            raise GrondError('Cannot get GF Store, modelling is not set up!')
        return self.get_engine().get_store(target.store_id)

    def random_uniform(self, xbounds, rstate):
        x = rstate.uniform(0., 1., self.nparameters)
        x *= (xbounds[:, 1] - xbounds[:, 0])
        x += xbounds[:, 0]
        return x

    def preconstrain(self, x):
        return x

    def extract(self, xs, i):
        if xs.ndim == 1:
            return self.extract(xs[num.newaxis, :], i)[0]

        if i < self.nparameters:
            return xs[:, i]
        else:
            return self.make_dependant(
                xs, self.dependants[i - self.nparameters].name)

    def get_target_weights(self):
        if self._target_weights is None:
            self._target_weights = num.concatenate(
                [target.get_combined_weight() for target in self.targets])

        return self._target_weights

    def get_target_residuals(self):
        pass

    def inter_family_weights(self, ns):
        exp, root = self.get_norm_functions()

        family, nfamilies = self.get_family_mask()

        ws = num.zeros(self.nmisfits)
        for ifamily in range(nfamilies):
            mask = family == ifamily
            ws[mask] = 1.0 / root(num.nansum(exp(ns[mask])))

        return ws

    def inter_family_weights2(self, ns):
        '''
        :param ns: 2D array with normalization factors ``ns[imodel, itarget]``
        :returns: 2D array ``weights[imodel, itarget]``
        '''

        exp, root = self.get_norm_functions()

        family, nfamilies = self.get_family_mask()
        ws = num.zeros(ns.shape)
        for ifamily in range(nfamilies):
            mask = family == ifamily
            ws[:, mask] = (
                1.0 / root(num.nansum(exp(ns[:, mask]), axis=1)))[:,
                                                                  num.newaxis]
        return ws

    def get_reference_model(self, expand=False):
        if expand:
            src_params = self.pack(self.base_source)
            ref = num.zeros(self.nparameters)
            ref[:src_params.size] = src_params
        else:
            ref = self.pack(self.base_source)
        return ref

    def get_parameter_bounds(self):
        out = []
        for p in self.problem_parameters:
            r = self.ranges[p.name]
            out.append((r.start, r.stop))

        for target in self.targets:
            for p in target.target_parameters:
                r = target.target_ranges[p.name_nogroups]
                out.append((r.start, r.stop))

        return num.array(out, dtype=num.float)

    def get_dependant_bounds(self):
        return num.zeros((0, 2))

    def get_combined_bounds(self):
        return num.vstack(
            (self.get_parameter_bounds(), self.get_dependant_bounds()))

    def raise_invalid_norm_exponent(self):
        raise GrondError('Invalid norm exponent: %f' % self.norm_exponent)

    def get_norm_functions(self):
        if self.norm_exponent == 2:

            def sqr(x):
                return x**2

            return sqr, num.sqrt

        elif self.norm_exponent == 1:

            def noop(x):
                return x

            return noop, num.abs

        else:
            self.raise_invalid_norm_exponent()

    def combine_misfits(self,
                        misfits,
                        extra_weights=None,
                        extra_residuals=None,
                        get_contributions=False):
        '''
        Combine misfit contributions (residuals) to global or bootstrap misfits

        :param misfits: 3D array ``misfits[imodel, iresidual, 0]`` are the
            misfit contributions (residuals) ``misfits[imodel, iresidual, 1]``
            are the normalisation contributions. It is also possible to give
            the misfit and normalisation contributions for a single model as
            ``misfits[iresidual, 0]`` and misfits[iresidual, 1]`` in which
            case, the first dimension (imodel) of the result will be stipped
            off.

        :param extra_weights: if given, 2D array of extra weights to be applied
            to the contributions, indexed as
            ``extra_weights[ibootstrap, iresidual]``.

        :param extra_residuals: if given, 2D array of perturbations to be added
            to the residuals, indexed as
            ``extra_residuals[ibootstrap, iresidual]``.

        :param get_contributions: get the weighted and perturbed contributions
            (don't do the sum).

        :returns: if no *extra_weights* or *extra_residuals* are given, a 1D
            array indexed as ``misfits[imodel]`` containing the global misfit
            for each model is returned, otherwise a 2D array
            ``misfits[imodel, ibootstrap]`` with the misfit for every model and
            weighting/residual set is returned.
        '''

        exp, root = self.get_norm_functions()

        if misfits.ndim == 2:
            misfits = misfits[num.newaxis, :, :]
            return self.combine_misfits(misfits, extra_weights,
                                        extra_residuals,
                                        get_contributions)[0, ...]

        assert misfits.ndim == 3
        assert extra_weights is None or extra_weights.ndim == 2
        assert extra_residuals is None or extra_residuals.ndim == 2

        if extra_weights is not None or extra_residuals is not None:
            if extra_weights is not None:
                w = extra_weights[num.newaxis, :, :] \
                    * self.get_target_weights()[num.newaxis, num.newaxis, :] \
                    * self.inter_family_weights2(
                        misfits[:, :, 1])[:, num.newaxis, :]
            else:
                w = 1.0

            if extra_residuals is not None:
                r = extra_residuals[num.newaxis, :, :]
            else:
                r = 0.0

            if get_contributions:
                return exp(w*(misfits[:, num.newaxis, :, 0]+r)) \
                    / num.nansum(
                        exp(w*misfits[:, num.newaxis, :, 1]),
                        axis=2)[:, :, num.newaxis]

            res = root(
                num.nansum(exp(w * (misfits[:, num.newaxis, :, 0] + r)),
                           axis=2) /
                num.nansum(exp(w * (misfits[:, num.newaxis, :, 1])), axis=2))
            assert res[res < 0].size == 0
            return res
        else:
            w = self.get_target_weights()[num.newaxis, :] \
                * self.inter_family_weights2(misfits[:, :, 1])

            r = self.get_target_weights()[num.newaxis, :] \
                * self.inter_family_weights2(misfits[:, :, 1])

            if get_contributions:
                return exp(w*misfits[:, :, 0]) \
                    / num.nansum(
                        exp(w*misfits[:, :, 1]),
                        axis=1)[:, num.newaxis]

            return root(
                num.nansum(exp(w * misfits[:, :, 0]), axis=1) /
                num.nansum(exp(w * misfits[:, :, 1]), axis=1))

    def make_family_mask(self):
        family_names = set()
        families = num.zeros(self.nmisfits, dtype=num.int)

        idx = 0
        for itarget, target in enumerate(self.targets):
            family_names.add(target.normalisation_family)
            families[idx:idx + target.nmisfits] = len(family_names) - 1
            idx += target.nmisfits

        return families, len(family_names)

    def get_family_mask(self):
        if self._family_mask is None:
            self._family_mask = self.make_family_mask()

        return self._family_mask

    def evaluate(self, x, mask=None, result_mode='full', targets=None):
        source = self.get_source(x)
        engine = self.get_engine()

        self.set_target_parameter_values(x)

        if mask is not None and targets is not None:
            raise ValueError('Mask cannot be defined with targets set.')
        targets = targets if targets is not None else self.targets

        for target in targets:
            target.set_result_mode(result_mode)

        modelling_targets = []
        t2m_map = {}
        for itarget, target in enumerate(targets):
            t2m_map[target] = target.prepare_modelling(engine, source, targets)
            if mask is None or mask[itarget]:
                modelling_targets.extend(t2m_map[target])

        u2m_map = {}
        for imtarget, mtarget in enumerate(modelling_targets):
            if mtarget not in u2m_map:
                u2m_map[mtarget] = []

            u2m_map[mtarget].append(imtarget)

        modelling_targets_unique = list(u2m_map.keys())

        resp = engine.process(source,
                              modelling_targets_unique,
                              nthreads=self.nthreads)
        modelling_results_unique = list(resp.results_list[0])

        modelling_results = [None] * len(modelling_targets)

        for mtarget, mresult in zip(modelling_targets_unique,
                                    modelling_results_unique):

            for itarget in u2m_map[mtarget]:
                modelling_results[itarget] = mresult

        imt = 0
        results = []
        for itarget, target in enumerate(targets):
            nmt_this = len(t2m_map[target])
            if mask is None or mask[itarget]:
                result = target.finalize_modelling(
                    engine, source, t2m_map[target],
                    modelling_results[imt:imt + nmt_this])

                imt += nmt_this
            else:
                result = gf.SeismosizerError(
                    'target was excluded from modelling')

            results.append(result)

        return results

    def misfits(self, x, mask=None):
        results = self.evaluate(x, mask=mask, result_mode='sparse')
        misfits = num.full((self.nmisfits, 2), num.nan)

        imisfit = 0
        for target, result in zip(self.targets, results):
            if isinstance(result, MisfitResult):
                misfits[imisfit:imisfit + target.nmisfits, :] = result.misfits

            imisfit += target.nmisfits

        return misfits

    def forward(self, x):
        source = self.get_source(x)
        engine = self.get_engine()

        plain_targets = []
        for target in self.targets:
            plain_targets.extend(target.get_plain_targets(engine, source))

        resp = engine.process(source, plain_targets)

        results = []
        for target, result in zip(plain_targets, resp.results_list[0]):
            if isinstance(result, gf.SeismosizerError):
                logger.debug('%s.%s.%s.%s: %s' % (target.codes +
                                                  (str(result), )))
            else:
                results.append(result)

        return results

    def get_random_model(self, ntries_limit=100):
        xbounds = self.get_parameter_bounds()

        for _ in range(ntries_limit):
            x = self.random_uniform(xbounds, rstate=g_rstate)
            try:
                return self.preconstrain(x)

            except Forbidden:
                pass

        raise GrondError(
            'Could not find any suitable candidate sample within %i tries' %
            (ntries_limit))
Ejemplo n.º 3
0
class RectangularProblem(Problem):
    # nucleation_x
    # nucleation_y
    # time
    # stf

    problem_parameters = [
        Parameter('east_shift', 'm', label='Easting', **as_km),
        Parameter('north_shift', 'm', label='Northing', **as_km),
        Parameter('depth', 'm', label='Depth', **as_km),
        Parameter('length', 'm', label='Length', **as_km),
        Parameter('width', 'm', label='Width', **as_km),
        Parameter('slip', 'm', label='Slip'),
        Parameter('strike', 'deg', label='Strike'),
        Parameter('dip', 'deg', label='Dip'),
        Parameter('rake', 'deg', label='Rake')
    ]

    problem_waveform_parameters = [
        Parameter('nucleation_x', 'offset', label='Nucleation X'),
        Parameter('nucleation_y', 'offset', label='Nucleation Y'),
        Parameter('time', 's', label='Time'),
    ]

    dependants = []

    distance_min = Float.T(default=0.0)

    def pack(self, source):
        arr = self.get_parameter_array(source)
        for ip, p in enumerate(self.parameters):
            if p.name == 'time':
                arr[ip] -= self.base_source.time
        return arr

    def get_source(self, x):
        d = self.get_parameter_dict(x)
        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]))

        source = self.base_source.clone(**p)

        return source

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

        return x

    def preconstrain(self, x):
        # source = self.get_source(x)
        # if any(self.distance_min > source.distance_to(t)
        #        for t in self.targets):
        # raise Forbidden()
        return x

    @classmethod
    def get_plot_classes(cls):
        plots = super(RectangularProblem, cls).get_plot_classes()
        return plots
Ejemplo n.º 4
0
class SatelliteMisfitTarget(gf.SatelliteTarget, MisfitTarget):
    """Handles and carries out operations related to the objective functions.

    Standard operations are the calculation of the weighted misfit between
    observed and predicted (synthetic) data. If enabled in the misfit
    configuration, orbital ramps are optimized for.
    """
    scene_id = String.T(help='UID string each Kite displacemente scene.'
                        ' Corresponds to Kite scene_id.')
    misfit_config = SatelliteMisfitConfig.T(
        help='Configuration of the ``SatelliteTarget``')

    can_bootstrap_residuals = True

    available_parameters = [
        Parameter('offset', 'm'),
        Parameter('ramp_north', 'm/m'),
        Parameter('ramp_east', 'm/m')
    ]

    def __init__(self, *args, **kwargs):
        gf.SatelliteTarget.__init__(self, *args, **kwargs)
        MisfitTarget.__init__(self, **kwargs)
        if not self.misfit_config.optimise_orbital_ramp:
            self.parameters = []
        else:
            self.parameters = self.available_parameters

        self.parameter_values = {}

    @property
    def target_ranges(self):
        return self.misfit_config.ranges

    def string_id(self):
        return '.'.join([self.path, self.scene_id])

    def set_dataset(self, ds):
        MisfitTarget.set_dataset(self, ds)

    @property
    def nmisfits(self):
        return self.lats.size

    @property
    def scene(self):
        return self._ds.get_kite_scene(self.scene_id)

    def post_process(self, engine, source, statics):
        """Applies the objective function.

        As a result the weighted misfits are given and the observed and
        synthetic data. For the satellite target the orbital ramp is
        calculated and applied here."""
        scene = self.scene
        quadtree = scene.quadtree

        obs = quadtree.leaf_medians

        if self.misfit_config.optimise_orbital_ramp:
            stat_level = num.full_like(obs, self.parameter_values['offset'])

            stat_level += (quadtree.leaf_center_distance[:, 0] *
                           self.parameter_values['ramp_east'])
            stat_level += (quadtree.leaf_center_distance[:, 1] *
                           self.parameter_values['ramp_north'])
            statics['displacement.los'] += stat_level

        stat_syn = statics['displacement.los']

        res = obs - stat_syn

        misfit_value = res
        misfit_norm = obs

        mf = num.vstack([misfit_value, misfit_norm]).T
        result = SatelliteMisfitResult(misfits=mf)

        if self._result_mode == 'full':
            result.statics_syn = statics
            result.statics_obs = quadtree.leaf_medians

        return result

    def get_combined_weight(self):
        if self._combined_weight is None:
            self._combined_weight = num.full(self.nmisfits, self.manual_weight)
        return self._combined_weight

    def prepare_modelling(self, engine, source, targets):
        return [self]

    def finalize_modelling(self, engine, source, modelling_targets,
                           modelling_results):
        return modelling_results[0]

    def init_bootstrap_residuals(self, nbootstraps, rstate=None):
        logger.info(
            'Scene "%s", initializing bootstrapping residuals from noise '
            'pertubations...' % self.scene_id)

        if rstate is None:
            rstate = num.random.RandomState()

        scene = self.scene
        qt = scene.quadtree
        cov = scene.covariance
        bootstraps = num.empty((nbootstraps, qt.nleaves))

        for ibs in range(nbootstraps):
            if not (ibs + 1) % 5:
                logger.info('Calculating noise realisation %d/%d.' %
                            (ibs + 1, nbootstraps))
            bootstraps[ibs, :] = cov.getQuadtreeNoise(rstate=rstate)

        self.set_bootstrap_residuals(bootstraps)

    @classmethod
    def get_plot_classes(cls):
        from . import plot
        plots = super(SatelliteMisfitTarget, cls).get_plot_classes()
        plots.extend(plot.get_plot_classes())
        return plots
Ejemplo n.º 5
0
class SatelliteMisfitTarget(gf.SatelliteTarget, MisfitTarget):
    """Handles and carries out operations related to the objective functions.

    Standard operations are the calculation of the weighted misfit between
    observed and predicted (synthetic) data. If enabled in the misfit
    configuration, orbital ramps are optimized for.
    """
    scene_id = String.T(
        help='UID string each Kite displacemente scene.'
             ' Corresponds to Kite scene_id.')
    misfit_config = SatelliteMisfitConfig.T(
        help='Configuration of the ``SatelliteTarget``')

    can_bootstrap_residuals = True

    available_parameters = [
        Parameter('offset', 'm'),
        Parameter('ramp_north', 'm/m'),
        Parameter('ramp_east', 'm/m')]

    def __init__(self, *args, **kwargs):
        gf.SatelliteTarget.__init__(self, *args, **kwargs)
        MisfitTarget.__init__(self, **kwargs)
        if not self.misfit_config.optimise_orbital_ramp:
            self.parameters = []
        else:
            self.parameters = self.available_parameters

        self.parameter_values = {}

        self._noise_weight_matrix = None

    @property
    def target_ranges(self):
        return self.misfit_config.ranges

    def string_id(self):
        return '.'.join([self.path, self.scene_id])

    @property
    def id(self):
        return self.scene_id

    def set_dataset(self, ds):
        MisfitTarget.set_dataset(self, ds)

    @property
    def nmisfits(self):
        return self.lats.size

    def get_correlated_weights(self, nthreads=0):
        ''' is for L2-norm weighting, the square-rooted, inverse covar '''
        if self._noise_weight_matrix is None:
            logger.info(
                'Inverting scene covariance matrix (nthreads=%i)...'
                % nthreads)
            cov = self.scene.covariance
            cov.nthreads = nthreads

            self._noise_weight_matrix = splinalg.sqrtm(
                num.linalg.inv(cov.covariance_matrix))

            logger.info('Inverting scene covariance matrix done.')

        return self._noise_weight_matrix

    @property
    def scene(self):
        return self._ds.get_kite_scene(self.scene_id)

    def post_process(self, engine, source, statics):
        """Applies the objective function.

        As a result the weighted misfits are given and the observed and
        synthetic data. For the satellite target the orbital ramp is
        calculated and applied here."""
        scene = self.scene
        quadtree = scene.quadtree

        obs = quadtree.leaf_medians

        if self.misfit_config.optimise_orbital_ramp:
            stat_level = num.full_like(obs, self.parameter_values['offset'])

            stat_level += (quadtree.leaf_center_distance[:, 0]
                           * self.parameter_values['ramp_east'])
            stat_level += (quadtree.leaf_center_distance[:, 1]
                           * self.parameter_values['ramp_north'])
            statics['displacement.los'] += stat_level

        stat_syn = statics['displacement.los']

        res = obs - stat_syn

        misfit_value = res
        misfit_norm = obs

        mf = num.vstack([misfit_value, misfit_norm]).T
        result = SatelliteMisfitResult(
            misfits=mf)

        if self._result_mode == 'full':
            result.statics_syn = statics
            result.statics_obs = quadtree.leaf_medians

        return result

    def get_combined_weight(self):
        if self._combined_weight is None:
            # invcov = self.scene.covariance.weight_matrix
            # self._combined_weight = invcov * self.manual_weight
            self._combined_weight = num.full(self.nmisfits, self.manual_weight)

        return self._combined_weight

    def prepare_modelling(self, engine, source, targets):
        return [self]

    def finalize_modelling(
            self, engine, source, modelling_targets, modelling_results):
        return modelling_results[0]

    def init_bootstrap_residuals(self, nbootstraps, rstate=None, nthreads=0):
        logger.info(
            'Scene "%s", initializing bootstrapping residuals from noise '
            'pertubations...' % self.scene_id)

        if rstate is None:
            rstate = num.random.RandomState()

        scene = self.scene
        qt = scene.quadtree
        cov = scene.covariance
        bootstraps = num.zeros((nbootstraps, qt.nleaves))

        try:
            # TODO:mi Signal handler is not given back to the main task!
            # This is a python3.7 bug
            warnings.warn('Using multi-threading for SatelliteTargets. '
                          'Python 3.7 needs to be killed hard:'
                          ' `killall grond`', UserWarning)
            from concurrent.futures import ThreadPoolExecutor
            nthreads = os.cpu_count() if not nthreads else nthreads

            with ThreadPoolExecutor(max_workers=nthreads) as executor:
                res = executor.map(
                    cov.getQuadtreeNoise,
                    [rstate for _ in range(nbootstraps)])

                for ibs, bs in enumerate(res):
                    bootstraps[ibs, :] = bs
        except ImportError:
            for ibs in range(nbootstraps):
                if not (ibs+1) % 5:
                    logger.info('Calculating noise realisation %d/%d.'
                                % (ibs+1, nbootstraps))
                bootstraps[ibs, :] = cov.getQuadtreeNoise(rstate=rstate)

        self.set_bootstrap_residuals(bootstraps)

    @classmethod
    def get_plot_classes(cls):
        from . import plot
        plots = super(SatelliteMisfitTarget, cls).get_plot_classes()
        plots.extend(plot.get_plot_classes())
        return plots
Ejemplo n.º 6
0
class DoubleDCProblem(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('strike1', 'deg', label='Strike 1'),
        Parameter('dip1', 'deg', label='Dip 1'),
        Parameter('rake1', 'deg', label='Rake 1'),
        Parameter('strike2', 'deg', label='Strike 2'),
        Parameter('dip2', 'deg', label='Dip 2'),
        Parameter('rake2', 'deg', label='Rake 2'),
        Parameter('delta_time', 's', label='$\\Delta$ Time'),
        Parameter('delta_depth', 'm', label='$\\Delta$ Depth'),
        Parameter('azimuth', 'deg', label='Azimuth'),
        Parameter('distance', 'm', label='Distance'),
        Parameter('mix', label='Mix'),
        Parameter('duration1', 's', label='Duration 1'),
        Parameter('duration2', 's', label='Duration 2')]

    dependants = []
    distance_min = Float.T(default=0.0)

    def get_source(self, x):
        d = self.get_parameter_dict(x)
        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]))

        stf1 = gf.HalfSinusoidSTF(duration=float(d.duration1))
        stf2 = gf.HalfSinusoidSTF(duration=float(d.duration2))

        source = self.base_source.clone(stf1=stf1, stf2=stf2, **p)
        return source

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

        raise KeyError(pname)

    def pack(self, source):
        arr = self.get_parameter_array(source)
        for ip, p in enumerate(self.parameters):
            if p.name == 'time':
                arr[ip] -= self.base_source.time
            if p.name == 'duration1':
                arr[ip] = source.stf1.duration if source.stf1 else 0.0
            if p.name == 'duration2':
                arr[ip] = source.stf2.duration if source.stf2 else 0.0
        return arr

    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])

        return x.tolist()

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

        return num.array(x, dtype=num.float)

    @classmethod
    def get_plot_classes(cls):
        from .. import plot
        plots = super(DoubleDCProblem, cls).get_plot_classes()
        plots.extend([plot.HudsonPlot, plot.MTDecompositionPlot,
                      plot.MTLocationPlot])
        return plots
Ejemplo n.º 7
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