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