def export_hcurves_csv(ekey, dstore): """ Exports the hazard curves into several .csv files :param ekey: export key, i.e. a pair (datastore key, fmt) :param dstore: datastore object """ oq = dstore['oqparam'] info = get_info(dstore) R = dstore['full_lt'].get_num_rlzs() sitecol = dstore['sitecol'] sitemesh = get_mesh(sitecol) key, kind, fmt = get_kkf(ekey) fnames = [] comment = dstore.metadata hmap_dt = oq.hmap_dt() for kind in oq.get_kinds(kind, R): fname = hazard_curve_name(dstore, (key, fmt), kind) comment.update(kind=kind, investigation_time=oq.investigation_time) if (key in ('hmaps', 'uhs') and oq.uniform_hazard_spectra or oq.hazard_maps): hmap = extract(dstore, 'hmaps?kind=' + kind)[kind] if key == 'uhs' and oq.poes and oq.uniform_hazard_spectra: uhs_curves = calc.make_uhs(hmap, info) writers.write_csv( fname, util.compose_arrays(sitemesh, uhs_curves), comment=comment) fnames.append(fname) elif key == 'hmaps' and oq.poes and oq.hazard_maps: fnames.extend( export_hmaps_csv(ekey, fname, sitemesh, hmap.flatten().view(hmap_dt), comment)) elif key == 'hcurves': # shape (N, R|S, M, L1) if ('amplification' in oq.inputs and oq.amplification_method == 'convolution'): imtls = DictArray( {imt: oq.soil_intensities for imt in oq.imtls}) else: imtls = oq.imtls for imt, imls in imtls.items(): hcurves = extract( dstore, 'hcurves?kind=%s&imt=%s' % (kind, imt))[kind] fnames.append( export_hcurves_by_imt_csv( ekey, kind, fname, sitecol, hcurves, imt, imls, comment)) return sorted(fnames)
class ContextMaker(object): """ A class to manage the creation of contexts for distances, sites, rupture. """ REQUIRES = ['DISTANCES', 'SITES_PARAMETERS', 'RUPTURE_PARAMETERS'] def __init__(self, trt, gsims, param=None, monitor=Monitor()): param = param or {} # empty in the gmpe-smtk self.af = param.get('af', None) self.max_sites_disagg = param.get('max_sites_disagg', 10) self.collapse_level = param.get('collapse_level', False) self.trt = trt self.gsims = gsims self.single_site_opt = numpy.array( [hasattr(gsim, 'get_mean_std1') for gsim in gsims]) self.maximum_distance = (param.get('maximum_distance') or MagDepDistance({})) self.investigation_time = param.get('investigation_time') self.trunclevel = param.get('truncation_level') self.num_epsilon_bins = param.get('num_epsilon_bins', 1) self.effect = param.get('effect') self.task_no = getattr(monitor, 'task_no', 0) for req in self.REQUIRES: reqset = set() for gsim in gsims: reqset.update(getattr(gsim, 'REQUIRES_' + req)) setattr(self, 'REQUIRES_' + req, reqset) # self.pointsource_distance is a dict mag -> dist, possibly empty psd = param.get('pointsource_distance') if hasattr(psd, 'ddic'): self.pointsource_distance = psd.ddic.get(trt, {}) else: self.pointsource_distance = {} if 'imtls' in param: self.imtls = param['imtls'] elif 'hazard_imtls' in param: self.imtls = DictArray(param['hazard_imtls']) else: self.imtls = {} self.imts = [imt_module.from_string(imt) for imt in self.imtls] self.reqv = param.get('reqv') if self.reqv is not None: self.REQUIRES_DISTANCES.add('repi') self.mon = monitor self.ctx_mon = monitor('make_contexts', measuremem=False) self.loglevels = DictArray(self.imtls) if self.imtls else {} self.shift_hypo = param.get('shift_hypo') with warnings.catch_warnings(): # avoid RuntimeWarning: divide by zero encountered in log warnings.simplefilter("ignore") for imt, imls in self.imtls.items(): if imt != 'MMI': self.loglevels[imt] = numpy.log(imls) # instantiate monitors self.gmf_mon = monitor('computing mean_std', measuremem=False) self.poe_mon = monitor('get_poes', measuremem=False) def multi(self, ctxs): """ :params ctxs: a list of contexts, all referring to a single point :returns: a multiple RuptureContext """ ctx = RuptureContext() for par in self.REQUIRES_SITES_PARAMETERS: setattr(ctx, par, getattr(ctxs[0], par)) for par in self.REQUIRES_RUPTURE_PARAMETERS: vals = [getattr(ctx, par) for ctx in ctxs] setattr(ctx, par, numpy.array(vals)) for par in self.REQUIRES_DISTANCES: dists = [getattr(ctx, par)[0] for ctx in ctxs] setattr(ctx, par, numpy.array(dists)) ctx.ctxs = ctxs return ctx def gen_ctx_poes(self, ctxs): """ :param ctxs: a list of C context objects :yields: C pairs (ctx, poes of shape (N, L, G)) """ nsites = numpy.array([len(ctx.sids) for ctx in ctxs]) C = len(ctxs) N = nsites.sum() poes = numpy.zeros((N, self.loglevels.size, len(self.gsims))) if self.single_site_opt.any(): ctx = self.multi(ctxs) for g, gsim in enumerate(self.gsims): with self.gmf_mon: # builds mean_std of shape (2, N, M) if self.single_site_opt[g] and C > 1 and (nsites == 1).all(): mean_std = gsim.get_mean_std1(ctx, self.imts) else: mean_std = gsim.get_mean_std(ctxs, self.imts) with self.poe_mon: # builds poes of shape (N, L, G) poes[:, :, g] = gsim.get_poes(mean_std, self.loglevels, self.trunclevel, self.af, ctxs) s = 0 for ctx, n in zip(ctxs, nsites): yield ctx, poes[s:s + n] s += n def get_ctx_params(self): """ :returns: the interesting attributes of the context """ params = { 'occurrence_rate', 'sids_', 'src_id', 'probs_occur_', 'clon_', 'clat_', 'rrup_' } params.update(self.REQUIRES_RUPTURE_PARAMETERS) for dparam in self.REQUIRES_DISTANCES: params.add(dparam + '_') return params def from_srcs(self, srcs, site1): # used in disagg.disaggregation """ :returns: a list RuptureContexts """ allctxs = [] for i, src in enumerate(srcs): src.id = i ctxs = [] for rup in src.iter_ruptures(shift_hypo=self.shift_hypo): ctxs.append(self.make_rctx(rup)) allctxs.extend(self.gen_ctxs(ctxs, site1, src.id)) return allctxs def filter(self, sites, rup): """ Filter the site collection with respect to the rupture. :param sites: Instance of :class:`openquake.hazardlib.site.SiteCollection`. :param rup: Instance of :class:`openquake.hazardlib.source.rupture.BaseRupture` :returns: (filtered sites, distance context) """ distances = get_distances(rup, sites, 'rrup') mdist = self.maximum_distance(self.trt, rup.mag) mask = distances <= mdist if mask.any(): sites, distances = sites.filter(mask), distances[mask] else: raise FarAwayRupture('%d: %d km' % (rup.rup_id, distances.min())) return sites, DistancesContext([('rrup', distances)]) def make_rctx(self, rupture): """ Add .REQUIRES_RUPTURE_PARAMETERS to the rupture """ ctx = RuptureContext() vars(ctx).update(vars(rupture)) for param in self.REQUIRES_RUPTURE_PARAMETERS: if param == 'mag': value = rupture.mag elif param == 'strike': value = rupture.surface.get_strike() elif param == 'dip': value = rupture.surface.get_dip() elif param == 'rake': value = rupture.rake elif param == 'ztor': value = rupture.surface.get_top_edge_depth() elif param == 'hypo_lon': value = rupture.hypocenter.longitude elif param == 'hypo_lat': value = rupture.hypocenter.latitude elif param == 'hypo_depth': value = rupture.hypocenter.depth elif param == 'width': value = rupture.surface.get_width() else: raise ValueError('%s requires unknown rupture parameter %r' % (type(self).__name__, param)) setattr(ctx, param, value) return ctx def make_contexts(self, sites, rupture): """ Filter the site collection with respect to the rupture and create context objects. :param sites: Instance of :class:`openquake.hazardlib.site.SiteCollection`. :param rupture: Instance of :class:`openquake.hazardlib.source.rupture.BaseRupture` :returns: Tuple of three items: rupture, sites and distances context. :raises ValueError: If any of declared required parameters (site, rupture and distance parameters) is unknown. """ sites, dctx = self.filter(sites, rupture) for param in self.REQUIRES_DISTANCES - {'rrup'}: distances = get_distances(rupture, sites, param) setattr(dctx, param, distances) reqv_obj = (self.reqv.get(self.trt) if self.reqv else None) if reqv_obj and isinstance(rupture.surface, PlanarSurface): reqv = reqv_obj.get(dctx.repi, rupture.mag) if 'rjb' in self.REQUIRES_DISTANCES: dctx.rjb = reqv if 'rrup' in self.REQUIRES_DISTANCES: dctx.rrup = numpy.sqrt(reqv**2 + rupture.hypocenter.depth**2) return self.make_rctx(rupture), sites, dctx def gen_ctxs(self, ruptures, sites, src_id, mon=Monitor()): """ :param ruptures: a list of ruptures generated by the same source :param sites: a (filtered) SiteCollection :param src_id: the ID of the source (for debugging purposes) :param mon: a Monitor object :yields: fat RuptureContexts """ fewsites = len(sites.complete) <= self.max_sites_disagg for rup in ruptures: with mon: try: ctx, r_sites, dctx = self.make_contexts( getattr(rup, 'sites', sites), rup) except FarAwayRupture: continue for par in self.REQUIRES_SITES_PARAMETERS: setattr(ctx, par, r_sites[par]) ctx.sids = r_sites.sids ctx.src_id = src_id for par in self.REQUIRES_DISTANCES | {'rrup'}: setattr(ctx, par, getattr(dctx, par)) if fewsites: closest = rup.surface.get_closest_points(sites.complete) ctx.clon = closest.lons[ctx.sids] ctx.clat = closest.lats[ctx.sids] yield ctx def collapse_the_ctxs(self, ctxs): """ Collapse contexts with similar parameters and distances. :param ctxs: a list of pairs (rup, dctx) :returns: collapsed contexts """ if len(ctxs) == 1: return ctxs if self.collapse_level >= 3: # hack, ignore everything except mag rrp = ['mag'] rnd = 0 # round distances to 1 km else: rrp = self.REQUIRES_RUPTURE_PARAMETERS rnd = 1 # round distances to 100 m def params(ctx): lst = [] for par in rrp: lst.append(getattr(ctx, par)) for dst in self.REQUIRES_DISTANCES: lst.extend(numpy.round(getattr(ctx, dst), rnd)) return tuple(lst) out = [] for values in groupby(ctxs, params).values(): out.extend(_collapse(values)) return out def max_intensity(self, sitecol1, mags, dists): """ :param sitecol1: a SiteCollection instance with a single site :param mags: a sequence of magnitudes :param dists: a sequence of distances :returns: an array of GMVs of shape (#mags, #dists) """ assert len(sitecol1) == 1, sitecol1 nmags, ndists = len(mags), len(dists) gmv = numpy.zeros((nmags, ndists)) for m, d in itertools.product(range(nmags), range(ndists)): mag, dist = mags[m], dists[d] ctx = RuptureContext() for par in self.REQUIRES_RUPTURE_PARAMETERS: setattr(ctx, par, 0) for dst in self.REQUIRES_DISTANCES: setattr(ctx, dst, numpy.array([dist])) for par in self.REQUIRES_SITES_PARAMETERS: setattr(ctx, par, getattr(sitecol1, par)) ctx.sids = sitecol1.sids ctx.mag = mag ctx.width = .01 # 10 meters to avoid warnings in abrahamson_2014 means = [] for gsim in self.gsims: try: mean = gsim.get_mean_std([ctx], self.imts)[0, 0] except ValueError: # magnitude outside of supported range continue means.append(mean.max()) if means: gmv[m, d] = numpy.exp(max(means)) return gmv
class ContextMaker(object): """ A class to manage the creation of contexts and to compute mean/stddevs and possibly PoEs. :param trt: a tectonic region type string :param gsims: a list of GSIMs or a dictionary gsim -> rlz indices :param param: a dictionary of parameters like the maximum_distance, the IMTLs, the investigation time, etc NB: the trt can be different from the tectonic region type for which the underlying GSIMs are defined. This is intentional. """ REQUIRES = ['DISTANCES', 'SITES_PARAMETERS', 'RUPTURE_PARAMETERS'] rup_indep = True tom = None @property def dtype(self): """ :returns: dtype of the underlying ctx_builder """ return self.ctx_builder.dtype def __init__(self, trt, gsims, oq, monitor=Monitor()): if isinstance(oq, dict): param = oq self.cross_correl = param.get('cross_correl') # cond_spectra_test else: # OqParam param = vars(oq) param['split_sources'] = oq.split_sources param['min_iml'] = oq.min_iml param['reqv'] = oq.get_reqv() param['af'] = getattr(oq, 'af', None) self.cross_correl = oq.cross_correl self.imtls = oq.imtls self.af = param.get('af', None) self.max_sites_disagg = param.get('max_sites_disagg', 10) self.max_sites_per_tile = param.get('max_sites_per_tile', 50_000) self.time_per_task = param.get('time_per_task', 60) self.disagg_by_src = param.get('disagg_by_src') self.collapse_level = int(param.get('collapse_level', 0)) self.disagg_by_src = param.get('disagg_by_src', False) self.trt = trt self.gsims = gsims self.maximum_distance = _interp(param, 'maximum_distance', trt) if 'pointsource_distance' not in param: self.pointsource_distance = 1000. else: self.pointsource_distance = getdefault( param['pointsource_distance'], trt) self.minimum_distance = param.get('minimum_distance', 0) self.investigation_time = param.get('investigation_time') if self.investigation_time: self.tom = registry['PoissonTOM'](self.investigation_time) self.ses_seed = param.get('ses_seed', 42) self.ses_per_logic_tree_path = param.get('ses_per_logic_tree_path', 1) self.truncation_level = param.get('truncation_level') self.num_epsilon_bins = param.get('num_epsilon_bins', 1) self.ps_grid_spacing = param.get('ps_grid_spacing') self.split_sources = param.get('split_sources') self.effect = param.get('effect') self.use_recarray = use_recarray(gsims) for req in self.REQUIRES: reqset = set() for gsim in gsims: reqset.update(getattr(gsim, 'REQUIRES_' + req)) setattr(self, 'REQUIRES_' + req, reqset) if 'imtls' in param: self.imtls = param['imtls'] elif 'hazard_imtls' in param: self.imtls = DictArray(param['hazard_imtls']) elif not hasattr(self, 'imtls'): raise KeyError('Missing imtls in ContextMaker!') try: self.min_iml = param['min_iml'] except KeyError: self.min_iml = [0. for imt in self.imtls] self.reqv = param.get('reqv') if self.reqv is not None: self.REQUIRES_DISTANCES.add('repi') reqs = (sorted(self.REQUIRES_RUPTURE_PARAMETERS) + sorted(self.REQUIRES_SITES_PARAMETERS) + sorted(self.REQUIRES_DISTANCES)) dic = {} for req in reqs: if req in site_param_dt: dt = site_param_dt[req] if isinstance(dt, tuple): # (string_, size) dic[req] = b'' else: dic[req] = dt(0) else: dic[req] = 0. dic['occurrence_rate'] = numpy.float64(0) dic['sids'] = numpy.uint32(0) self.ctx_builder = RecordBuilder(**dic) self.loglevels = DictArray(self.imtls) if self.imtls else {} self.shift_hypo = param.get('shift_hypo') with warnings.catch_warnings(): # avoid RuntimeWarning: divide by zero encountered in log warnings.simplefilter("ignore") for imt, imls in self.imtls.items(): if imt != 'MMI': self.loglevels[imt] = numpy.log(imls) self.init_monitoring(monitor) def init_monitoring(self, monitor): # instantiating child monitors, may be called in the workers self.ctx_mon = monitor('make_contexts', measuremem=True) self.gmf_mon = monitor('computing mean_std', measuremem=False) self.poe_mon = monitor('get_poes', measuremem=False) self.pne_mon = monitor('composing pnes', measuremem=False) self.task_no = getattr(monitor, 'task_no', 0) def read_ctxs(self, dstore, slc=None): """ :param dstore: a DataStore instance :param slice: a slice of contexts with the same grp_id :returns: a list of contexts plus N lists of contexts for each site """ sitecol = dstore['sitecol'].complete if slc is None: slc = dstore['rup/grp_id'][:] == self.grp_id params = {n: dstore['rup/' + n][slc] for n in dstore['rup']} ctxs = [] for u in range(len(params['mag'])): ctx = RuptureContext() for par, arr in params.items(): if par.endswith('_'): par = par[:-1] setattr(ctx, par, arr[u]) for par in sitecol.array.dtype.names: setattr(ctx, par, sitecol[par][ctx.sids]) ctxs.append(ctx) return ctxs def recarray(self, ctxs): """ :params ctxs: a list of contexts :returns: a recarray """ C = sum(len(ctx) for ctx in ctxs) ra = self.ctx_builder.zeros(C).view(numpy.recarray) start = 0 for ctx in ctxs: slc = slice(start, start + len(ctx)) for par in self.ctx_builder.names: getattr(ra, par)[slc] = getattr(ctx, par) ra.sids[slc] = ctx.sids start = slc.stop return ra def get_ctx_params(self): """ :returns: the interesting attributes of the context """ params = { 'occurrence_rate', 'sids_', 'src_id', 'probs_occur_', 'clon_', 'clat_', 'rrup_' } params.update(self.REQUIRES_RUPTURE_PARAMETERS) for dparam in self.REQUIRES_DISTANCES: params.add(dparam + '_') return params def from_srcs(self, srcs, sitecol): # used in disagg.disaggregation """ :param srcs: a list of Source objects :param sitecol: a SiteCollection instance :returns: a list RuptureContexts """ allctxs = [] cnt = 0 for i, src in enumerate(srcs): src.id = i rctxs = [] for rup in src.iter_ruptures(shift_hypo=self.shift_hypo): rup.rup_id = cnt rctxs.append(self.make_rctx(rup)) cnt += 1 allctxs.extend(self.get_ctxs(rctxs, sitecol, src.id)) return allctxs def filter(self, sites, rup): """ Filter the site collection with respect to the rupture. :param sites: Instance of :class:`openquake.hazardlib.site.SiteCollection`. :param rup: Instance of :class:`openquake.hazardlib.source.rupture.BaseRupture` :returns: (filtered sites, distance context) """ distances = get_distances(rup, sites, 'rrup') mdist = self.maximum_distance(rup.mag) + _delta_pr(rup) mask = distances <= mdist if mask.any(): sites, distances = sites.filter(mask), distances[mask] else: raise FarAwayRupture('%d: %d km' % (rup.rup_id, distances.min())) return sites, DistancesContext([('rrup', distances)]) def make_rctx(self, rupture): """ Add .REQUIRES_RUPTURE_PARAMETERS to the rupture """ ctx = RuptureContext() vars(ctx).update(vars(rupture)) for param in self.REQUIRES_RUPTURE_PARAMETERS: if param == 'mag': value = rupture.mag elif param == 'strike': value = rupture.surface.get_strike() elif param == 'dip': value = rupture.surface.get_dip() elif param == 'rake': value = rupture.rake elif param == 'ztor': value = rupture.surface.get_top_edge_depth() elif param == 'hypo_lon': value = rupture.hypocenter.longitude elif param == 'hypo_lat': value = rupture.hypocenter.latitude elif param == 'hypo_depth': value = rupture.hypocenter.depth elif param == 'width': value = rupture.surface.get_width() else: raise ValueError('%s requires unknown rupture parameter %r' % (type(self).__name__, param)) setattr(ctx, param, value) return ctx def get_ctxs(self, src_or_ruptures, sitecol, src_id=None): """ :param src_or_ruptures: a source or a list of ruptures generated by a source :param sitecol: a (filtered) SiteCollection :param src_id: the numeric ID of the source (to be assigned to the ruptures) :returns: fat RuptureContexts """ if hasattr(src_or_ruptures, 'source_id'): irups = self._gen_rups(src_or_ruptures, sitecol) else: irups = src_or_ruptures ctxs = [] fewsites = len(sitecol.complete) <= self.max_sites_disagg for rup in irups: sites = getattr(rup, 'sites', sitecol) try: r_sites, dctx = self.filter(sites, rup) except FarAwayRupture: continue ctx = self.make_rctx(rup) ctx.sites = r_sites for param in self.REQUIRES_DISTANCES - {'rrup'}: distances = get_distances(rup, r_sites, param) setattr(dctx, param, distances) reqv_obj = (self.reqv.get(self.trt) if self.reqv else None) if reqv_obj and isinstance(rup.surface, PlanarSurface): reqv = reqv_obj.get(dctx.repi, rup.mag) if 'rjb' in self.REQUIRES_DISTANCES: dctx.rjb = reqv if 'rrup' in self.REQUIRES_DISTANCES: dctx.rrup = numpy.sqrt(reqv**2 + rup.hypocenter.depth**2) for name in r_sites.array.dtype.names: setattr(ctx, name, r_sites[name]) ctx.src_id = src_id for par in self.REQUIRES_DISTANCES | {'rrup'}: setattr(ctx, par, getattr(dctx, par)) if fewsites: # get closest point on the surface closest = rup.surface.get_closest_points(sitecol.complete) ctx.clon = closest.lons[ctx.sids] ctx.clat = closest.lats[ctx.sids] ctxs.append(ctx) return ctxs # this is used with pointsource_distance approximation for close distances, # when there are many ruptures affecting few sites def collapse_the_ctxs(self, ctxs): """ Collapse contexts with similar parameters and distances. :param ctxs: a list of pairs (rup, dctx) :returns: collapsed contexts """ if len(ctxs) == 1: return ctxs if self.collapse_level >= 3: # hack, ignore everything except mag rrp = ['mag'] rnd = 0 # round distances to 1 km else: rrp = self.REQUIRES_RUPTURE_PARAMETERS rnd = 1 # round distances to 100 m def params(ctx): lst = [] for par in rrp: lst.append(getattr(ctx, par)) for dst in self.REQUIRES_DISTANCES: lst.extend(numpy.round(getattr(ctx, dst), rnd)) return tuple(lst) out = [] for values in groupby(ctxs, params).values(): out.extend(_collapse(values)) return out def max_intensity(self, sitecol1, mags, dists): """ :param sitecol1: a SiteCollection instance with a single site :param mags: a sequence of magnitudes :param dists: a sequence of distances :returns: an array of GMVs of shape (#mags, #dists) """ assert len(sitecol1) == 1, sitecol1 nmags, ndists = len(mags), len(dists) gmv = numpy.zeros((nmags, ndists)) for m, d in itertools.product(range(nmags), range(ndists)): mag, dist = mags[m], dists[d] ctx = RuptureContext() for par in self.REQUIRES_RUPTURE_PARAMETERS: setattr(ctx, par, 0) for dst in self.REQUIRES_DISTANCES: setattr(ctx, dst, numpy.array([dist])) for par in self.REQUIRES_SITES_PARAMETERS: setattr(ctx, par, getattr(sitecol1, par)) ctx.sids = sitecol1.sids ctx.mag = mag ctx.width = .01 # 10 meters to avoid warnings in abrahamson_2014 try: maxmean = self.get_mean_stds([ctx])[0].max() # shape NM except ValueError: # magnitude outside of supported range continue else: gmv[m, d] = numpy.exp(maxmean) return gmv def _ruptures(self, src, filtermag=None, point_rup=False): return src.iter_ruptures(shift_hypo=self.shift_hypo, mag=filtermag, point_rup=point_rup) def _gen_rups(self, src, sites): # yield ruptures, each one with a .sites attribute def rups(rupiter, sites): for rup in rupiter: rup.sites = sites yield rup if getattr(src, 'location', None): # finite site effects are averaged for sites over the # pointsource_distance from the rupture (if any) for r, s in self._cps_rups(src, sites): yield from rups(r, s) else: # just add the ruptures yield from rups(self._ruptures(src), sites) def _cps_rups(self, src, sites, point_rup=False): if src.count_nphc() == 1: # nothing to collapse for rup in src.iruptures(point_rup): yield self._ruptures(src, rup.mag, point_rup), sites return fewsites = len(sites) <= self.max_sites_disagg cdist = sites.get_cdist(src.location) for rup in src.iruptures(point_rup): psdist = self.pointsource_distance + _delta_pr(rup) close = sites.filter(cdist <= psdist) far = sites.filter(cdist > psdist) if fewsites: if close is None: # all is far, common for small mag yield [rup], sites else: # something is close yield self._ruptures(src, rup.mag, point_rup), sites else: # many sites if close is None: # all is far yield [rup], far elif far is None: # all is close yield self._ruptures(src, rup.mag, point_rup), close else: # some sites are far, some are close yield [rup], far yield self._ruptures(src, rup.mag, point_rup), close def get_pmap(self, ctxs, probmap=None): """ :param ctxs: a list of contexts :param probmap: if not None, update it :returns: a new ProbabilityMap if probmap is None """ tom = self.tom rup_indep = self.rup_indep if probmap is None: # create new pmap pmap = ProbabilityMap(self.imtls.size, len(self.gsims)) else: # update passed probmap pmap = probmap for block in block_splitter(ctxs, 20_000, len): for ctx, poes in self.gen_poes(block): # pnes and poes of shape (N, L, G) with self.pne_mon: pnes = get_probability_no_exceedance(ctx, poes, tom) for sid, pne in zip(ctx.sids, pnes): probs = pmap.setdefault(sid, self.rup_indep).array if rup_indep: probs *= pne else: # rup_mutex probs += (1. - pne) * ctx.weight if probmap is None: # return the new pmap return ~pmap if rup_indep else pmap
class ContextMaker(object): """ A class to manage the creation of contexts for distances, sites, rupture. """ REQUIRES = ['DISTANCES', 'SITES_PARAMETERS', 'RUPTURE_PARAMETERS'] def __init__(self, trt, gsims, param=None, monitor=Monitor()): param = param or {} self.max_sites_disagg = param.get('max_sites_disagg', 10) self.collapse_level = param.get('collapse_level', False) self.point_rupture_bins = param.get('point_rupture_bins', 20) self.num_probs_occur = param.get('num_probs_occur', 0) self.trt = trt self.gsims = gsims self.maximum_distance = (param.get('maximum_distance') or IntegrationDistance({})) self.trunclevel = param.get('truncation_level') self.effect = param.get('effect') for req in self.REQUIRES: reqset = set() for gsim in gsims: reqset.update(getattr(gsim, 'REQUIRES_' + req)) setattr(self, 'REQUIRES_' + req, reqset) # self.pointsource_distance is a dict mag -> dist, possibly empty if param.get('pointsource_distance'): self.pointsource_distance = param['pointsource_distance'][trt] else: self.pointsource_distance = {} self.filter_distance = 'rrup' if 'imtls' in param: self.imtls = param['imtls'] elif 'hazard_imtls' in param: self.imtls = DictArray(param['hazard_imtls']) else: self.imtls = {} self.imts = [imt_module.from_string(imt) for imt in self.imtls] self.reqv = param.get('reqv') if self.reqv is not None: self.REQUIRES_DISTANCES.add('repi') self.mon = monitor self.ctx_mon = monitor('make_contexts', measuremem=False) self.loglevels = DictArray(self.imtls) self.shift_hypo = param.get('shift_hypo') with warnings.catch_warnings(): # avoid RuntimeWarning: divide by zero encountered in log warnings.simplefilter("ignore") for imt, imls in self.imtls.items(): if imt != 'MMI': self.loglevels[imt] = numpy.log(imls) def from_srcs(self, srcs, site1): # used in disagg.disaggregation """ :returns: a list RuptureContexts """ grp_ids = [0] allctxs = [] for src in srcs: ctxs = [] for rup in src.iter_ruptures(shift_hypo=self.shift_hypo): ctxs.append(self.make_rctx(rup)) allctxs.extend(self.make_ctxs(ctxs, site1, grp_ids, False)) return allctxs def filter(self, sites, rup): """ Filter the site collection with respect to the rupture. :param sites: Instance of :class:`openquake.hazardlib.site.SiteCollection`. :param rup: Instance of :class:`openquake.hazardlib.source.rupture.BaseRupture` :returns: (filtered sites, distance context) """ distances = get_distances(rup, sites, self.filter_distance) mdist = self.maximum_distance(self.trt, rup.mag) mask = distances <= mdist if mask.any(): sites, distances = sites.filter(mask), distances[mask] else: raise FarAwayRupture('%d: %d km' % (rup.rup_id, distances.min())) return sites, DistancesContext([(self.filter_distance, distances)]) def get_dctx(self, sites, rup): """ :param sites: :class:`openquake.hazardlib.site.SiteCollection` :param rup: :class:`openquake.hazardlib.source.rupture.BaseRupture` :returns: :class:`DistancesContext` """ distances = get_distances(rup, sites, self.filter_distance) mdist = self.maximum_distance(self.trt, rup.mag) if (distances > mdist).all(): raise FarAwayRupture('%d: %d km' % (rup.rup_id, distances.min())) return DistancesContext([(self.filter_distance, distances)]) def make_rctx(self, rupture): """ Add .REQUIRES_RUPTURE_PARAMETERS to the rupture """ ctx = RuptureContext() vars(ctx).update(vars(rupture)) for param in self.REQUIRES_RUPTURE_PARAMETERS: if param == 'mag': value = rupture.mag elif param == 'strike': value = rupture.surface.get_strike() elif param == 'dip': value = rupture.surface.get_dip() elif param == 'rake': value = rupture.rake elif param == 'ztor': value = rupture.surface.get_top_edge_depth() elif param == 'hypo_lon': value = rupture.hypocenter.longitude elif param == 'hypo_lat': value = rupture.hypocenter.latitude elif param == 'hypo_depth': value = rupture.hypocenter.depth elif param == 'width': value = rupture.surface.get_width() else: raise ValueError('%s requires unknown rupture parameter %r' % (type(self).__name__, param)) setattr(ctx, param, value) return ctx def make_contexts(self, sites, rupture, filt=True): """ Filter the site collection with respect to the rupture and create context objects. :param sites: Instance of :class:`openquake.hazardlib.site.SiteCollection`. :param rupture: Instance of :class:`openquake.hazardlib.source.rupture.BaseRupture` :param boolean filt: If True filter the sites :returns: Tuple of two items: sites and distances context. :raises ValueError: If any of declared required parameters (site, rupture and distance parameters) is unknown. """ if filt: sites, dctx = self.filter(sites, rupture) else: dctx = self.get_dctx(sites, rupture) for param in self.REQUIRES_DISTANCES - set([self.filter_distance]): distances = get_distances(rupture, sites, param) setattr(dctx, param, distances) reqv_obj = (self.reqv.get(self.trt) if self.reqv else None) if reqv_obj and isinstance(rupture.surface, PlanarSurface): reqv = reqv_obj.get(dctx.repi, rupture.mag) if 'rjb' in self.REQUIRES_DISTANCES: dctx.rjb = reqv if 'rrup' in self.REQUIRES_DISTANCES: dctx.rrup = numpy.sqrt(reqv**2 + rupture.hypocenter.depth**2) return self.make_rctx(rupture), sites, dctx def make_ctxs(self, ruptures, sites, grp_ids, filt): """ :returns: a list of fat RuptureContexts """ ctxs = [] for rup in ruptures: try: ctx, r_sites, dctx = self.make_contexts(sites, rup, filt) except FarAwayRupture: continue for par in self.REQUIRES_SITES_PARAMETERS: setattr(ctx, par, r_sites[par]) ctx.sids = r_sites.sids for par in self.REQUIRES_DISTANCES | {'rrup'}: setattr(ctx, par, getattr(dctx, par)) ctx.grp_ids = grp_ids if not filt: closest = rup.surface.get_closest_points(sites) ctx.clon = closest.lons ctx.clat = closest.lats ctxs.append(ctx) return ctxs def collapse_the_ctxs(self, ctxs): """ Collapse contexts with similar parameters and distances. :param ctxs: a list of pairs (rup, dctx) :returns: collapsed contexts """ if self.collapse_level >= 3: # hack, ignore everything except mag rrp = ['mag'] rnd = 0 # round distances to 1 km else: rrp = self.REQUIRES_RUPTURE_PARAMETERS rnd = 1 # round distances to 100 m def params(ctx): lst = [] for par in rrp: lst.append(getattr(ctx, par)) for dst in self.REQUIRES_DISTANCES: lst.extend(numpy.round(getattr(ctx, dst), rnd)) return tuple(lst) out = [] for values in groupby(ctxs, params).values(): out.extend(_collapse(values)) return out def max_intensity(self, sitecol1, mags, dists): """ :param sitecol1: a SiteCollection instance with a single site :param mags: a sequence of magnitudes :param dists: a sequence of distances :returns: an array of GMVs of shape (#mags, #dists) """ assert len(sitecol1) == 1, sitecol1 nmags, ndists = len(mags), len(dists) gmv = numpy.zeros((nmags, ndists)) for m, d in itertools.product(range(nmags), range(ndists)): mag, dist = mags[m], dists[d] ctx = RuptureContext() for par in self.REQUIRES_RUPTURE_PARAMETERS: setattr(ctx, par, 0) for dst in self.REQUIRES_DISTANCES: setattr(ctx, dst, numpy.array([dist])) for par in self.REQUIRES_SITES_PARAMETERS: setattr(ctx, par, getattr(sitecol1, par)) ctx.sids = sitecol1.sids ctx.mag = mag ctx.width = .01 # 10 meters to avoid warnings in abrahamson_2014 means = [] for gsim in self.gsims: try: mean = ctx.get_mean_std( # shape (2, N, M, G) -> M self.imts, [gsim])[0, 0, :, 0] except ValueError: # magnitude outside of supported range continue means.append(mean.max()) if means: gmv[m, d] = numpy.exp(max(means)) return gmv