Exemple #1
0
 def test_sample_number_of_occurrences(self):
     time_span = 40
     rate = 0.05
     num_samples = 8000
     tom = PoissonTOM(time_span)
     numpy.random.seed(31)
     mean = sum(tom.sample_number_of_occurrences(rate)
                for i in range(num_samples)) / float(num_samples)
     self.assertAlmostEqual(mean, rate * time_span, delta=1e-3)
Exemple #2
0
class UCERFSource(BaseSeismicSource):
    """
    :param source_file:
        Path to an existing HDF5 file containing the UCERF model
    :param float investigation_time:
        Investigation time of event set (years)
    :param start_date:
        Starting date of the investigation (None for time independent)
    :param float min_mag:
        Minimim magnitude for consideration of background sources
    :param npd:
        Nodal plane distribution as instance of :class:
        openquake.hazardlib.pmf.PMF
    :param hdd:
        Hypocentral depth distribution as instance of :class:
        openquake.hazardlib.pmf.PMF
    :param float aspect:
        Aspect ratio
    :param float upper_seismoge nic_depth:
        Upper seismogenic depth (km)
    :param float lower_seismogenic_depth:
        Lower seismogenic depth (km)
    :param msr:
        Magnitude scaling relation
    :param float mesh_spacing:
        Spacing (km) of fault mesh
    :param str trt:
        Tectonic region type
    :param float integration_distance:
        Maximum distance from rupture to site for consideration
    """
    code = b'U'
    MODIFICATIONS = set()
    tectonic_region_type = DEFAULT_TRT
    ruptures_per_block = None  # overridden by the source_reader
    checksum = 0
    _wkt = ''

    def __init__(self,
                 source_file,
                 investigation_time,
                 start_date,
                 min_mag,
                 npd,
                 hdd,
                 aspect=1.5,
                 upper_seismogenic_depth=0.0,
                 lower_seismogenic_depth=15.0,
                 msr=WC1994(),
                 mesh_spacing=1.0,
                 trt="Active Shallow Crust",
                 integration_distance=1000):
        assert os.path.exists(source_file), source_file
        self.source_file = source_file
        self.source_id = None  # unset until .new is called
        self.inv_time = investigation_time
        self.start_date = start_date
        self.temporal_occurrence_model = PoissonTOM(self.inv_time)
        self.min_mag = min_mag
        self.npd = npd
        self.hdd = hdd
        self.aspect = aspect
        self.usd = upper_seismogenic_depth
        self.lsd = lower_seismogenic_depth
        self.msr = msr
        self.mesh_spacing = mesh_spacing
        self.tectonic_region_type = trt
        self.stop = None
        self.start = None

    @property
    def num_ruptures(self):
        return self.stop - self.start

    @num_ruptures.setter
    def num_ruptures(self, value):  # hack to make the sourceconverter happy
        pass

    @cached_property
    def mags(self):
        # read from FM0_0/MEANFS/MEANMSR/Magnitude
        with h5py.File(self.source_file, "r") as hdf5:
            arr = hdf5[self.ukey["mag"]][self.start:self.stop]
        return arr

    @cached_property
    def rate(self):
        # read from FM0_0/MEANFS/MEANMSR/Rates/MeanRates
        with h5py.File(self.source_file, "r") as hdf5:
            return hdf5[self.ukey["rate"]][self.start:self.stop]

    @cached_property
    def rake(self):
        # read from FM0_0/MEANFS/Rake
        with h5py.File(self.source_file, "r") as hdf5:
            return hdf5[self.ukey["rake"]][self.start:self.stop]

    def wkt(self):
        return ''

    def count_ruptures(self):
        """
        The length of the rupture array if the branch_id is set, else 0
        """
        return self.num_ruptures

    def new(self, trt_smr, branch_id):
        """
        :param trt_smr: ordinal of the source group
        :param branch_name: name of the UCERF branch
        :param branch_id: string associated to the branch
        :returns: a new UCERFSource associated to the branch_id
        """
        new = copy.copy(self)
        new.trt_smr = trt_smr
        new.source_id = branch_id  # i.e. FM3_1/ABM/Shaw09Mod/
        # DsrUni_CharConst_M5Rate6.5_MMaxOff7.3_NoFix_SpatSeisU2
        new.ukey = build_ukey(branch_id, self.start_date)
        with h5py.File(self.source_file, "r") as hdf5:
            new.start = 0
            new.stop = len(hdf5[new.ukey["mag"]])
        return new

    def get_min_max_mag(self):
        """
        Called when updating the SourceGroup
        """
        return self.min_mag, 10

    def get_sections(self):
        """
        :returns: array of list of section indices
        """
        with h5py.File(self.source_file, 'r') as hdf5:
            dset = hdf5[self.ukey["geol"] + "/RuptureIndex"]
            return dset[self.start:self.stop]

    def get_planes(self):
        """
        :returns: dictionary of planes, one per section
        """
        dic = {}
        with h5py.File(self.source_file, 'r') as hdf5:
            sections = sorted(map(int, hdf5[self.ukey["sec"]]))
            for sec in sections:
                key = "{:s}/{:d}/RupturePlanes".format(self.ukey["sec"], sec)
                dic[sec] = hdf5[key][:]
        return dic

    def get_bounding_box(self, maxdist):
        """
        :returns: min_lon, min_lat, max_lon, max_lat
        """
        # this is the bounding box of the background, i.e. all of California!
        with h5py.File(self.source_file, 'r') as hdf5:
            locations = hdf5["Grid/Locations"][()]
        lons, lats = locations[:, 0], locations[:, 1]
        bbox = lons.min(), lats.min(), lons.max(), lats.max()
        a1 = min(maxdist * KM_TO_DEGREES, 90)
        a2 = angular_distance(maxdist, bbox[1], bbox[3])
        return bbox[0] - a2, bbox[1] - a1, bbox[2] + a2, bbox[3] + a1

    def get_background_sids(self):
        """
        We can apply the filtering of the background sites as a pre-processing
        step - this is done here rather than in the sampling of the ruptures
        themselves
        """
        branch_key = self.ukey["grid_key"]
        with h5py.File(self.source_file, 'r') as hdf5:
            bg_locations = hdf5["Grid/Locations"][()]
            if hasattr(self, 'src_filter'):
                # in event based
                idist = self.src_filter.integration_distance[DEFAULT_TRT][-1][
                    1]
            else:
                # in classical
                return numpy.arange(len(bg_locations))
            distances = min_geodetic_distance(
                self.src_filter.sitecol.xyz,
                (bg_locations[:, 0], bg_locations[:, 1]))
            # Add buffer equal to half of length of median area from Mmax
            mmax_areas = self.msr.get_median_area(
                hdf5["/".join(["Grid", branch_key, "MMax"])][()], 0.0)
            # for instance hdf5['Grid/FM0_0_MEANFS_MEANMSR/MMax']
            mmax_lengths = numpy.sqrt(mmax_areas / self.aspect)
            ok = distances <= (0.5 * mmax_lengths + idist)
            # get list of indices from array of booleans
            return numpy.where(ok)[0].tolist()

    def get_ucerf_rupture(self, ridx):
        """
        :param ridx: rupture index
        """
        sections = self.sections[ridx]
        mag = self.mags[ridx]
        if mag < self.min_mag:
            return

        surface_set = []
        for sec in sections:
            plane = self.planes[sec]
            for p in range(plane.shape[2]):
                surface_set.append(PlanarSurface.from_ucerf(plane[:, :, p]))

        rupture = ParametricProbabilisticRupture(
            mag, self.rake[ridx], self.tectonic_region_type,
            surface_set[len(surface_set) // 2].get_middle_point(),
            MultiSurface(surface_set), self.rate[ridx],
            self.temporal_occurrence_model)
        rupture.rup_id = self.start + ridx
        return rupture

    def iter_ruptures(self, **kwargs):
        """
        Yield ruptures for the current set of indices
        """
        for ridx in range(self.start, self.stop):
            if self.rate[ridx - self.start]:  # may have have zero rate
                rup = self.get_ucerf_rupture(ridx - self.start)
                if rup:
                    yield rup

    def few_ruptures(self, **kwargs):
        """
        Fast version of iter_ruptures used in estimate_weight
        """
        for ridx in range(self.start, self.stop, 25):
            if self.rate[ridx - self.start]:  # may have have zero rate
                rup = self.get_ucerf_rupture(ridx - self.start)
                if rup:
                    yield rup

    # called upfront, before start_classical
    def __iter__(self):
        if self.stop - self.start <= self.ruptures_per_block:  # already split
            yield self
            return
        for start in range(self.start, self.stop, self.ruptures_per_block):
            stop = min(start + self.ruptures_per_block, self.stop)
            new = copy.copy(self)
            new.id = self.id
            new.source_id = '%s:%d-%d' % (self.source_id, start, stop)
            new.start = start
            new.stop = stop
            yield new

    def __repr__(self):
        return '<%s %s>' % (self.__class__.__name__, self.source_id)

    def get_background_sources(self):
        """
        Turn the background model of a given branch into a set of point sources
        """
        background_sids = self.get_background_sids()
        with h5py.File(self.source_file, "r") as hdf5:
            grid_loc = "/".join(["Grid", self.ukey["grid_key"]])
            # for instance Grid/FM0_0_MEANFS_MEANMSR_MeanRates
            mags = hdf5[grid_loc + "/Magnitude"][()]
            mmax = hdf5[grid_loc + "/MMax"][background_sids]
            rates = hdf5[grid_loc + "/RateArray"][background_sids, :]
            locations = hdf5["Grid/Locations"][background_sids, :]
            sources = []
            for i, bg_idx in enumerate(background_sids):
                src_id = "_".join([self.ukey["grid_key"], str(bg_idx)])
                src_name = "|".join([self.ukey["total_key"], str(bg_idx)])
                mag_idx = (self.min_mag <= mags) & (mags < mmax[i])
                src_mags = mags[mag_idx]
                if len(src_mags) == 1:  # too short
                    continue
                src_mfd = EvenlyDiscretizedMFD(src_mags[0],
                                               src_mags[1] - src_mags[0],
                                               rates[i, mag_idx].tolist())
                ps = PointSource(src_id, src_name, self.tectonic_region_type,
                                 src_mfd, self.mesh_spacing, self.msr,
                                 self.aspect, self.temporal_occurrence_model,
                                 self.usd, self.lsd,
                                 Point(locations[i, 0],
                                       locations[i, 1]), self.npd, self.hdd)
                ps.checksum = zlib.adler32(pickle.dumps(vars(ps), protocol=4))
                ps._wkt = ps.wkt()
                ps.id = self.id
                ps.trt_smr = self.trt_smr
                ps.num_ruptures = ps.count_ruptures()
                ps.nsites = 1  # anything <> 0 goes
                sources.append(ps)
        return sources

    def get_one_rupture(self, ses_seed):
        raise NotImplementedError

    def generate_event_set(self, background_sids, eff_num_ses):
        """
        Generates the event set corresponding to a particular branch
        """
        # get rates from file
        with h5py.File(self.source_file, 'r') as hdf5:
            occurrences = (
                self.temporal_occurrence_model.sample_number_of_occurrences(
                    self.rate * eff_num_ses, self.serial))
            indices, = numpy.where(occurrences)
            logging.debug('Considering "%s", %d ruptures', self.source_id,
                          len(indices))

            # get ruptures from the indices
            ruptures = []
            rupture_occ = []
            for ridx, n_occ in zip(indices, occurrences[indices]):
                ucerf_rup = self.get_ucerf_rupture(ridx)
                if ucerf_rup:
                    ruptures.append(ucerf_rup)
                    rupture_occ.append(n_occ)

            # sample background sources
            background_ruptures, background_n_occ = sample_background_model(
                hdf5, self.ukey["grid_key"], self.temporal_occurrence_model,
                eff_num_ses, self.serial, background_sids, self.min_mag,
                self.npd, self.hdd, self.usd, self.lsd, self.msr, self.aspect,
                self.tectonic_region_type)
            ruptures.extend(background_ruptures)
            rupture_occ.extend(background_n_occ)
        return ruptures, rupture_occ

    def _sample_ruptures(self, eff_num_ses):
        background_sids = self.get_background_sids()
        n_occ = AccumDict(accum=0)
        rups, occs = self.generate_event_set(background_sids, eff_num_ses)
        for rup, occ in zip(rups, occs):
            n_occ[rup] += occ
        yield from n_occ.items()