def run_preclassical(csm, oqparam, h5): """ :param csm: a CompositeSourceModel with attribute .srcfilter :param oqparam: the parameters in job.ini file :param h5: a DataStore instance """ logging.info('Sending %s', csm.sitecol) # do nothing for atomic sources except counting the ruptures for src in csm.get_sources(atomic=True): src.num_ruptures = src.count_ruptures() src.nsites = len(csm.sitecol) # run preclassical for non-atomic sources sources_by_grp = groupby( csm.get_sources(atomic=False), lambda src: (src.grp_id, msr_name(src))) param = dict(maximum_distance=oqparam.maximum_distance, pointsource_distance=oqparam.pointsource_distance, ps_grid_spacing=oqparam.ps_grid_spacing, split_sources=oqparam.split_sources) srcfilter = SourceFilter( csm.sitecol.reduce(10000) if csm.sitecol else None, oqparam.maximum_distance) res = parallel.Starmap( preclassical, ((srcs, srcfilter, param) for srcs in sources_by_grp.values()), h5=h5, distribute=None if len(sources_by_grp) > 1 else 'no').reduce() if res and res['before'] != res['after']: logging.info('Reduced the number of sources from {:_d} -> {:_d}'. format(res['before'], res['after'])) if res and h5: csm.update_source_info(res['calc_times'], nsites=True) for grp_id, srcs in res.items(): # srcs can be empty if the minimum_magnitude filter is on if srcs and not isinstance(grp_id, str): newsg = SourceGroup(srcs[0].tectonic_region_type) newsg.sources = srcs csm.src_groups[grp_id] = newsg # sanity check for sg in csm.src_groups: for src in sg: assert src.num_ruptures assert src.nsites # store ps_grid data, if any for key, sources in res.items(): if isinstance(key, str) and key.startswith('ps_grid/'): arrays = [] for ps in sources: if hasattr(ps, 'location'): lonlats = [ps.location.x, ps.location.y] for src in getattr(ps, 'pointsources', []): lonlats.extend([src.location.x, src.location.y]) arrays.append(F32(lonlats)) h5[key] = arrays
def read_source_groups(fname): """ :param fname: a path to a source model XML file :return: a list of SourceGroup objects containing source nodes """ smodel = nrml.read(fname).sourceModel src_groups = [] if smodel[0].tag.endswith('sourceGroup'): # NRML 0.5 format for sg_node in smodel: sg = SourceGroup(sg_node['tectonicRegion']) sg.sources = sg_node.nodes src_groups.append(sg) else: # NRML 0.4 format: smodel is a list of source nodes src_groups.extend(SourceGroup.collect(smodel)) return src_groups
def test_mutually_exclusive_ruptures(self): # Test the calculation of hazard curves using mutually exclusive # ruptures for a single source gsim_by_trt = [SadighEtAl1997()] rupture = _create_rupture(10., 6.) data = [(rupture, PMF([(0.7, 0), (0.3, 1)])), (rupture, PMF([(0.6, 0), (0.4, 1)]))] data[0][0].weight = 0.5 data[1][0].weight = 0.5 src = NonParametricSeismicSource('0', 'test', "Active Shallow Crust", data) src.id = 0 src.grp_id = 0 src.trt_smr = 0 src.mutex_weight = 1 group = SourceGroup(src.tectonic_region_type, [src], 'test', 'mutex', 'mutex') param = dict(imtls=self.imtls, src_interdep=group.src_interdep, rup_interdep=group.rup_interdep, grp_probability=group.grp_probability) cmaker = ContextMaker(src.tectonic_region_type, gsim_by_trt, param) crv = classical(group, self.sites, cmaker)['pmap'][0] npt.assert_almost_equal(numpy.array([0.35000, 0.32497, 0.10398]), crv.array[:, 0], decimal=4)
def test_mutually_exclusive_ruptures(self): # Test the calculation of hazard curves using mutually exclusive # ruptures for a single source gsim_by_trt = [SadighEtAl1997()] rupture = _create_rupture(10., 6.) data = [(rupture, PMF([(0.7, 0), (0.3, 1)])), (rupture, PMF([(0.6, 0), (0.4, 1)]))] print(data[0][0]) data[0][0].weight = 0.5 data[1][0].weight = 0.5 print(data[0][0].weight) src = NonParametricSeismicSource('0', 'test', TRT.ACTIVE_SHALLOW_CRUST, data) src.src_group_id = 0 src.mutex_weight = 1 group = SourceGroup(src.tectonic_region_type, [src], 'test', 'mutex', 'mutex') param = dict(imtls=self.imtls, filter_distance='rjb', src_interdep=group.src_interdep, rup_interdep=group.rup_interdep, grp_probability=group.grp_probability) crv = classical(group, self.sites, gsim_by_trt, param)['pmap'][0] npt.assert_almost_equal(numpy.array([0.35000, 0.32497, 0.10398]), crv[0].array[:, 0], decimal=4)
def calc_hazard_curves( groups, ss_filter, imtls, gsim_by_trt, truncation_level=None, apply=Sequential.apply): """ Compute hazard curves on a list of sites, given a set of seismic source groups and a dictionary of ground shaking intensity models (one per tectonic region type). Probability of ground motion exceedance is computed in different ways depending if the sources are independent or mutually exclusive. :param groups: A sequence of groups of seismic sources objects (instances of of :class:`~openquake.hazardlib.source.base.BaseSeismicSource`). :param ss_filter: A source filter over the site collection or the site collection itself :param imtls: Dictionary mapping intensity measure type strings to lists of intensity measure levels. :param gsim_by_trt: Dictionary mapping tectonic region types (members of :class:`openquake.hazardlib.const.TRT`) to :class:`~openquake.hazardlib.gsim.base.GMPE` or :class:`~openquake.hazardlib.gsim.base.IPE` objects. :param truncation_level: Float, number of standard deviations for truncation of the intensity distribution. :param maximum_distance: The integration distance, if any :returns: An array of size N, where N is the number of sites, which elements are records with fields given by the intensity measure types; the size of each field is given by the number of levels in ``imtls``. """ # This is ensuring backward compatibility i.e. processing a list of # sources if not isinstance(groups[0], SourceGroup): # sent a list of sources dic = groupby(groups, operator.attrgetter('tectonic_region_type')) groups = [SourceGroup(trt, dic[trt], 'src_group', 'indep', 'indep') for trt in dic] if hasattr(ss_filter, 'sitecol'): # a filter, as it should be sitecol = ss_filter.sitecol else: # backward compatibility, a site collection was passed sitecol = ss_filter ss_filter = SourceFilter(sitecol, {}) imtls = DictArray(imtls) pmap = ProbabilityMap(len(imtls.array), 1) # Processing groups with homogeneous tectonic region for group in groups: if group.src_interdep == 'mutex': # do not split the group pmap |= pmap_from_grp( group, ss_filter, imtls, gsim_by_trt, truncation_level) else: # split the group and apply `pmap_from_grp` in parallel pmap |= apply( pmap_from_grp, (group, ss_filter, imtls, gsim_by_trt, truncation_level), weight=operator.attrgetter('weight')).reduce(operator.or_) return pmap.convert(imtls, len(sitecol.complete))
def pmap_from_grp(sources, source_site_filter, imtls, gsims, truncation_level=None, bbs=(), monitor=Monitor()): """ Compute the hazard curves for a set of sources belonging to the same tectonic region type for all the GSIMs associated to that TRT. The arguments are the same as in :func:`calc_hazard_curves`, except for ``gsims``, which is a list of GSIM instances. :returns: a ProbabilityMap instance """ if isinstance(sources, SourceGroup): group = sources sources = group.sources trt = sources[0].tectonic_region_type else: # list of sources trt = sources[0].tectonic_region_type group = SourceGroup(trt, sources, 'src_group', 'indep', 'indep') try: maxdist = source_site_filter.integration_distance[trt] except: maxdist = source_site_filter.integration_distance if hasattr(gsims, 'keys'): # dictionary trt -> gsim gsims = [gsims[trt]] with GroundShakingIntensityModel.forbid_instantiation(): imtls = DictArray(imtls) cmaker = ContextMaker(gsims, maxdist) ctx_mon = monitor('making contexts', measuremem=False) pne_mon = monitor('computing poes', measuremem=False) disagg_mon = monitor('get closest points', measuremem=False) src_indep = group.src_interdep == 'indep' pmap = ProbabilityMap(len(imtls.array), len(gsims)) pmap.calc_times = [] # pairs (src_id, delta_t) pmap.grp_id = sources[0].src_group_id for src, s_sites in source_site_filter(sources): t0 = time.time() poemap = poe_map(src, s_sites, imtls, cmaker, truncation_level, bbs, group.rup_interdep == 'indep', ctx_mon, pne_mon, disagg_mon) if src_indep: # usual composition of probabilities pmap |= poemap else: # mutually exclusive probabilities weight = float(group.srcs_weights[src.source_id]) for sid in poemap: pmap[sid] += poemap[sid] * weight pmap.calc_times.append( (src.source_id, len(s_sites), time.time() - t0)) # storing the number of contributing ruptures too pmap.eff_ruptures = {pmap.grp_id: pne_mon.counts} return pmap
def test_hazard_curve_B(self): # Test simple calculation group = SourceGroup( TRT.ACTIVE_SHALLOW_CRUST, [self.src2], 'test', 'indep', 'indep') groups = [group] curves = calc_hazard_curves(groups, self.sites, self.imtls, self.gsim_by_trt, truncation_level=None) npt.assert_almost_equal(numpy.array([0.30000, 0.2646, 0.0625]), curves[0][0], decimal=4)
def create(label, rupture_hdf5_fname, output_folder, investigation_t, trt=TRT.SUBDUCTION_INTRASLAB): """ :param label: A string identifying the source :param rupture_hdf5_fname: Name of the .hdf5 file containing the ruptures :param output_folder: Folder where to write the .xl files :param investigation_t: Investigation time in years :param trt: Tectonic region type label """ # Open the input .hdf5 file with the ruptures f = h5py.File(rupture_hdf5_fname, 'r') if not os.path.exists(output_folder): os.mkdir(output_folder) # Create xml for mag in f['ruptures'].keys(): # Check the number of ruptures defined for the current magnitude value grp = f['ruptures'][mag] if len(grp) < 1: tmps = 'Skipping ruptures for magnitude {:.2f}'.format(float(mag)) logging.warning(tmps) continue # Set the name of the output nrml file fxml = os.path.join(output_folder, '{:s}.xml'.format(mag)) # Set the source ID mags = re.sub('\\.', 'pt', mag) sid = 'src_{:s}_{:s}'.format(label, mags) name = 'Ruptures for mag bin {:s}'.format(mags) # Creates a non-parametric seismic source src = create_source(grp, float(mag), sid, name, trt) # Create source group sgrp = SourceGroup(trt, [src]) # Create source model name = 'Source model for {:s} magnitude {:s}'.format(label, mags) mdl = SourceModel([sgrp], name, investigation_t) # Write source model write_source_model(fxml, mdl, mag) f.close()
def test_hazard_curve_B(self): # independent sources in a group group = SourceGroup("Active Shallow Crust", [self.src2], 'test', 'indep', 'indep') groups = [group] curves = calc_hazard_curves(groups, self.sites, self.imtls, self.gsim_by_trt, truncation_level=None, investigation_time=1) npt.assert_almost_equal(numpy.array([0.30000, 0.2785, 0.0891]), curves[0][0], decimal=4)
def test_mutually_exclusive_ruptures(self): # Test the calculation of hazard curves using mutually exclusive # ruptures for a single source gsim_by_trt = [SadighEtAl1997()] rupture = _create_rupture(10., 6.) data = [(rupture, PMF([(0.7, 0), (0.3, 1)])), (rupture, PMF([(0.6, 0), (0.4, 1)]))] src = NonParametricSeismicSource('0', 'test', TRT.ACTIVE_SHALLOW_CRUST, data) group = SourceGroup( src.tectonic_region_type, [src], 'test', 'indep', 'mutex') param = dict(imtls=self.imtls) crv = pmap_from_grp(group, self.sites, gsim_by_trt, param)[0] npt.assert_almost_equal(numpy.array([0.35000, 0.32497, 0.10398]), crv.array[:, 0], decimal=4)
def create(label, rupture_hdf5_fname, output_folder, investigation_t): """ :param label: :param rupture_hdf5_fname: :param output_folder: """ # # f = h5py.File(rupture_hdf5_fname, 'r') if not os.path.exists(output_folder): os.mkdir(output_folder) # # trt = TRT.SUBDUCTION_INTRASLAB for mag in f['ruptures'].keys(): # # check the number of ruptures defined for the current magnitude value grp = f['ruptures'][mag] if len(grp) < 1: tmps = 'Skipping ruptures for magnitude {:.2f}'.format(float(mag)) print(tmps) logging.warning(tmps) continue # # set the name of the output nrml file fnrml = os.path.join(output_folder, '{:s}.nrml'.format(mag)) # # source ID mags = re.sub('\\.', 'pt', mag) sid = 'src_{:s}_{:s}'.format(label, mags) name = 'Ruptures for mag bin {:s}'.format(mags) # # creates a non-parametric seismic source src = create_nrml_source(grp, float(mag), sid, name, trt) # # create source group sgrp = SourceGroup(trt, [src]) # # create source model name = 'Source model for {:s} magnitude {:s}'.format(label, mags) mdl = SourceModel([sgrp], name, investigation_t) # # write source model write_source_model(fnrml, mdl, mag) f.close() print('Done')
def calc_hazard_curves(groups, srcfilter, imtls, gsim_by_trt, truncation_level=None, apply=sequential_apply, reqv=None, **kwargs): """ Compute hazard curves on a list of sites, given a set of seismic source groups and a dictionary of ground shaking intensity models (one per tectonic region type). Probability of ground motion exceedance is computed in different ways depending if the sources are independent or mutually exclusive. :param groups: A sequence of groups of seismic sources objects (instances of of :class:`~openquake.hazardlib.source.base.BaseSeismicSource`). :param srcfilter: A source filter over the site collection or the site collection itself :param imtls: Dictionary mapping intensity measure type strings to lists of intensity measure levels. :param gsim_by_trt: Dictionary mapping tectonic region types (members of :class:`openquake.hazardlib.const.TRT`) to :class:`~openquake.hazardlib.gsim.base.GMPE` or :class:`~openquake.hazardlib.gsim.base.IPE` objects. :param truncation_level: Float, number of standard deviations for truncation of the intensity distribution. :param apply: apply function to use (default sequential_apply) :param reqv: If not None, an instance of RjbEquivalent :returns: An array of size N, where N is the number of sites, which elements are records with fields given by the intensity measure types; the size of each field is given by the number of levels in ``imtls``. """ # This is ensuring backward compatibility i.e. processing a list of # sources if not isinstance(groups[0], SourceGroup): # sent a list of sources odic = groupby(groups, operator.attrgetter('tectonic_region_type')) groups = [ SourceGroup(trt, odic[trt], 'src_group', 'indep', 'indep') for trt in odic ] idx = 0 span = None for i, grp in enumerate(groups): for src in grp: tom = getattr(src, 'temporal_occurrence_model', None) if tom: span = tom.time_span src.weight = src.count_ruptures() src.grp_id = i src.id = idx idx += 1 imtls = DictArray(imtls) shift_hypo = kwargs['shift_hypo'] if 'shift_hypo' in kwargs else False param = dict(imtls=imtls, truncation_level=truncation_level, reqv=reqv, cluster=grp.cluster, shift_hypo=shift_hypo, investigation_time=kwargs.get('investigation_time', span)) pmap = ProbabilityMap(imtls.size, 1) # Processing groups with homogeneous tectonic region mon = Monitor() sitecol = getattr(srcfilter, 'sitecol', srcfilter) for group in groups: trt = group.trt if sitecol is not srcfilter: param['maximum_distance'] = srcfilter.integration_distance(trt) cmaker = ContextMaker(trt, [gsim_by_trt[trt]], param, mon) if group.atomic: # do not split it = [classical(group, sitecol, cmaker)] else: # split the group and apply `classical` in parallel it = apply(classical, (group.sources, sitecol, cmaker), weight=operator.attrgetter('weight')) for dic in it: pmap |= dic['pmap'] return pmap.convert(imtls, len(sitecol.complete))
def pt2fault_distance(pt_sources, fault_sources, min_distance=5.0, filename='source_model.xml', buffer_distance=100., nrml_version='04', name=None): """Calculate distances from a pt source rupture plane to the fault sources to then reduce Mmax on events that are within a certain distance :param pt_sources: list of PointSource objects :param fault_sources: List of FaultSource objects :param min_distance: Minimum distance (km) within which we want a point source rupture to be from a fault. :param filename: Name of output nrml file for revised pt source model :param buffer_distance: Km, initial filter to only process pts within this distance from the fault """ if name is None: name = filename[:-4] + '_geom_filtered' id_index = 0 # We need to re-number all sources to avoid duplicate ids # Extract the points of the fault source mesh fault_lons = [] fault_lats = [] fault_depths = [] for fault in fault_sources: whole_fault_surface = SimpleFaultSurface.from_fault_data( fault.fault_trace, fault.upper_seismogenic_depth, fault.lower_seismogenic_depth, fault.dip, fault.rupture_mesh_spacing) fault_lons.append(whole_fault_surface.mesh.lons.flatten()) fault_lats.append(whole_fault_surface.mesh.lats.flatten()) fault_depths.append(whole_fault_surface.mesh.depths.flatten()) fault_lons = np.concatenate(fault_lons) fault_lats = np.concatenate(fault_lats) fault_depths = np.concatenate(fault_depths) min_fault_lon = np.min(fault_lons) max_fault_lon = np.max(fault_lons) min_fault_lat = np.min(fault_lats) max_fault_lat = np.max(fault_lats) # Generate ruptures for point sources minimum_distance_list = [] revised_point_sources = { 'Cratonic': [], 'Non_cratonic': [], 'Extended': [], 'Banda': [] } for pt in pt_sources: print 'Looping over point sources' # For speeding things up filter based on initial distances # to find points very far from or very close to a fault mfd_type = type(pt.mfd).__name__ pt_depths = [] for probs, depths in pt.hypocenter_distribution.data: pt_depths.append(depths) np_probs = [] np_list = [] for prob, nodal_plane in pt.nodal_plane_distribution.data: np_probs.append(prob) np_list.append(nodal_plane) centroid_distances = [] for pt_depth in pt_depths: centroid_distances.append( distance(pt.location.longitude, pt.location.latitude, pt_depth, fault_lons, fault_lats, fault_depths)) centroid_distances = np.array(centroid_distances).flatten() # print 'Minimum distance', min(centroid_distances) # print 'Maximum distance', max(centroid_distances) if (min(centroid_distances)) > buffer_distance: # Keep point as it, not within buffer distance of any faults revised_point_sources[pt.tectonic_region_type].append(pt) continue if (min(centroid_distances)) < min_distance: # Discard point sources as too close to a fault print 'Discarding point source, too close to a fault' continue rupture_mags = [] rupture_lons = [] rupture_lats = [] rupture_depths = [] rupture_strikes = [] rupture_dips = [] ruptures = pt.iter_ruptures() for rupture in ruptures: rupture_mags.append(rupture.mag) rupture_lons.append(rupture.surface.corner_lons) rupture_lats.append(rupture.surface.corner_lats) rupture_depths.append(rupture.surface.corner_depths) rupture_strikes.append(rupture.surface.strike) rupture_dips.append(rupture.surface.dip) rupture_mags = np.array(rupture_mags).flatten() # make the same length as the corners rupture_mags = np.repeat(rupture_mags, 4) rupture_strikes = np.repeat(rupture_strikes, 4) rupture_dips = np.repeat(rupture_dips, 4) rupture_lons = np.array(rupture_lons).flatten() rupture_lats = np.array(rupture_lats).flatten() rupture_depths = np.array(rupture_depths).flatten() print 'Doing meshgrid' lons1, lons2 = np.meshgrid(fault_lons, rupture_lons) lats1, lats2 = np.meshgrid(fault_lats, rupture_lats) depths1, depths2 = np.meshgrid(fault_depths, rupture_depths) # Calculate distance from pt to all fault print 'Distance calculations' distances = distance(lons1, lats1, depths1, lons2, lats2, depths2) closest_distance_to_faults = np.min(distances) print 'Shortest pt to fault distance is', closest_distance_to_faults minimum_distance_list.append(closest_distance_to_faults) # Find where the distance is less than the threshold min_distance too_close_lons = lons2[np.where(distances < min_distance)] too_close_lats = lats2[np.where(distances < min_distance)] if too_close_lons.size > 0: lon_indices = np.where(np.in1d(rupture_lons, too_close_lons))[0] lat_indices = np.where(np.in1d(rupture_lats, too_close_lats))[0] too_close_mags = rupture_mags[np.intersect1d( lon_indices, lat_indices)] too_close_strikes = rupture_strikes[np.intersect1d( lon_indices, lat_indices)] too_close_dips = rupture_dips[np.intersect1d( lon_indices, lat_indices)] # print 'Magnitudes of rupture close to fault', too_close_mags # print 'Strikes of rupture close to fault', too_close_strikes # print 'Dips of rupture close to fault', too_close_dips unique_strikes = np.unique(rupture_strikes) unique_dips = np.unique(rupture_dips) src_name_index = 0 for prob, nodal_plane in pt.nodal_plane_distribution.data: id_index += 1 src_name_index += 1 # We are now splitting the source into many with different # combinations of Mmaxs and nodal planes new_pt = copy.deepcopy(pt) new_pt.source_id = "%i" % id_index new_pt.name = new_pt.name + ("_%i" % src_name_index) new_np = NodalPlane(nodal_plane.strike, nodal_plane.dip, nodal_plane.rake) new_np_distribution = PMF([ (1.0, new_np) ]) # weight of nodal plane is 1 as making # a separate source # Calculate new rates based on probability of original nodal plane new_pt.nodal_plane_distribution = new_np_distribution if mfd_type == 'TruncatedGRMFD': b_val = pt.mfd.b_val # rescale a value in log sapce a_val = np.log10(np.power(10, pt.mfd.a_val) * prob) #*area_src_weight)) new_pt.mfd.modify_set_ab(a_val, b_val) elif mfd_type == 'EvenlyDiscretizedMFD': mag_bins, rates = zip( *pt.mfd.get_annual_occurrence_rates()) mag_bins = np.array(mag_bins) rates = np.array(rates) new_rates = rates * prob #*area_src_weight) new_pt.mfd.modify_set_mfd(new_pt.mfd.min_mag, new_pt.mfd.bin_width, list(new_rates)) else: msg = 'Weighting method for mfd type %s not yet defined' % mfd_type raise (msg) pair_index = np.where( np.logical_and(too_close_strikes == nodal_plane.strike, too_close_dips == nodal_plane.dip)) # Deal with intersecting cases if len(pair_index[0]) > 0: intersecting_magnitudes = too_close_mags[pair_index] minimum_magnitude_intersecting_fault = min( intersecting_magnitudes) if minimum_magnitude_intersecting_fault >= \ (pt.mfd.min_mag + pt.mfd.bin_width): new_mmax = minimum_magnitude_intersecting_fault - \ pt.mfd.bin_width if mfd_type == 'TruncatedGRMFD': new_pt.mfd.max_mag = new_mmax if mfd_type == 'EvenlyDiscretizedMFD': trimmed_rates = new_rates[np.where( mag_bins <= new_mmax)] else: print 'Minimum magnitude intersects fault, discarding source' continue else: pass # Append revised source for given nodal plane distribution to # list of revised sources print 'Appending revised source' revised_point_sources[pt.tectonic_region_type].append(new_pt) else: id_index += 1 pt.source_id = "%i" % id_index 'Appending original source' revised_point_sources[pt.tectonic_region_type].append(pt) if len(minimum_distance_list) > 0: print 'Overall minimum distance (km):', min(minimum_distance_list) # Write pts to source model on their own source_model_file = filename print 'Writing to source model file %s' % source_model_file if nrml_version == '04': source_list = [] for trt, sources in revised_point_sources.iteritems(): for source in sources: source_list.append(source) nodes = list(map(obj_to_node, sorted(source_list))) source_model = Node("sourceModel", {"name": name}, nodes=nodes) with open(source_model_file, 'wb') as f: nrml.write([source_model], f, '%s', xmlns=NAMESPACE) elif nrml_version == '05': source_group_list = [] id = 0 for trt, sources in revised_point_sources.iteritems(): source_group = SourceGroup(trt, sources=sources, id=id) id += 1 source_group_list.append(source_group) write_source_model(source_model_file, source_group_list, name=name) else: print 'Warning: nrml version not specfied, xml not created' # Write pts to source model with faults source_model_file = filename[:-4] + '_inc_faults.xml' name = name + '_inc_faults' write_combined_faults_points(revised_point_sources, fault_sources, source_model_file, name, nrml_version='04')
def write_combined_faults_points(point_sources, fault_sources, filename, name, nrml_version='04'): """Write pts and fault sources to file :param point_sources: list without trt or dict with trt key of point sources """ print 'Writing to source model file %s' % filename ps_id_index = 1 fs_id_index = 1 if nrml_version == '04': if type(point_sources) == dict: source_list = [] for trt, sources in point_sources.iteritems(): for source in sources: source.source_id = 'PS_%i' % ps_id_index source_list.append(source) ps_id_index += 1 # id_index = max(id_index, source.source_id) elif type(point_sources) == list: source_list = point_sources for source in source_list: source.source_id = 'PS_%i' % ps_id_index source_list.append(source) ps_id_index += 1 # id_index = max(id_index, source.source_id) for fault_source in fault_sources: # id_index += 1 fault_source.source_id = "FS_%i" % fs_id_index fs_id_index += 1 source_list.append(fault_source) nodes = list(map(obj_to_node, sorted(source_list))) source_model = Node("sourceModel", {"name": name}, nodes=nodes) with open(filename, 'wb') as f: nrml.write([source_model], f, '%s', xmlns=NAMESPACE) elif nrml_version == '05': if type(point_sources) == dict: source_group_list = [] id = 0 for trt, sources in point_sources.iteritems(): for source in sources: id_index = max(id_index, source.source_id) for trt, sources in point_sources.iteritems(): for fault_source in fault_sources: if fault_source.tectonic_region_type == trt: id_index += 1 fault_source.source_id = "%i" % id_index sources.append(fault_source) source_group = SourceGroup(trt, sources=sources, id=id) id += 1 source_group_list.append(source_group) write_source_model(filename, source_group_list, name=name) elif type(point_sources) == list: msg = 'Method not yet implemented for nrml version 0.5' raise (msg) else: print 'Warning: nrml version not specfied, xml not created'
def run_preclassical(csm, oqparam, h5): """ :param csm: a CompositeSourceModel with attribute .srcfilter :param oqparam: the parameters in job.ini file :param h5: a DataStore instance """ # do nothing for atomic sources except counting the ruptures for src in csm.get_sources(atomic=True): src.num_ruptures = src.count_ruptures() src.nsites = len(csm.sitecol) if csm.sitecol else 1 # run preclassical for non-atomic sources sources_by_grp = groupby(csm.get_sources(atomic=False), lambda src: (src.grp_id, msr_name(src))) param = dict(maximum_distance=oqparam.maximum_distance, pointsource_distance=oqparam.pointsource_distance, ps_grid_spacing=oqparam.ps_grid_spacing, split_sources=oqparam.split_sources) srcfilter = SourceFilter( csm.sitecol.reduce(10000) if csm.sitecol else None, oqparam.maximum_distance) if csm.sitecol: logging.info('Sending %s', srcfilter.sitecol) if oqparam.ps_grid_spacing: # produce a preclassical task for each group allargs = ((srcs, srcfilter, param) for srcs in sources_by_grp.values()) else: # produce many preclassical task maxw = sum(len(srcs) for srcs in sources_by_grp.values()) / ( oqparam.concurrent_tasks or 1) allargs = ((blk, srcfilter, param) for srcs in sources_by_grp.values() for blk in block_splitter(srcs, maxw)) res = parallel.Starmap( preclassical, allargs, h5=h5, distribute=None if len(sources_by_grp) > 1 else 'no').reduce() if res and res['before'] != res['after']: logging.info( 'Reduced the number of sources from {:_d} -> {:_d}'.format( res['before'], res['after'])) if res and h5: csm.update_source_info(res['calc_times'], nsites=True) acc = AccumDict(accum=0) code2cls = get_code2cls() for grp_id, srcs in res.items(): # srcs can be empty if the minimum_magnitude filter is on if srcs and not isinstance(grp_id, str): newsg = SourceGroup(srcs[0].tectonic_region_type) newsg.sources = srcs csm.src_groups[grp_id] = newsg for src in srcs: acc[src.code] += int(src.num_ruptures) for val, key in sorted((val, key) for key, val in acc.items()): cls = code2cls[key].__name__ logging.info('{} ruptures: {:_d}'.format(cls, val)) # sanity check for sg in csm.src_groups: for src in sg: assert src.num_ruptures assert src.nsites # store ps_grid data, if any for key, sources in res.items(): if isinstance(key, str) and key.startswith('ps_grid/'): arrays = [] for ps in sources: if hasattr(ps, 'location'): lonlats = [ps.location.x, ps.location.y] for src in getattr(ps, 'pointsources', []): lonlats.extend([src.location.x, src.location.y]) arrays.append(F32(lonlats)) h5[key] = arrays
def area2pt_source(area_source_file, sources=None, investigation_time=50, rupture_mesh_spacing=10., width_of_mfd_bin=0.1, area_source_discretisation=10., filename=None, nrml_version='04', name=None): """Calls OpenQuake parsers to read area source model from source_mode.xml type file, convert to point sources and write to a new nrml source model file. :params area_source_file: nrml format file of the area source :params discretisation: Grid size (km) for the area source discretisation, which defines the distance between resulting point sources. """ if sources is None: sources = nrml2sourcelist( area_source_file, investigation_time=investigation_time, rupture_mesh_spacing=rupture_mesh_spacing, width_of_mfd_bin=width_of_mfd_bin, area_source_discretisation=area_source_discretisation) if name is None: name = '%s_points' % filename new_pt_sources = {} for source in sources: pt_sources = area_to_point_sources(source) for pt in pt_sources: pt.source_id = pt.source_id.replace(':', '') pt.name = pt.name.replace(':', '_') try: new_pt_sources[pt.tectonic_region_type].append(pt) except KeyError: new_pt_sources[pt.tectonic_region_type] = [pt] # print [method for method in dir(pt) if callable(getattr(pt, method))] # print [attribute for attribute in dir(pt)] nrml_pt_file = area_source_file[:-4] + '_pts.xml' source_group_list = [] id = 0 for trt, sources in new_pt_sources.iteritems(): source_group = SourceGroup(trt, sources=sources, id=id) id += 1 source_group_list.append(source_group) if filename is not None: if nrml_version == '04': source_list = [] for trt, sources in new_pt_sources.iteritems(): for source in sources: source_list.append(source) nodes = list(map(obj_to_node, sorted(source_list))) source_model = Node("sourceModel", {"name": name}, nodes=nodes) with open(nrml_pt_file, 'wb') as f: nrml.write([source_model], f, '%s', xmlns=NAMESPACE) # This will write version 0.5 elif nrml_version == '05': write_source_model(nrml_pt_file, source_group_list, name=filename) else: print 'Warning: nrml version not specfied, xml not created' return source_group_list
def run_preclassical(calc): """ :param csm: a CompositeSourceModel :param oqparam: the parameters in job.ini file :param h5: a DataStore instance """ csm = calc.csm calc.datastore['trt_smrs'] = csm.get_trt_smrs() calc.datastore['toms'] = numpy.array( [sg.tom_name for sg in csm.src_groups], hdf5.vstr) cmakers = read_cmakers(calc.datastore, csm.full_lt) h5 = calc.datastore.hdf5 calc.sitecol = sites = csm.sitecol if csm.sitecol else None # do nothing for atomic sources except counting the ruptures atomic_sources = [] normal_sources = [] for sg in csm.src_groups: grp_id = sg.sources[0].grp_id if sg.atomic: cmakers[grp_id].set_weight(sg, sites) atomic_sources.extend(sg) else: normal_sources.extend(sg) # run preclassical for non-atomic sources sources_by_grp = groupby(normal_sources, lambda src: (src.grp_id, msr_name(src))) if csm.sitecol: logging.info('Sending %s', sites) smap = parallel.Starmap(preclassical, h5=h5) for (grp_id, msr), srcs in sources_by_grp.items(): pointsources, pointlike, others = [], [], [] for src in srcs: if hasattr(src, 'location'): pointsources.append(src) elif hasattr(src, 'nodal_plane_distribution'): pointlike.append(src) else: others.append(src) if calc.oqparam.ps_grid_spacing: if pointsources or pointlike: smap.submit((pointsources + pointlike, sites, cmakers[grp_id])) else: smap.submit_split((pointsources, sites, cmakers[grp_id]), 10, 100) for src in pointlike: # area, multipoint smap.submit(([src], sites, cmakers[grp_id])) smap.submit_split((others, sites, cmakers[grp_id]), 10, 100) normal = smap.reduce() if atomic_sources: # case_35 n = len(atomic_sources) atomic = AccumDict({'before': n, 'after': n}) for grp_id, srcs in groupby(atomic_sources, lambda src: src.grp_id).items(): atomic[grp_id] = srcs else: atomic = AccumDict() res = normal + atomic if res['before'] != res['after']: logging.info( 'Reduced the number of point sources from {:_d} -> {:_d}'.format( res['before'], res['after'])) acc = AccumDict(accum=0) code2cls = get_code2cls() for grp_id, srcs in res.items(): # srcs can be empty if the minimum_magnitude filter is on if srcs and not isinstance(grp_id, str) and grp_id not in atomic: # check if OQ_SAMPLE_SOURCES is set ss = os.environ.get('OQ_SAMPLE_SOURCES') if ss: logging.info('Sampled sources for group #%d', grp_id) srcs = general.random_filter(srcs, float(ss)) or [srcs[0]] newsg = SourceGroup(srcs[0].tectonic_region_type) newsg.sources = srcs csm.src_groups[grp_id] = newsg for src in srcs: assert src.weight assert src.num_ruptures acc[src.code] += int(src.num_ruptures) for val, key in sorted((val, key) for key, val in acc.items()): cls = code2cls[key].__name__ logging.info('{} ruptures: {:_d}'.format(cls, val)) source_data = zero_times(csm.get_sources()) calc.store_source_info(source_data) # store ps_grid data, if any for key, sources in res.items(): if isinstance(key, str) and key.startswith('ps_grid/'): arrays = [] for ps in sources: if hasattr(ps, 'location'): lonlats = [ps.location.x, ps.location.y] for src in getattr(ps, 'pointsources', []): lonlats.extend([src.location.x, src.location.y]) arrays.append(F32(lonlats)) h5[key] = arrays h5['full_lt'] = csm.full_lt return res
def pmap_from_grp(sources, src_filter, gsims, param, monitor=Monitor()): """ Compute the hazard curves for a set of sources belonging to the same tectonic region type for all the GSIMs associated to that TRT. The arguments are the same as in :func:`calc_hazard_curves`, except for ``gsims``, which is a list of GSIM instances. :returns: a ProbabilityMap instance """ if isinstance(sources, SourceGroup): group = sources sources = group.sources trt = sources[0].tectonic_region_type mutex_weight = { src.source_id: weight for src, weight in zip(group.sources, group.srcs_weights) } else: # list of sources trt = sources[0].tectonic_region_type group = SourceGroup(trt, sources, 'src_group', 'indep', 'indep') grp_id = sources[0].src_group_id maxdist = src_filter.integration_distance if hasattr(gsims, 'keys'): # dictionary trt -> gsim gsims = [gsims[trt]] srcs = [] for src in sources: if hasattr(src, '__iter__'): # MultiPointSource srcs.extend(src) else: srcs.append(src) del sources with GroundShakingIntensityModel.forbid_instantiation(): imtls = param['imtls'] trunclevel = param.get('truncation_level') cmaker = ContextMaker(gsims, maxdist) ctx_mon = monitor('making contexts', measuremem=False) pne_mons = [ monitor('%s.get_poes' % gsim, measuremem=False) for gsim in gsims ] src_indep = group.src_interdep == 'indep' pmap = ProbabilityMap(len(imtls.array), len(gsims)) pmap.calc_times = [] # pairs (src_id, delta_t) pmap.grp_id = grp_id for src, s_sites in src_filter(srcs): t0 = time.time() poemap = poe_map(src, s_sites, imtls, cmaker, trunclevel, ctx_mon, pne_mons, group.rup_interdep == 'indep') if src_indep: # usual composition of probabilities pmap |= poemap else: # mutually exclusive probabilities weight = mutex_weight[src.source_id] for sid in poemap: pcurve = pmap.setdefault(sid, 0) pcurve += poemap[sid] * weight pmap.calc_times.append( (src.source_id, src.weight, len(s_sites), time.time() - t0)) # storing the number of contributing ruptures too pmap.eff_ruptures = {pmap.grp_id: pne_mons[0].counts} if group.grp_probability is not None: return pmap * group.grp_probability return pmap
def calc_hazard_curves(groups, srcfilter, imtls, gsim_by_trt, truncation_level=None, apply=sequential_apply, filter_distance='rjb', reqv=None, **kwargs): """ Compute hazard curves on a list of sites, given a set of seismic source groups and a dictionary of ground shaking intensity models (one per tectonic region type). Probability of ground motion exceedance is computed in different ways depending if the sources are independent or mutually exclusive. :param groups: A sequence of groups of seismic sources objects (instances of of :class:`~openquake.hazardlib.source.base.BaseSeismicSource`). :param srcfilter: A source filter over the site collection or the site collection itself :param imtls: Dictionary mapping intensity measure type strings to lists of intensity measure levels. :param gsim_by_trt: Dictionary mapping tectonic region types (members of :class:`openquake.hazardlib.const.TRT`) to :class:`~openquake.hazardlib.gsim.base.GMPE` or :class:`~openquake.hazardlib.gsim.base.IPE` objects. :param truncation_level: Float, number of standard deviations for truncation of the intensity distribution. :param apply: apply function to use (default sequential_apply) :param filter_distance: The distance used to filter the ruptures (default rjb) :param reqv: If not None, an instance of RjbEquivalent :returns: An array of size N, where N is the number of sites, which elements are records with fields given by the intensity measure types; the size of each field is given by the number of levels in ``imtls``. """ # This is ensuring backward compatibility i.e. processing a list of # sources if not isinstance(groups[0], SourceGroup): # sent a list of sources odic = groupby(groups, operator.attrgetter('tectonic_region_type')) groups = [ SourceGroup(trt, odic[trt], 'src_group', 'indep', 'indep') for trt in odic ] # ensure the sources have the right src_group_id for i, grp in enumerate(groups): for src in grp: if src.src_group_id is None: src.src_group_id = i imtls = DictArray(imtls) shift_hypo = kwargs['shift_hypo'] if 'shift_hypo' in kwargs else False param = dict(imtls=imtls, truncation_level=truncation_level, filter_distance=filter_distance, reqv=reqv, cluster=grp.cluster, shift_hypo=shift_hypo) pmap = ProbabilityMap(len(imtls.array), 1) # Processing groups with homogeneous tectonic region gsim = gsim_by_trt[groups[0][0].tectonic_region_type] mon = Monitor() for group in groups: if group.atomic: # do not split it = [classical(group, srcfilter, [gsim], param, mon)] else: # split the group and apply `classical` in parallel it = apply(classical, (group.sources, srcfilter, [gsim], param, mon), weight=operator.attrgetter('weight')) for dic in it: for grp_id, pval in dic['pmap'].items(): pmap |= pval sitecol = getattr(srcfilter, 'sitecol', srcfilter) return pmap.convert(imtls, len(sitecol.complete))
def execute(self): """ Run in parallel `core_task(sources, sitecol, monitor)`, by parallelizing on the sources according to their weight and tectonic region type. """ oq = self.oqparam if oq.hazard_calculation_id and not oq.compare_with_classical: with util.read(self.oqparam.hazard_calculation_id) as parent: self.full_lt = parent['full_lt'] self.calc_stats() # post-processing return {} assert oq.max_sites_per_tile > oq.max_sites_disagg, ( oq.max_sites_per_tile, oq.max_sites_disagg) psd = self.set_psd() srcfilter = self.src_filter() performance.Monitor.save(self.datastore, 'srcfilter', srcfilter) srcs = self.csm.get_sources(atomic=False) if srcs: res = parallel.Starmap.apply(preclassical, (srcs, self.params), concurrent_tasks=oq.concurrent_tasks or 1, h5=self.datastore.hdf5).reduce() if oq.calculation_mode == 'preclassical': self.store_source_info(res['calc_times'], nsites=True) self.datastore['full_lt'] = self.csm.full_lt self.datastore.swmr_on() # fixes HDF5 error in build_hazard return self.update_source_info(res['calc_times'], nsites=True) sources_by_grp = groupby(res['sources'], operator.attrgetter('grp_id')) else: for src in self.csm.get_sources(atomic=True): src.num_ruptures = src.count_ruptures() src.nsites = self.N sources_by_grp = {} self.csm.src_groups = [sg for sg in self.csm.src_groups if sg.atomic] if oq.ps_grid_spacing: smap = parallel.Starmap( grid_point_sources, h5=self.datastore.hdf5, distribute=None if len(sources_by_grp) > 1 else 'no') for grp_id, sources in sources_by_grp.items(): smap.submit((sources, oq.ps_grid_spacing)) dic = smap.reduce() before, after = 0, 0 for grp_id, sources in sources_by_grp.items(): before += len(sources) after += len(dic[grp_id]) sg = SourceGroup(sources[0].tectonic_region_type) sg.sources = dic[grp_id] self.csm.src_groups.append(sg) logging.info('Reduced point sources %d->%d', before, after) else: for grp_id, sources in sources_by_grp.items(): sg = SourceGroup(sources[0].tectonic_region_type) sg.sources = sources self.csm.src_groups.append(sg) smap = parallel.Starmap(classical, h5=self.datastore.hdf5) self.submit_tasks(smap) acc0 = self.acc0() # create the rup/ datasets BEFORE swmr_on() self.datastore.swmr_on() smap.h5 = self.datastore.hdf5 self.calc_times = AccumDict(accum=numpy.zeros(3, F32)) try: acc = smap.reduce(self.agg_dicts, acc0) self.store_rlz_info(acc.eff_ruptures) finally: source_ids = self.store_source_info(self.calc_times) if self.by_task: logging.info('Storing by_task information') num_tasks = max(self.by_task) + 1, er = self.datastore.create_dset('by_task/eff_ruptures', U32, num_tasks) es = self.datastore.create_dset('by_task/eff_sites', U32, num_tasks) si = self.datastore.create_dset('by_task/srcids', hdf5.vstr, num_tasks, fillvalue=None) for task_no, rec in self.by_task.items(): effrups, effsites, srcids = rec er[task_no] = effrups es[task_no] = effsites si[task_no] = ' '.join(source_ids[s] for s in srcids) self.by_task.clear() if self.calc_times: # can be empty in case of errors self.numrups = sum(arr[0] for arr in self.calc_times.values()) numsites = sum(arr[1] for arr in self.calc_times.values()) logging.info('Effective number of ruptures: {:_d}/{:_d}'.format( int(self.numrups), self.totrups)) logging.info('Effective number of sites per rupture: %d', numsites / self.numrups) if psd: psdist = max(max(psd.ddic[trt].values()) for trt in psd.ddic) if psdist and self.maxradius >= psdist / 2: logging.warning( 'The pointsource_distance of %d km is too ' 'small compared to a maxradius of %d km', psdist, self.maxradius) self.calc_times.clear() # save a bit of memory return acc