Source code for openquake.pfd.secondary_surf_rup.takao2013

# -*- 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.secondary_surf_rup.takao2013` implements the
model of Takao et al. (2013) in :class:`Takao2013SecondarySR`.

Supported Fault Styles: Reverse and Strike-slip

References
----------
Takao, M., Tsuchiyama, J., Annaka, T., & Kurita, T. (2013). Application of
probabilistic fault displacement hazard analysis in Japan. Journal of Japan
Association for Earthquake Engineering, 13(1), 17-36.
https://doi.org/10.5610/jaee.13.17
"""

import numpy as np
from openquake.pfd.secondary_surf_rup.base import BaseSecondarySurfRup


[docs]class Takao2013SecondarySR(BaseSecondarySurfRup): """ Implementation of the Takao et al. (2013) model for reverse and strike-slip faults. """ # Coefficients for 500m pixel size from Takao et al. (2013) COEFFS = { 500: {'C1': -3.839, 'C2': -3.886, 'C3': 0.35, 'C4': 0.2} } def get_prob(self, r, mag, pixel_size=500): """ Calculates probability of distributed surface rupture for reverse and strike-slip faults. This model estimates the probability of distributed faulting as a function of distance from the principal fault trace and earthquake magnitude. :param r: Distance to the principal fault trace in km (scalar or array). :param mag: Earthquake moment magnitude (scalar). :param pixel_size: Size of the analysis pixel in meters. Currently only 500m is supported. :return: Probability of distributed surface rupture (0-1). """ # Validate inputs if not np.isscalar(mag): raise ValueError("mag must be a scalar") if pixel_size not in self.COEFFS: raise ValueError( f"Invalid pixel_size '{pixel_size}'. " f"Must be one of {list(self.COEFFS)} meters" ) # Get coefficients coeffs = self.COEFFS[pixel_size] C1, C2, C3, C4 = coeffs['C1'], coeffs['C2'], coeffs['C3'], coeffs['C4'] # Convert r to array r = np.atleast_1d(np.asarray(r, dtype=float)) # Logistic regression from Takao et al. (2013). Coefficients C2 and # C3 are signed such that fx decreases with r (since C2 + C3*mag < 0 # for the calibrated magnitude range), giving the physically correct # behaviour: probability of distributed rupture decreases with # distance from the principal trace. fx = C1 + (C2 + C3 * mag) * np.log(r + C4) prob = np.exp(fx) / (1.0 + np.exp(fx)) return prob.item() if prob.size == 1 else prob