Source code for pygmm.macedo_abrahamson_liu_2021

"""Macedo, Abrahamson, and Liu (2021, :cite:`macedo21`) CAV model."""

import numpy as np

from . import model

__author__ = "Mahdi Bahrampouri"


[docs] class MacedoAbrahamsonLiu2021(model.Model): """Macedo, Abrahamson, and Liu (2021, :cite:`macedo21`) CAV model. Conditional ground-motion model (CGMM) and scenario-based models for cumulative absolute velocity (CAV, m/s) for shallow crustal tectonic settings. Calibrated on the NGA-West2 database (~14,000 recordings, 287 events, M 3.0–7.9). Two evaluation modes are supported: **Conditional mode** (``pga_model=None``): CAV is estimated from a user-supplied PGA value (in g) using Eq. (12). The aleatory variability is :math:`\\sigma = \\sqrt{\\tau^2 + \\phi^2} \\approx 0.31`. **Scenario mode** (``pga_model`` is one of ``'ASK14'``, ``'BSSA14'``, ``'CB14'``, ``'CY14'``, ``'I14'``): The median PGA and its lognormal standard deviation are taken from the chosen NGA-West2 GMM, then combined with the conditional model via propagation of errors (Eq. 17) to yield a full scenario-based CAV estimate. Parameters ---------- scenario : :class:`pygmm.model.Scenario` Earthquake scenario. When using scenario mode the scenario must also contain all parameters required by the chosen backbone PGA GMM (e.g. ``dist_x``, ``dip``, ``depth_tor`` for CY14). pga_model : str or None, optional Backbone NGA-West2 GMM for scenario mode. Accepted values: ``'ASK14'``, ``'BSSA14'``, ``'CB14'``, ``'CY14'``, ``'I14'``. Default ``None`` selects conditional mode. pga : float or None, optional Peak ground acceleration in g for conditional mode. Required when ``pga_model`` is ``None``. References ---------- .. [1] Macedo, J., Abrahamson, N., and Liu, C. (2021). "New Scenario-Based Cumulative Absolute Velocity Models for Shallow Crustal Tectonic Settings." Bulletin of the Seismological Society of America, 111(1), 157–172. doi: 10.1785/0120190321. """ NAME = "Macedo, Abrahamson, & Liu (2021)" ABBREV = "MAL21" # Regression coefficients from Table 1 of Macedo et al. (2021). # Eq. (12): ln(CAV[m/s]) = c1 + c2*ln(PGA[g]) + c3*Mw # + c4*ln(Vs30[m/s]) + c5*ln(Rrup[km]) # + c6 * F_HW * T1(dip) * T2(Mw) * T5(RJB) C1 = 1.79 C2 = 0.67 C3 = 0.57 C4 = -0.47 C5 = -0.0026 C6 = 0.17 # Aleatory variability (Table 1). # Note: sqrt(0.17² + 0.26²) ≈ 0.31. TAU = 0.17 # between-event PHI = 0.26 # within-event SIGMA_COND = float(np.sqrt(TAU**2 + PHI**2)) SUPPORTED_PGA_MODELS = ("ASK14", "BSSA14", "CB14", "CY14", "I14") PARAMS = [ model.NumericParameter("mag", True, 3.0, 8.0), model.NumericParameter("dist_rup", True, 0, 300), model.NumericParameter("dist_jb", True, 0, 300), model.NumericParameter("v_s30", True, 150, 1500), model.NumericParameter("dip", False, 15, 90, default=90.0), model.CategoricalParameter("on_hanging_wall", False, [True, False], False), ]
[docs] def __init__(self, scenario, pga_model=None, pga=None): """Initialize the model.""" # Preserve the full scenario so backbone GMMs can access their own params. self._full_scenario = scenario super().__init__(scenario) s = self._scenario if pga_model is not None: if pga_model not in self.SUPPORTED_PGA_MODELS: raise ValueError( f"pga_model '{pga_model}' is not supported. " f"Choose from: {', '.join(self.SUPPORTED_PGA_MODELS)}" ) gmm = self._build_pga_gmm(pga_model) self._ln_cav = self._calc_ln_cav(np.log(gmm.pga), s) self._ln_std = _calc_scenario_ln_std(gmm.ln_std_pga) self._tau = None self._phi = None else: if pga is None: raise ValueError( "Either 'pga_model' (scenario mode) or 'pga' " "(conditional mode) must be provided." ) self._ln_cav = self._calc_ln_cav(np.log(float(pga)), s) self._ln_std = self.SIGMA_COND self._tau = self.TAU self._phi = self.PHI
# ------------------------------------------------------------------ # # Public properties # # ------------------------------------------------------------------ # @property def ln_cav(self): """Natural log of the median CAV (m/s).""" return self._ln_cav @property def cav(self): """Median CAV (m/s).""" return np.exp(self._ln_cav) @property def ln_std(self): """Total lognormal standard deviation of CAV.""" return self._ln_std @property def tau(self): """Between-event standard deviation (conditional mode only).""" return self._tau @property def phi(self): """Within-event standard deviation (conditional mode only).""" return self._phi @property def cav_plus_sigma(self): """84th-percentile CAV (m/s): ``exp(ln_cav + ln_std)``.""" return np.exp(self._ln_cav + self._ln_std) @property def cav_minus_sigma(self): """16th-percentile CAV (m/s): ``exp(ln_cav - ln_std)``.""" return np.exp(self._ln_cav - self._ln_std) # ------------------------------------------------------------------ # # Private helpers # # ------------------------------------------------------------------ # def _calc_ln_cav(self, ln_pga, s): """Evaluate Eq. (12) / Eq. (14) of Macedo et al. (2021).""" hw = self._hanging_wall_term(s) return ( self.C1 + self.C2 * ln_pga + self.C3 * s.mag + self.C4 * np.log(s.v_s30) + self.C5 * np.log(s.dist_rup) + self.C6 * hw ) def _hanging_wall_term(self, s): """F_HW * T1(dip) * T2(Mw) * T5(RJB) — last term of Eq. (12).""" if not s.on_hanging_wall: return 0.0 return _t1_dip(s.dip) * _t2_mag(s.mag) * _t5_rjb(s.dist_jb) def _build_pga_gmm(self, pga_model): """Lazily import and instantiate the chosen backbone PGA GMM.""" from . import ( abrahamson_silva_kamai_2014, boore_stewart_seyhan_atkinson_2014, campbell_bozorgnia_2014, chiou_youngs_2014, idriss_2014, ) gmm_map = { "ASK14": abrahamson_silva_kamai_2014.AbrahamsonSilvaKamai2014, "BSSA14": boore_stewart_seyhan_atkinson_2014.BooreStewartSeyhanAtkinson2014, "CB14": campbell_bozorgnia_2014.CampbellBozorgnia2014, "CY14": chiou_youngs_2014.ChiouYoungs2014, "I14": idriss_2014.Idriss2014, } return gmm_map[pga_model](self._full_scenario)
# ------------------------------------------------------------------ # # Module-level taper functions (Eqs. 13a–c) # # ------------------------------------------------------------------ # def _t1_dip(dip): """Dip taper T1 — Eq. (13a) of Macedo et al. (2021).""" if dip > 30: return (90.0 - dip) / 45.0 return 60.0 / 45.0 def _t2_mag(mag): """Magnitude taper T2 — Eq. (13b) of Macedo et al. (2021).""" if mag >= 6.5: return 1.0 elif mag <= 5.5: return 0.0 return 1.0 + 0.2 * (mag - 6.5) - 0.8 * (mag - 6.5) ** 2 def _t5_rjb(dist_jb): """Distance taper T5 — Eq. (13c) of Macedo et al. (2021).""" if dist_jb == 0: return 1.0 elif dist_jb < 15: return 1.0 - dist_jb / 15.0 return 0.0 def _calc_scenario_ln_std(sigma_ln_pga): """Total sigma for scenario mode — Eq. (17) of Macedo et al. (2021).""" return np.sqrt(0.097 + 0.4541 * sigma_ln_pga**2)