"""Moss and Ross (2011) fault displacement model (https://doi.org/10.1785/0120100248)."""
from typing import Dict, Optional, Union
import numpy as np
from scipy import stats
from scipy.stats._distn_infrastructure import rv_continuous
from fdhpy.cli import cli_runner
from fdhpy.fault_displacement_model import FaultDisplacementModel
from fdhpy.normalized_fault_displacement_model import NormalizedFaultDisplacementModel
[docs]class MossRoss2011(FaultDisplacementModel):
"""
Moss and Ross (2011) fault displacement model. Applicable to principal surface fault
displacement on reverse faults.
This is a normalized model (i.e., uses the D/AD or D/MD form). The reference displacement
models (AD or MD) from Moss and Ross (2011) are used. All calculations include total aleatory
variability; in other words, the statistical distributions for D/AD and AD (or D/MD and MD) are
convolved.
Parameters
----------
style : str, optional
Style of faulting (case-insensitive). Default is "reverse".
magnitude : float
Earthquake moment magnitude. Recommended range is (5.5, 8).
xl : float
Normalized location x/L along the rupture length, range [0, 1.0].
xl_step : float, optional
Step size increment for slip profile calculations. Default is 0.05.
percentile : float
Aleatory quantile of interest. Use -1 for mean.
metric : str, optional
Definition of displacement (case-insensitive). Valid options are "principal". Default is
"principal".
version : str
Name of the model formulation (case-insensitive). Valid options are "d/ad" or "d/md".
displ_array : np.ndarray, optional
Displacement test value(s) in meters. Default array is provided.
Notes
-----
See model help at the module level:
.. code-block:: python
from fdhpy import MossRoss2011
print(MossRoss2011.__doc__)
See model help in command line:
.. code-block:: console
$ fd-mr11 --help
"""
_CONDITIONS = {
"style": {
"reverse": {"magnitude": (5.5, 8)},
},
"metric": {
"principal": {"version": ("d/ad", "d/md")},
},
}
_MODEL_NAME = "MossRoss2011"
# Override the init method to set model defaults
def __init__(self, **kwargs):
kwargs.setdefault("metric", "principal")
kwargs.setdefault("style", "reverse")
super().__init__(**kwargs)
# NOTE: magnitude, xl, and version are validated in parent class initialization
# Necessary optional methods in FaultDisplacementModel parent class
@property
def _folded_xl(self) -> Optional[float]:
return self._calc_folded_xl()
# Required methods for implementing normalized fault displacement models
@property
def _AD_MAG_SCALE_PARAMS(self):
"""Set parameters for loglinear magnitude scaling for average displacement."""
return {"intercept": -2.2192, "slope": 0.3244, "std_dev": 0.17}
@property
def _MD_MAG_SCALE_PARAMS(self):
"""Set parameters for loglinear magnitude scaling for maximum displacement."""
return {"intercept": -3.1971, "slope": 0.5102, "std_dev": 0.31}
@property
def _normalized_calcs(self) -> NormalizedFaultDisplacementModel:
"""Return an instance of the NormalizedFaultDisplacementModel for the current context."""
return NormalizedFaultDisplacementModel(self)
# Mandated methods in FaultDisplacementModel parent class
def _statistical_distribution_params(self) -> None:
# Compute alpha and beta parameters based on folded x/L and model version
if self.xl is not None:
if self.version == "d/ad":
alpha = np.exp(
-30.4 * self._folded_xl**3
+ 19.9 * self._folded_xl**2
- 2.29 * self._folded_xl
+ 0.574
)
beta = np.exp(
50.3 * self._folded_xl**3
- 34.6 * self._folded_xl**2
+ 6.6 * self._folded_xl
- 1.05
)
elif self.version == "d/md":
a1, a2 = 0.901, 0.713
b1, b2 = -1.86, 1.74
alpha = a1 * self._folded_xl + a2
beta = b1 * self._folded_xl + b2
self._alpha = float(alpha)
self._beta = float(beta)
# Compute mu and sigma parameters based on magnitude and model version
regr_params_map = {
"d/ad": self._AD_MAG_SCALE_PARAMS,
"d/md": self._MD_MAG_SCALE_PARAMS,
}
if self.magnitude is not None:
# NOTE: mu based on magnitude is handled in self._normalized_calcs
p = self._normalized_calcs._calc_mag_scale_stat_params(regr_params_map[self.version])
self._mu, self._sigma = p[0], p[1]
# Set to None if not computed
self._alpha = getattr(self, "_alpha", None)
self._beta = getattr(self, "_beta", None)
self._mu = getattr(self, "_mu", None)
self._sigma = getattr(self, "_sigma", None)
def _calc_displ_site(self) -> Optional[float]:
"""Calculate deterministic scenario displacement."""
return self._normalized_calcs._site_displ()
def _calc_displ_avg(self) -> Optional[float]:
"""Calculate average displacement."""
return self._normalized_calcs._avg_displ(self._AD_MAG_SCALE_PARAMS)
def _calc_displ_max(self) -> Optional[float]:
"""Calculate maximum displacement."""
return self._normalized_calcs._max_displ(self._MD_MAG_SCALE_PARAMS)
def _calc_cdf(self) -> Optional[np.ndarray]:
"""Calculate cumulative probability."""
return self._normalized_calcs._cdf()
# Ensure statistical distribution parameters are updated for current instance
@property
def stat_params_info(
self,
) -> Dict[str, Union[Dict[str, Optional[float]], rv_continuous, Dict[str, float]]]:
"""
Dictionary of statistical parameters ("params"), probability distribution
("prob_distribution"), and its arguments ("prob_distribution_kwargs") for the instance.
"""
self._statistical_distribution_params()
statistical_parameters = {
"mu": self._mu,
"sigma": self._sigma,
"alpha": self._alpha,
"beta": self._beta,
} # Use nomenclature in Moss & Ross (2011)
if self.version == "d/ad":
probability_distribution = stats.gamma
probability_distribution_kwargs = {
"a": statistical_parameters["alpha"],
"loc": 0,
"scale": statistical_parameters["beta"],
}
elif self.version == "d/md":
probability_distribution = stats.beta
probability_distribution_kwargs = {
"a": statistical_parameters["alpha"],
"b": statistical_parameters["beta"],
}
return {
"params": statistical_parameters,
"prob_distribution": probability_distribution,
"prob_distribution_kwargs": probability_distribution_kwargs,
}
@staticmethod
def main():
cli_runner(MossRoss2011)
# Inherit the docstrings from parent class
MossRoss2011.displ_site.__doc__ = FaultDisplacementModel.displ_site.__doc__
MossRoss2011.displ_avg.__doc__ = FaultDisplacementModel.displ_avg.__doc__
MossRoss2011.displ_max.__doc__ = FaultDisplacementModel.displ_max.__doc__
MossRoss2011.displ_profile.__doc__ = FaultDisplacementModel.displ_profile.__doc__
MossRoss2011.cdf.__doc__ = FaultDisplacementModel.cdf.__doc__
MossRoss2011.prob_exceed.__doc__ = FaultDisplacementModel.prob_exceed.__doc__
if __name__ == "__main__":
MossRoss2011.main()