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
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]) print 'IncrementalMFD'