Source code for openquake.pfd.primary_surf_rup.moss2013

# -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (C) 2024-2026 Yen-Shin Chen, OGS
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

"""
Module :mod:`openquake.pfd.primary_surf_rup.moss2013`
"""

import numpy as np
from openquake.pfd.params import check_style
from openquake.pfd.primary_surf_rup.base import BasePrimarySurfRup


[docs]class Moss2013PrimarySR(BasePrimarySurfRup): """Principal surface-rupture probability model of Moss et al. (2013). Logistic model of the probability of principal surface rupture as a function of magnitude, faulting style, and site Vs30. Moss, R. E. S., Stanton, K. V., & Buelna, M. I. (2013). The impact of material stiffness on the likelihood of fault rupture propagating to the ground surface. Seismological Research Letters, 84(3), 485-488. https://doi.org/10.1785/0220110109 """ _ACCEPTED_STYLES = frozenset(["reverse", "strike-slip"]) def __init__(self, style=None, vs30=None): """ :param style: optional faulting style pinned by the logic-tree branch ('reverse' or 'strike-slip'); ``None`` defers to the ``get_prob`` call (normally resolved from the rupture rake). :param vs30: optional site Vs30 (m/s) pinned by the logic-tree branch; ``None`` defers to the ``get_prob`` call (normally the site collection's value). A pinned value overrides the site-specific one, matching the historical parameter-merge behaviour of the calculators. """ self.style = check_style(type(self).__name__, style, self._ACCEPTED_STYLES) self.vs30 = None if vs30 is None else float(vs30) def get_prob(self, mag: float, style=None, vs30: float = None) -> float: """ Model of Moss et al. (2013) for the probability of surface rupture based on the rupture mechanism (faulting style) and site conditions (shear-wave velocity). This model estimates the likelihood of surface rupture for two styles of faulting: "reverse" and "strike-slip." The probability is calculated as a function of event magnitude (mag) and the time-averaged shear-wave velocity to a depth of 30 meters (Vs30), which characterizes the site as soft or stiff soil. :param mag: The magnitude of the seismic event (float). Higher magnitudes generally result in greater probabilities of surface rupture. :param style: The faulting style (string, required). Accepted values are: - "reverse": Reverse faulting mechanism. - "strike-slip": Strike-slip faulting mechanism. If an unsupported style is provided, a ValueError will be raised. :param vs30: The time-averaged shear-wave velocity (float) to a depth of 30 meters (Vs30). This parameter distinguishes between stiff soil or rock (Vs30 > 600 m/s) and soft soil (Vs30 ≤ 600 m/s). The rupture probability varies based on this classification. :raises ValueError: If an invalid style is provided, the function raises an error indicating the acceptable faulting styles. :return: The probability of surface rupture as a float value between 0 and 1. :example: # Example usage model = Moss2013PrimarySR() probability = model.get_prob(mag=7.5, style="reverse", vs30=500) print(f"Probability of surface rupture: {probability}") """ # Fall back to constructor-pinned values (call-time argument wins) if style is None: style = self.style if vs30 is None: vs30 = self.vs30 # Define the accepted style of faultings accepted_styles = ["reverse", "strike-slip"] # Validate the style if style not in accepted_styles: raise ValueError( f"Invalid style '{style}'. Accepted values are: {', '.join(accepted_styles)}" ) if vs30 is None: raise ValueError( "Moss2013PrimarySR requires vs30, either from the site " "collection or pinned in the logic-tree branch") m = np.asarray(mag, dtype=float) if style == 'reverse': if vs30 > 600: a, b = 13.9745, 2.1395 prob = 1.0 / (1.0 + np.exp(a - b * m)) else: a, b = 6.2548, 0.8308 prob = 1.0 / (1.0 + np.exp(a - b * m)) else: # strike-slip if vs30 > 600: a, b = 11.4071, 1.8465 prob = 1.0 / (1.0 + np.exp(a - b * m)) else: a, b = 12.2908, 1.9520 prob = 1.0 / (1.0 + np.exp(a - b * m)) return prob.item() if prob.shape == () else prob