Source code for shakelib.gmice.wgrw12

import numpy as np

from openquake.hazardlib.imt import PGA, PGV, SA


[docs]class WGRW12(object): """ Implements the ground motion intensity conversion equations (GMICE) of Worden et al. (2012). References: Worden, C. B., Gerstenberger, M. C., Rhoades, D. A., & Wald, D. J. (2012). Probabilistic relationships between ground‐motion parameters and modified Mercalli intensity in California. Bulletin of the Seismological Society of America, 102(1), 204-221. """ # ----------------------------------------------------------------------- # # MMI = c2->C1 + c2->C2 * log(Y) for log(Y) <= c2->T1 # MMI = C1 + C2 * log(Y) for c2->T1 < log(Y) <= T1 # MMI = C3 + C4 * log(Y) for log(Y) > T1 # # or # # MMI = c2->C1 + c2->C2 * log(Y) + C5 + C6 * log(D) + C7 * M # for log(Y) <= c2->T1 # MMI = C1 + C2 * log(Y) + C5 + C6 * log(D) + C7 * M # for c2->T1 < log(Y) <= T1 # MMI = C3 + C4 * log(Y) + C5 + C6 * log(D) + C7 * M for log(Y) > T1 # # Limit the distance residuals to between 10 and 300 km. # Limit the magnitude residuals to between M3.0 and M7.3. # # ----------------------------------------------------------------------- __pga = PGA() __pgv = PGV() __sa03 = SA(0.3) __sa10 = SA(1.0) __sa30 = SA(3.0) __constants = { __pga: {'C1': 1.78, 'C2': 1.55, 'C3': -1.60, 'C4': 3.70, 'C5': -0.91, 'C6': 1.02, 'C7': -0.17, 'T1': 1.57, 'T2': 4.22, 'SMMI': 0.66, 'SPGM': 0.35}, __pgv: {'C1': 3.78, 'C2': 1.47, 'C3': 2.89, 'C4': 3.16, 'C5': 0.90, 'C6': 0.00, 'C7': -0.18, 'T1': 0.53, 'T2': 4.56, 'SMMI': 0.63, 'SPGM': 0.38}, __sa03: {'C1': 1.26, 'C2': 1.69, 'C3': -4.15, 'C4': 4.14, 'C5': -1.05, 'C6': 0.60, 'C7': 0.00, 'T1': 2.21, 'T2': 4.99, 'SMMI': 0.82, 'SPGM': 0.44}, __sa10: {'C1': 2.50, 'C2': 1.51, 'C3': 0.20, 'C4': 2.90, 'C5': 2.27, 'C6': -0.49, 'C7': -0.29, 'T1': 1.65, 'T2': 4.98, 'SMMI': 0.75, 'SPGM': 0.47}, __sa30: {'C1': 3.81, 'C2': 1.17, 'C3': 1.99, 'C4': 3.01, 'C5': 1.91, 'C6': -0.57, 'C7': -0.21, 'T1': 0.99, 'T2': 4.96, 'SMMI': 0.89, 'SPGM': 0.64} } __constants2 = { __pga: {'C1': 1.71, 'C2': 2.08, 'T1': 0.14, 'T2': 2.0}, __pgv: {'C1': 4.62, 'C2': 2.17, 'T1': -1.21, 'T2': 2.0}, __sa03: {'C1': 1.15, 'C2': 1.92, 'T1': 0.44, 'T2': 2.0}, __sa10: {'C1': 2.71, 'C2': 2.17, 'T1': -0.33, 'T2': 2.0}, __sa30: {'C1': 7.35, 'C2': 3.45, 'T1': -1.55, 'T2': 2.0} } DEFINED_FOR_INTENSITY_MEASURE_TYPES = set([ PGA, PGV, SA ]) DEFINED_FOR_SA_PERIODS = set([0.3, 1.0, 3.0])
[docs] def getMIfromGM(self, amps, imt, dists=None, mag=None): """ Function to compute macroseismic intensity from ground-motion intensity. Supported ground-motion IMTs are PGA, PGV and PSA at 0.3, 1.0, and 3.0 sec periods. Args: amps (ndarray): Ground motion amplitude; natural log units; g for PGA and PSA, cm/s for PGV. imt (OpenQuake IMT): Type the input amps (must be one of PGA, PGV, or SA). Supported SA periods are 0.3, 1.0, and 3.0 sec. `[link] <http://docs.openquake.org/oq-hazardlib/master/imt.html>` dists (ndarray): Numpy array of distances from rupture (km). mag (float): Earthquake magnitude. Returns: ndarray of Modified Mercalli Intensity and ndarray of dMMI / dln(amp) (i.e., the slope of the relationship at the point in question). """ # noqa lfact = np.log10(np.e) c, c2 = self.__getConsts(imt) if dists is not None and mag is not None: doresid = True ldd = np.log10(np.clip(dists, 10, 300)) lmm = np.clip(mag, 3.0, 7.3) else: doresid = False # # Convert (for accelerations) from ln(g) to cm/s^2 # then take the log10 # if imt != self.__pgv: units = 981.0 else: units = 1.0 # # Math: log10(981 * exp(amps)) = log10(981) + log10(exp(amps)) # = log10(981) + amps * log10(e) # For PGV, just convert ln(amp) to log10(amp) by multiplying # by log10(e) # lamps = np.log10(units) + amps * lfact mmi = np.zeros_like(amps) dmmi_damp = np.zeros_like(amps) # # This is the MMI 1 to 2 range that is discussed in the paper but not # specifically implemented there # idx = lamps < c2['T1'] mmi[idx] = c2['C1'] + c2['C2'] * lamps[idx] dmmi_damp[idx] = c2['C2'] * lfact # # This is the lower segment of the bi-linear fit # idx = (lamps >= c2['T1']) & (lamps < c['T1']) mmi[idx] = c['C1'] + c['C2'] * lamps[idx] dmmi_damp[idx] = c['C2'] * lfact # # This is the upper segment of the bi-linear fit # idx = lamps >= c['T1'] mmi[idx] = c['C3'] + c['C4'] * lamps[idx] dmmi_damp[idx] = c['C4'] * lfact if doresid: mmi += c['C5'] + c['C6'] * ldd + c['C7'] * lmm mmi = np.clip(mmi, 1.0, 10.0) return mmi, dmmi_damp
[docs] def getGMfromMI(self, mmi, imt, dists=None, mag=None): """ Function to tcompute ground-motion intensity from macroseismic intensity. Supported IMTs are PGA, PGV and PSA for 0.3, 1.0, and 3.0 sec periods. Args: mmi (ndarray): Macroseismic intensity. imt (OpenQuake IMT): IMT of the requested ground-motions intensities (must be one of PGA, PGV, or SA). `[link] <http://docs.openquake.org/oq-hazardlib/master/imt.html>` dists (ndarray): Rupture distances (km) to the corresponding MMIs. mag (float): Earthquake magnitude. Returns: Ndarray of ground motion intensity in natural log of g for PGA and PSA, and natural log cm/s for PGV; ndarray of dln(amp) / dMMI (i.e., the slope of the relationship at the point in question). """ # noqa lfact = np.log10(np.e) c, c2 = self.__getConsts(imt) mmi = mmi.copy() ix_nan = np.isnan(mmi) mmi[ix_nan] = 1.0 if dists is not None and mag is not None: doresid = True ldd = np.log10(np.clip(dists, 10, 300)) lmm = np.clip(mag, 3.0, 7.3) else: doresid = False if doresid: mmi -= c['C5'] + c['C6'] * ldd + c['C7'] * lmm pgm = np.zeros_like(mmi) dpgm_dmmi = np.zeros_like(mmi) # # MMI 1 to 2 # idx = mmi < 2.0 pgm[idx] = np.power(10, (mmi[idx] - c2['C1']) / c2['C2']) dpgm_dmmi[idx] = 1.0 / (c2['C2'] * lfact) # # Lower segment of bi-linear relationship # idx = (mmi >= 2.0) & (mmi < c['T2']) pgm[idx] = np.power(10, (mmi[idx] - c['C1']) / c['C2']) dpgm_dmmi[idx] = 1.0 / (c['C2'] * lfact) # # Upper segment of bi-linear relationship # idx = mmi >= c['T2'] pgm[idx] = np.power(10, (mmi[idx] - c['C3']) / c['C4']) dpgm_dmmi[idx] = 1.0 / (c['C4'] * lfact) if imt != self.__pgv: units = 981.0 else: units = 1.0 pgm /= units pgm = np.log(pgm) pgm[ix_nan] = np.nan dpgm_dmmi[ix_nan] = np.nan return pgm, dpgm_dmmi
[docs] def getGM2MIsd(self): """ Return a dictionary of standard deviations for the ground-motion to MMI conversion. The keys are the ground motion types. Returns: Dictionary of GM to MI sigmas (in MMI units). """ return {self.__pga: self.__constants[self.__pga]['SMMI'], self.__pgv: self.__constants[self.__pgv]['SMMI'], self.__sa03: self.__constants[self.__sa03]['SMMI'], self.__sa10: self.__constants[self.__sa10]['SMMI'], self.__sa30: self.__constants[self.__sa30]['SMMI']}
[docs] def getMI2GMsd(self): """ Return a dictionary of standard deviations for the MMI to ground-motion conversion. The keys are the ground motion types. Returns: Dictionary of MI to GM sigmas (ln(PGM) units). """ # # Need to convert log10 to ln units # lfact = np.log(10.0) return {self.__pga: lfact * self.__constants[self.__pga]['SPGM'], self.__pgv: lfact * self.__constants[self.__pgv]['SPGM'], self.__sa03: lfact * self.__constants[self.__sa03]['SPGM'], self.__sa10: lfact * self.__constants[self.__sa10]['SPGM'], self.__sa30: lfact * self.__constants[self.__sa30]['SPGM']}
[docs] @staticmethod def getName(): """ Get the name of this GMICE. Returns: String containing name of this GMICE. """ return 'Worden et al. (2012)'
[docs] @staticmethod def getScale(): """ Get the name of the PostScript file containing this GMICE's scale. Returns: Name of GMICE scale file. """ return 'scale_wgrw12.ps'
[docs] @staticmethod def getMinMax(): """ Get the minimum and maximum MMI values produced by this GMICE. Returns: Tuple of min and max values of GMICE. """ return (1.0, 10.0)
[docs] @staticmethod def getDistanceType(): return 'rrup'
def __getConsts(self, imt): """ Helper function to get the constants. """ if (imt != self.__pga and imt != self.__pgv and imt != self.__sa03 and imt != self.__sa10 and imt != self.__sa30): raise ValueError("Invalid IMT " + str(imt)) c = self.__constants[imt] c2 = self.__constants2[imt] return (c, c2)