def __init__(self, min_mag: float, max_mag: float, corner_mag: float, bin_width: float, a_val: float, b_val: float): self.min_mag = min_mag self.max_mag = max_mag self.corner_mag = corner_mag self.bin_width = bin_width self.a_val = a_val self.b_val = b_val self.beta = 2. / 3. * self.b_val # constants from Mfds.java self.SMALL_MO_MAG: float = 4. self.TAPERED_LARGE_MAG: float = 9.05 self.min_mo = mag_to_mo(self.SMALL_MO_MAG) self.large_mo = mag_to_mo(self.TAPERED_LARGE_MAG) self.corner_mo = mag_to_mo(corner_mag) # This class uses a Double Truncated GR distribution, and the # rates are modified per the tapering. self._dt_gr = TruncatedGRMFD(self.min_mag, self.max_mag, self.bin_width, self.a_val, self.b_val) self._get_min_mag_and_num_bins = self._dt_gr._get_min_mag_and_num_bins self.check_constraints()
def setUp(self): """ """ mfd_1 = TruncatedGRMFD(4.5, 8.0, 0.1, 4.0, 1.0) mfd_2 = TruncatedGRMFD(4.5, 7.5, 0.1, 3.5, 1.1) self.source_model = [ PointSource('001', 'Point1', 'Active Shallow Crust', mfd_1, 1.0, WC1994(), 1.0, PoissonTOM(50.0), 0.0, 30.0, Point(30.0, 30.5), PMF([(1.0, NodalPlane(0.0, 90.0, 0.0))]), PMF([(1.0, 10.0)])), PointSource('002', 'Point2', 'Active Shallow Crust', mfd_2, 1.0, WC1994(), 1.0, PoissonTOM(50.0), 0.0, 30.0, Point(30.0, 30.5), PMF([(1.0, NodalPlane(0.0, 90.0, 0.0))]), PMF([(1.0, 10.0)])) ] self.sites = SiteCollection([ Site(Point(30.0, 30.0), 760., True, 1.0, 1.0, 1), Site(Point(30.25, 30.25), 760., True, 1.0, 1.0, 2), Site(Point(30.4, 30.4), 760., True, 1.0, 1.0, 2) ]) self.gsims = {'Active Shallow Crust': 'AkkarBommer2010'} self.imts = ['PGA', 'SA(0.5)'] self.imls = [[0.01, 0.1, 0.2, 0.5, 0.8]]
def example_calc(apply): sitecol = SiteCollection([ Site(Point(30.0, 30.0), 760., 1.0, 1.0), Site(Point(30.25, 30.25), 760., 1.0, 1.0), Site(Point(30.4, 30.4), 760., 1.0, 1.0) ]) mfd_1 = TruncatedGRMFD(4.5, 8.0, 0.1, 4.0, 1.0) mfd_2 = TruncatedGRMFD(4.5, 7.5, 0.1, 3.5, 1.1) sources = [ PointSource('001', 'Point1', 'Active Shallow Crust', mfd_1, 1.0, WC1994(), 1.0, PoissonTOM(50.0), 0.0, 30.0, Point(30.0, 30.5), PMF([(1.0, NodalPlane(0.0, 90.0, 0.0))]), PMF([(1.0, 10.0)])), PointSource('002', 'Point2', 'Active Shallow Crust', mfd_2, 1.0, WC1994(), 1.0, PoissonTOM(50.0), 0.0, 30.0, Point(30.0, 30.5), PMF([(1.0, NodalPlane(0.0, 90.0, 0.0))]), PMF([(1.0, 10.0)])) ] imtls = { 'PGA': [0.01, 0.1, 0.2, 0.5, 0.8], 'SA(0.5)': [0.01, 0.1, 0.2, 0.5, 0.8] } gsims = {'Active Shallow Crust': AkkarBommer2010()} return calc_hazard_curves(sources, sitecol, imtls, gsims, apply=apply, filter_distance='rrup')
def reference_psha_calculation_openquake(): """ Sets up the reference PSHA calculation calling OpenQuake directly. All subsequent implementations should match this example """ # Site model - 3 Sites site_model = SiteCollection([ Site(Point(30.0, 30.0), 760., True, 1.0, 1.0, 1), Site(Point(30.25, 30.25), 760., True, 1.0, 1.0, 2), Site(Point(30.4, 30.4), 760., True, 1.0, 1.0, 2) ]) # Source Model Two Point Sources mfd_1 = TruncatedGRMFD(4.5, 8.0, 0.1, 4.0, 1.0) mfd_2 = TruncatedGRMFD(4.5, 7.5, 0.1, 3.5, 1.1) source_model = [ PointSource('001', 'Point1', 'Active Shallow Crust', mfd_1, 1.0, WC1994(), 1.0, PoissonTOM(50.0), 0.0, 30.0, Point(30.0, 30.5), PMF([(1.0, NodalPlane(0.0, 90.0, 0.0))]), PMF([(1.0, 10.0)])), PointSource('002', 'Point2', 'Active Shallow Crust', mfd_2, 1.0, WC1994(), 1.0, PoissonTOM(50.0), 0.0, 30.0, Point(30.0, 30.5), PMF([(1.0, NodalPlane(0.0, 90.0, 0.0))]), PMF([(1.0, 10.0)])) ] imts = { 'PGA': [0.01, 0.1, 0.2, 0.5, 0.8], 'SA(0.5)': [0.01, 0.1, 0.2, 0.5, 0.8] } # Akkar & Bommer (2010) GMPE gsims = {'Active Shallow Crust': gsim.akkar_bommer_2010.AkkarBommer2010()} truncation_level = None return calc_hazard_curves(source_model, site_model, imts, gsims, truncation_level)
def test_create_oq_hazardlib_point_source(self): """ Tests the function to create a point source model """ mfd1 = TruncatedGRMFD(5.0, 8.0, 0.1, 3.0, 1.0) self.point_source = mtkPointSource('001', 'A Point Source', trt='Active Shallow Crust', geometry = Point(10., 10.), upper_depth = 0., lower_depth = 20., mag_scale_rel=None, rupt_aspect_ratio=1.0, mfd=mfd1, nodal_plane_dist=None, hypo_depth_dist=None) test_source = self.point_source.create_oqhazardlib_source(TOM, 2.0, True) self.assertIsInstance(test_source, PointSource) self.assertIsInstance(test_source.mfd, TruncatedGRMFD) self.assertAlmostEqual(test_source.mfd.b_val, 1.0) self.assertIsInstance(test_source.nodal_plane_distribution, PMF) self.assertIsInstance(test_source.hypocenter_distribution, PMF) self.assertIsInstance(test_source.magnitude_scaling_relationship, WC1994)
def test_create_oqhazardlib_source(self): # Define a complete source area_geom = polygon.Polygon([ point.Point(10., 10.), point.Point(12., 10.), point.Point(12., 8.), point.Point(10., 8.) ]) mfd1 = TruncatedGRMFD(5.0, 8.0, 0.1, 3.0, 1.0) self.area_source = mtkAreaSource('001', 'A Point Source', trt='Active Shallow Crust', geometry=area_geom, upper_depth=0., lower_depth=20., mag_scale_rel=None, rupt_aspect_ratio=1.0, mfd=mfd1, nodal_plane_dist=None, hypo_depth_dist=None) test_source = self.area_source.create_oqhazardlib_source( TOM, 1.0, 10.0, True) self.assertIsInstance(test_source, AreaSource) self.assertIsInstance(test_source.mfd, TruncatedGRMFD) self.assertAlmostEqual(test_source.mfd.b_val, 1.0) self.assertIsInstance(test_source.nodal_plane_distribution, PMF) self.assertIsInstance(test_source.hypocenter_distribution, PMF) self.assertIsInstance(test_source.magnitude_scaling_relationship, WC1994)
def plot_trunc_gr_model(aval, bval, min_mag, max_mag, dmag, catalogue=None, completeness=None, figure_size=None, filename=None, filetype='png', dpi=300): """ Plots a Gutenberg-Richter model """ input_model = TruncatedGRMFD(min_mag, max_mag, dmag, aval, bval) if not catalogue: # Plot only the modelled recurrence annual_rates, cumulative_rates = _get_recurrence_model(input_model) plt.semilogy(annual_rates[:, 0], annual_rates[:, 1], 'b-') plt.semilogy(annual_rates[:, 0], cumulative_rates, 'r-') plt.xlabel('Magnitude', fontsize='large') plt.ylabel('Annual Rate', fontsize='large') plt.legend(['Incremental Rate', 'Cumulative Rate']) _save_image(filename, filetype, dpi) else: completeness = _check_completeness_table(completeness, catalogue) plot_recurrence_model(input_model, catalogue, completeness, input_model.bin_width, figure_size, filename, filetype, dpi)
def test_create_oqhazardlib_complex_fault_source(self): """ Tests the conversion of a point source to an instance of the :class: openquake.hazardlib.source.complex_fault.ComplexFaultSource """ complex_edges = [ line.Line([point.Point(11., 10., 0.), point.Point(10., 10., 0.)]), line.Line( [point.Point(11.5, 10., 21.), point.Point(10.0, 10., 21.)]) ] mfd1 = TruncatedGRMFD(5.0, 8.0, 0.1, 3.0, 1.0) self.fault_source = mtkComplexFaultSource('001', 'A Fault Source', trt='Active Shallow Crust', geometry=None, mag_scale_rel=None, rupt_aspect_ratio=1.0, mfd=mfd1, rake=0.) self.fault_source.create_geometry(complex_edges, 2.0) test_source = self.fault_source.create_oqhazardlib_source( TOM, 5.0, True) self.assertIsInstance(test_source, ComplexFaultSource) self.assertIsInstance(test_source.mfd, TruncatedGRMFD) self.assertAlmostEqual(test_source.mfd.b_val, 1.0) self.assertIsInstance(test_source.magnitude_scaling_relationship, WC1994)
def plot_trunc_gr_model( aval, bval, min_mag, max_mag, dmag, catalogue=None, completeness=None, filename=None, figure_size=(8, 6), filetype='png', dpi=300, ax=None): """ Plots a Gutenberg-Richter model """ input_model = TruncatedGRMFD(min_mag, max_mag, dmag, aval, bval) if not catalogue: # Plot only the modelled recurrence annual_rates, cumulative_rates = _get_recurrence_model(input_model) if ax is None: fig, ax = plt.subplots(figsize=figure_size) else: fig = ax.get_figure() ax.semilogy(annual_rates[:, 0], annual_rates[:, 1], 'b-') ax.semilogy(annual_rates[:, 0], cumulative_rates, 'r-') ax.xlabel('Magnitude') ax.set_ylabel('Annual Rate') ax.set_legend(['Incremental Rate', 'Cumulative Rate']) _save_image(fig, filename, filetype, dpi) else: completeness = _check_completeness_table(completeness, catalogue) plot_recurrence_model( input_model, catalogue, completeness, dmag, filename=filename, figure_size=figure_size, filetype=filetype, dpi=dpi, ax=ax)
def test_create_oqhazardlib_source(self): # Tests to ensure the hazardlib source is created trace = line.Line([point.Point(10., 10.), point.Point(11., 10.)]) mfd1 = TruncatedGRMFD(5.0, 8.0, 0.1, 3.0, 1.0) self.fault_source = mtkSimpleFaultSource( '001', 'A Fault Source', trt='Active Shallow Crust', geometry=None, dip=90., upper_depth=0., lower_depth=20., mag_scale_rel=None, rupt_aspect_ratio=1.0, mfd=mfd1, rake=0.) self.fault_source.create_geometry(trace, 90., 0., 20., 1.0) test_source = self.fault_source.create_oqhazardlib_source(TOM, 2.0, True) self.assertIsInstance(test_source, SimpleFaultSource) self.assertIsInstance(test_source.mfd, TruncatedGRMFD) self.assertAlmostEqual(test_source.mfd.b_val, 1.0) self.assertIsInstance(test_source.magnitude_scaling_relationship, WC1994)
def collapse_sources(source_df, source_tree_symbolic_df, bin_width=0.1): ''' Given a tree dataframe of branches and a source model dataframe of zones, for every branch affecting MFDs, collapse all possible MFDs into one using those weights, add the combined MFD to the zone in the source model and finally remove the corresponding branch from the tree. ''' collapsible = np.array([ branch['uncertaintyType'] in MODEL_LENGTHS.keys() for _, branch in source_tree_symbolic_df.iterrows() ]) source_df = source_df.loc[(source_df['a'] != 0) & (source_df['mmax'] != 0)].copy() zone_rates, all_rates, all_weights = [], [], [] for _, zone in source_df.iterrows(): mfds = [ TruncatedGRMFD(min_mag=zone['mmin'], max_mag=zone['mmax'], a_val=zone['a'], b_val=zone['b'], bin_width=bin_width) ] weights = [1.] labels = [''] # apply logic tree branches in successsion for _, branch in source_tree_symbolic_df.loc[collapsible].iterrows(): try: mfds, weights, labels = branch_mfds(mfds, weights, labels, branch, zone) except ValueError: print(zone) mfds, weights, labels = branch_mfds(mfds, weights, labels, branch, zone) rates = get_rates(mfds) # compute the weighted sum of the rates for this zones collapsed_rates = (rates * weights.reshape(1, -1)).sum(axis=1) zone_rates.append(limit_precision(collapsed_rates, 5)) # save intermediate results from each zone for diagnostic purposes all_rates.append(rates) all_weights.append(weights) source_df['occurRates'] = zone_rates source_df['magBin'] = bin_width source_df['all_rates'] = all_rates return (source_df, source_tree_symbolic_df.loc[np.logical_not(collapsible)], all_weights, labels)
def test(self): sitecol = SiteCollection([Site(Point(30.0, 30.0), 760., 1.0, 1.0)]) mfd = TruncatedGRMFD(4.5, 8.0, 0.1, 4.0, 1.0) sources = [ PointSource('001', 'Point1', 'Active Shallow Crust', mfd, 1.0, WC1994(), 1.0, PoissonTOM(50.0), 0.0, 30.0, Point(30.0, 30.5), PMF([(1.0, NodalPlane(0.0, 90.0, 0.0))]), PMF([(1.0, 10.0)])) ] imtls = {'PGA': [0.01, 0.1, 0.2, 0.5, 0.8]} hc1 = calc_hazard_curves( sources, sitecol, imtls, {'Active Shallow Crust': AkkarBommer2010()})['PGA'] hc2 = calc_hazard_curves( sources, sitecol, imtls, {'Active Shallow Crust': SadighEtAl1997()})['PGA'] hc = .6 * hc1 + .4 * hc2 ag = AvgGMPE(b1=dict(AkkarBommer2010={'weight': .6}), b2=dict(SadighEtAl1997={'weight': .4})) hcm = calc_hazard_curves(sources, sitecol, imtls, {'Active Shallow Crust': ag})['PGA'] # the AvgGMPE is not producing real means!! numpy.testing.assert_almost_equal(hc, hcm, decimal=3)
class TaperedGRMFD(BaseMFD): """ Tapered Gutenberg-Richter MFD is defined as a typical Truncated Gutenberg-Richter MFD up to a ``corner_mag`` above which an exponential taper is applied; see Kagan (2002a, Geophysical Journal International) for details. This implementation is based on the United States National Seismic Hazard Map Project code (https://github.com/usgs/nshmp-haz). :param min_mag: The lowest possible magnitude for this MFD. The first bin in the :meth:`result histogram <get_annual_occurrence_rates>` will be aligned to make its left border match this value. :param max_mag: The highest possible magnitude. The same as for ``min_mag``: the last bin in the histogram will correspond to the magnitude value equal to ``max_mag - bin_width / 2``. :param corner_mag: The magnitude above which the exponential tapering of the earthquake frequency (rate) occurs. Rates below this magnitude are identical to a Gutenberg-Richter with the same ``a`` and ``b`` values. :param bin_width: A positive float value -- the width of a single histogram bin. Values for ``min_mag`` and ``max_mag`` don't have to be aligned with respect to ``bin_width``. They get rounded accordingly anyway so that both are divisible by ``bin_width`` just before converting a function to a histogram. See :meth:`_get_min_mag_and_num_bins`. """ MODIFICATIONS = set(()) def __init__(self, min_mag: float, max_mag: float, corner_mag: float, bin_width: float, a_val: float, b_val: float): self.min_mag = min_mag self.max_mag = max_mag self.corner_mag = corner_mag self.bin_width = bin_width self.a_val = a_val self.b_val = b_val self.beta = 2. / 3. * self.b_val # constants from Mfds.java self.SMALL_MO_MAG: float = 4. self.TAPERED_LARGE_MAG: float = 9.05 self.min_mo = mag_to_mo(self.SMALL_MO_MAG) self.large_mo = mag_to_mo(self.TAPERED_LARGE_MAG) self.corner_mo = mag_to_mo(corner_mag) # This class uses a Double Truncated GR distribution, and the # rates are modified per the tapering. self._dt_gr = TruncatedGRMFD(self.min_mag, self.max_mag, self.bin_width, self.a_val, self.b_val) self._get_min_mag_and_num_bins = self._dt_gr._get_min_mag_and_num_bins self.check_constraints() def check_constraints(self): """ Checks the following constraints: * Bin width is greater than 0. * Minimum magnitude is positive. * Maximum magnitude is greater than minimum magnitude by at least one bin width (or equal to that value). * Corner magnitude is greater than minimum magnitude by at least one bin width (or equal to that value). * ``b`` value is positive. """ if not self.bin_width > 0: raise ValueError('bin width %g must be positive' % self.bin_width) if not self.min_mag >= 0: raise ValueError('minimum magnitude %g must be non-negative' % self.min_mag) if not self.max_mag >= self.min_mag + self.bin_width: raise ValueError('maximum magnitude %g must be higher than ' 'minimum magnitude %g by ' 'bin width %g at least' % (self.max_mag, self.min_mag, self.bin_width)) if not self.b_val > 0.: raise ValueError('b-value %g must be non-negative' % self.b_val) if not self.corner_mag >= self.min_mag + self.bin_width: raise ValueError('corner magnitude %g must be higher than ' 'minimum magnitude %g by ' 'bin width %g at least' % (self.corner_mag, self.min_mag, self.bin_width)) def _pareto(self, mo, corner_mo): """'pareto' function from nhsmp-haz """ return (self.min_mo / mo)**self.beta * math.exp( (self.min_mo - mo) / corner_mo) def _scale_mag_bin_rate(self, mag, rate): """ This function scales the TruncatedGRMFD rate for that bin using a tapering scheme developed in the NSHM-HAZ function; see Mfds.java in the nshmp-haz codebase. """ mag_mo_lo = mag_to_mo(mag - self.bin_width / 2.) mag_mo_hi = mag_to_mo(mag + self.bin_width / 2.) scale_num = (self._pareto(mag_mo_lo, self.corner_mo) - self._pareto(mag_mo_hi, self.corner_mo)) scale_denom = (self._pareto(mag_mo_lo, self.large_mo) - self._pareto(mag_mo_hi, self.large_mo)) scale = scale_num / scale_denom return rate * scale def _get_rate(self, mag): """ Calculate and return an annual occurrence rate for a specific bin. :param mag: Magnitude value corresponding to the center of the bin of interest. :returns: Float number, the annual occurrence rate """ gr_rate = self._dt_gr._get_rate(mag) return self._scale_mag_bin_rate(mag, gr_rate) def get_min_max_mag(self): """ Return the minimum and maximum magnitudes """ return self._dt_gr.get_min_max_mag() def get_annual_occurrence_rates(self): """ Calculate and return the annual occurrence rates histogram. The result histogram has only one bin if minimum and maximum magnitude values appear equal after rounding. :returns: See :meth: `openquake.hazardlib.mfd.base.BaseMFD.get_annual_occurrence_rates`. """ mag_rates = [] gr_rates = self._dt_gr.get_annual_occurrence_rates() for mag, rate in gr_rates: mag_rates.append((mag, self._scale_mag_bin_rate(mag, rate))) return mag_rates
min_mag = 4.5 # magnitude of earthquake max_mag = 7.5 a_val = log10(4e4) # random number N0 = 10**a_val b_val = 1.2 beta = bval2beta(b_val) bin_width = [0.1] #, 0.01] fig = plt.figure(1, figsize=(12, 6)) # loop through bin width for i, bw in enumerate(bin_width): plt.subplot(1, 2, i + 1) # get oq rates gr = TruncatedGRMFD(min_mag, max_mag, bw, a_val, b_val) oq_rates = gr.get_annual_occurrence_rates() # get cum OQ ratescum_oq_rates = [] # get cummulative frisk rates (for OQ) cum_inc_rates, frisk_mags = get_oq_incrementalMFD(beta, N0, min_mag, max_mag, bw) # get rates inc_rates = [] for i in range(0, len(cum_inc_rates)): if i < len(cum_inc_rates) - 1: inc_rates.append(cum_inc_rates[i] - cum_inc_rates[i + 1]) else: inc_rates.append(cum_inc_rates[i])