# ------------------- The OpenQuake Model Building Toolkit --------------------
# ------------------- FERMI: Fault nEtwoRks ModellIng -------------------------
# Copyright (C) 2023 GEM Foundation
# .-.
# / \ .-.
# | .`. ; .--. ___ .-. ___ .-. .-. ( __)
# | |(___) / \ ( ) \ ( ) ' \ (''")
# | |_ | .-. ; | ' .-. ; | .-. .-. ; | |
# ( __) | | | | | / (___) | | | | | | | |
# | | | |/ | | | | | | | | | | |
# | | | ' _.' | | | | | | | | | |
# | | | .'.-. | | | | | | | | | |
# | | ' `-' / | | | | | | | | | |
# (___) `.__.' (___) (___)(___)(___)(___)
#
# 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 <http://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------------
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# coding: utf-8
import os
import unittest
import numpy as np
from openquake.fnm.inversion.soe_builder import (
make_slip_rate_eqns,
rel_gr_mfd_rates,
make_rel_gr_mfd_eqns,
mean_slip_rate,
get_mag_counts,
make_abs_mfd_eqns,
make_slip_rate_smoothing_eqns,
get_fault_moment,
get_slip_rate_fraction,
make_fault_mfd_equation_components,
make_fault_rel_mfd_equation_components,
make_rup_rate_prior_from_fault_abs_mfds,
make_ridge_regularization_eqns_from_fault_abs_mfds,
make_eqns,
hz,
)
from openquake.fnm.inversion.utils import (
rup_df_to_rupture_dicts,
subsection_df_to_fault_dicts,
get_fault_moment_rate,
get_mfd_occurrence_rates,
)
from openquake.fnm.all_together_now import (
build_fault_network,
build_system_of_equations,
)
from openquake.fnm.tests.inversion.simple_test_data import (
rup_A,
rup_B,
rup_C,
rup_D,
f1,
f2,
simple_test_rups,
simple_test_faults,
simple_test_fault_adjacence,
)
[docs]
def test_make_slip_rate_eqns():
lhs, rhs, err, _ = make_slip_rate_eqns(
simple_test_rups, simple_test_faults
)
lhs = lhs.todense()
np.testing.assert_array_almost_equal(
lhs, np.array([[1.0, 0.0, 2.5, 1.75], [0.0, 1.0, 2.5, 1.75]])
)
np.testing.assert_array_almost_equal(rhs, np.array([0.001, 0.001]))
[docs]
@unittest.skip("function not implemented with new methods")
def test_make_slip_rate_smoothing_eqns():
lhs, rhs, err = make_slip_rate_smoothing_eqns(
simple_test_fault_adjacence,
simple_test_faults,
rups=simple_test_rups,
)
lhs = lhs.todense()
np.testing.assert_array_almost_equal(
lhs,
np.array(
[
[1.0, -1.0, 0.0, 0.0],
]
),
)
# np.testing.assert_array_almost_equal(rhs, np.array([0.0, 0.0, 0.0, 0.0]))
[docs]
def test_get_mag_counts():
mag_counts = get_mag_counts(simple_test_rups)
assert mag_counts == {6.0: 2, 7.0: 1, 6.5: 1}
[docs]
def test_rel_gr_mfd_rates():
rel_rates = rel_gr_mfd_rates([6.0, 6.5, 7.0], b=1.0)
_rel_rates = {
6.0: 1.0,
6.5: 0.31622776601683794,
7.0: 0.1,
}
for mag, rate in rel_rates.items():
assert np.isclose(rate, _rel_rates[mag])
# @unittest.skip("Not sure of correct rates")
[docs]
def test_make_rel_gr_mfd_eqns():
lhs, rhs, err, _ = make_rel_gr_mfd_eqns(simple_test_rups, b=1.0)
lhs = lhs.todense()
np.testing.assert_array_almost_equal(
lhs,
np.array(
[
[-1.0, -1.0, 2.16227766, 2.16227766],
[-1.0, -1.0, 9.0, -1.0],
],
),
)
np.testing.assert_array_almost_equal(
# err, np.array([1.77827941, 3.16227766])
err,
np.array([3.162278, 10.0]),
)
np.testing.assert_array_almost_equal(rhs, np.array([0.0, 0.0]))
# @unittest.skip("Not sure of correct rates")
[docs]
def test_and_solve_slip_rate_and_rel_gr_eqns(inversion_tol=1e-10):
lhs, rhs, err, _ = make_slip_rate_eqns(
simple_test_rups, simple_test_faults
)
lhs = lhs.todense()
lhs2, rhs2, err, _ = make_rel_gr_mfd_eqns(simple_test_rups, b=1.0)
lhs2 = lhs2.todense()
lhs = np.vstack([lhs, lhs2])
rhs = np.hstack([rhs, rhs2])
soln = np.linalg.solve(lhs, rhs)
np.testing.assert_array_almost_equal(
soln,
np.array(
[3.52359071e-04, 3.52359071e-04, 1.03058514e-04, 2.22850461e-04]
),
)
resids = lhs @ soln - rhs
for resid in resids:
assert np.isclose(resid, 0.0, atol=inversion_tol)
[docs]
def test_make_abs_mfd_eqns_nonnormalized_incremental():
mfd = hz.mfd.TruncatedGRMFD(5.0, 8.0, 0.1, 3.61759073, 1.0)
lhs, rhs, err, _ = make_abs_mfd_eqns(
simple_test_rups, mfd, normalize=False
)
lhs = lhs.todense()
lhs_ = np.array(
[[1.0, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 1.0], [0.0, 0.0, 1.0, 0.0]]
)
rhs_ = np.array([8.52639416e-04, 2.69628258e-04, 8.52639416e-05])
np.testing.assert_array_almost_equal(lhs, lhs_)
np.testing.assert_array_almost_equal(rhs, rhs_)
[docs]
def test_make_abs_mfd_eqns_nonnormalized_cumulative():
mfd = hz.mfd.TruncatedGRMFD(5.0, 8.0, 0.1, 3.61759073, 1.0)
lhs, rhs, err, _ = make_abs_mfd_eqns(
simple_test_rups, mfd, normalize=False, cumulative=True
)
lhs = lhs.todense()
lhs_ = np.array(
[[1.0, 1.0, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 1.0, 0.0]]
)
rhs_ = np.array([0.00410418, 0.00126951, 0.00037311])
np.testing.assert_array_almost_equal(lhs, lhs_)
np.testing.assert_array_almost_equal(rhs, rhs_)
[docs]
def test_make_abs_mfd_eqns_normalized():
mfd = hz.mfd.TruncatedGRMFD(5.0, 8.0, 0.1, 3.61759073, 1.0)
lhs, rhs, err, _ = make_abs_mfd_eqns(simple_test_rups, mfd, normalize=True)
lhs = lhs.todense()
lhs_ = np.array(
[
[3708.810078, 3708.810078, 0.0, 0.0],
[0.0, 0.0, 0.0, 3708.810078],
[0.0, 0.0, 3708.810078, 0.0],
]
)
rhs_ = np.array([3.162278, 1.0, 0.316228])
np.testing.assert_array_almost_equal(lhs, lhs_)
np.testing.assert_array_almost_equal(rhs, rhs_)
[docs]
def test_and_solve_slip_rate_and_abs_mfd_eqns():
lhs, rhs, err, _ = make_slip_rate_eqns(
simple_test_rups, simple_test_faults
)
lhs = lhs.todense()
mfd = hz.mfd.TruncatedGRMFD(5.0, 8.0, 0.1, 3.61759073, 1.0)
lhs2, rhs2, err, _ = make_abs_mfd_eqns(simple_test_rups, mfd)
lhs2 = lhs2.todense()
lhs = np.vstack([lhs, lhs2])
rhs = np.hstack([rhs, rhs2])
soln = np.linalg.lstsq(lhs, rhs, rcond=-1)[0]
resids = lhs @ soln - rhs
[docs]
def test_make_abs_mfd_eqns_bins_rupture_magnitudes_for_rhs_lookup():
# Regression: rupture magnitudes can carry float noise (e.g., 6.1 stored as
# 6.100000000000001), while target MFD keys are discretized/rounded. If the
# rupture magnitudes are not rounded similarly, RHS lookup can return 0.0,
# producing extreme row weights.
rups = [
{"M": 6.1 + 1e-12},
{"M": 6.1 - 1e-12},
]
mfd = {6.1: 1.23e-4}
lhs, rhs, err, _ = make_abs_mfd_eqns(rups, mfd, mag_decimals=1)
lhs = lhs.todense()
np.testing.assert_array_almost_equal(lhs, np.array([[1.0, 1.0]]))
np.testing.assert_array_almost_equal(rhs, np.array([1.23e-4]))
assert float(err[0]) < 1e8
[docs]
def test_make_abs_mfd_eqns_applies_min_mfd_error_floor_for_zero_rates():
# Regression: if a target MFD rate is zero, errors can go to zero and
# weights can explode. `make_abs_mfd_eqns` should apply the default
# `min_mfd_error` floor (1e-5), capping weights at 1e5.
rups = [{"M": 6.0}]
mfd = {6.0: 0.0}
_, rhs, err, _ = make_abs_mfd_eqns(rups, mfd, mag_decimals=1)
np.testing.assert_array_almost_equal(rhs, np.array([0.0]))
np.testing.assert_array_almost_equal(err, np.array([1.0e5]))
[docs]
def test_make_rup_rate_prior_from_fault_abs_mfds_simple():
rups = simple_test_rups
fault_abs_mfds = {
"f1": {
"mfd": {6.0: 2.0e-4, 6.5: 1.0e-4, 7.0: 5.0e-5},
"rups_include": [0, 2, 3],
"rup_fractions": [1.0, 0.5, 0.5],
},
"f2": {
"mfd": {6.0: 2.0e-4, 6.5: 1.0e-4, 7.0: 5.0e-5},
"rups_include": [1, 2, 3],
"rup_fractions": [1.0, 0.5, 0.5],
},
}
priors = make_rup_rate_prior_from_fault_abs_mfds(
fault_abs_mfds=fault_abs_mfds, rups=rups
)
np.testing.assert_allclose(priors["f1"], np.array([2.0e-4, 1.0e-4, 2.0e-4]))
np.testing.assert_allclose(priors["f2"], np.array([2.0e-4, 1.0e-4, 2.0e-4]))
[docs]
def test_make_ridge_regularization_eqns_from_fault_abs_mfds():
rups = simple_test_rups
fault_abs_mfds = {
"f1": {
"mfd": {6.0: 2.0e-4, 6.5: 1.0e-4, 7.0: 5.0e-5},
"rups_include": [0, 2, 3],
"rup_fractions": [1.0, 0.5, 0.5],
},
"f2": {
"mfd": {6.0: 2.0e-4, 6.5: 1.0e-4, 7.0: 5.0e-5},
"rups_include": [1, 2, 3],
"rup_fractions": [1.0, 0.5, 0.5],
},
}
lhs, rhs, err, meta = make_ridge_regularization_eqns_from_fault_abs_mfds(
fault_abs_mfds=fault_abs_mfds,
rups=rups,
ridge=4.0,
)
assert meta["type"] == "ridge_fault_mfd_prior"
np.testing.assert_allclose(
lhs.todense(),
np.array(
[
[1.0, 0.0, 0.0, 0.0], # f1: A
[0.0, 0.0, 1.0, 0.0], # f1: C
[0.0, 0.0, 0.0, 1.0], # f1: D
[0.0, 1.0, 0.0, 0.0], # f2: B
[0.0, 0.0, 1.0, 0.0], # f2: C
[0.0, 0.0, 0.0, 1.0], # f2: D
]
),
)
np.testing.assert_allclose(
rhs, np.array([2.0e-4, 1.0e-4, 2.0e-4, 2.0e-4, 1.0e-4, 2.0e-4])
)
np.testing.assert_allclose(err, np.full(6, 2.0))
[docs]
def test_make_eqns_fault_abs_mfds_ridge_mode_only():
rups = simple_test_rups
fault_abs_mfds = {
"f1": {
"mfd": {6.0: 2.0e-4, 6.5: 1.0e-4, 7.0: 5.0e-5},
"rups_include": [0, 2, 3],
"rup_fractions": [1.0, 0.5, 0.5],
},
"f2": {
"mfd": {6.0: 2.0e-4, 6.5: 1.0e-4, 7.0: 5.0e-5},
"rups_include": [1, 2, 3],
"rup_fractions": [1.0, 0.5, 0.5],
},
}
lhs0, rhs0, err0, _ = make_ridge_regularization_eqns_from_fault_abs_mfds(
fault_abs_mfds=fault_abs_mfds,
rups=rups,
ridge=4.0,
)
lhs, rhs, err = make_eqns(
rups=rups,
faults=None,
slip_rate_eqns=False,
mfd=None,
fault_abs_mfds=fault_abs_mfds,
fault_abs_mfd_mode="ridge",
fault_abs_mfd_ridge=4.0,
ridge=0.0,
return_sparse=False,
)
np.testing.assert_allclose(lhs, lhs0.todense())
np.testing.assert_allclose(rhs, rhs0)
np.testing.assert_allclose(err, err0)
[docs]
def test_make_eqns_fault_abs_mfds_ridge_mode_with_global_ridge():
import scipy.sparse as ssp
from openquake.fnm.inversion.soe_builder import make_ridge_regularization_eqns
rups = simple_test_rups
fault_abs_mfds = {
"f1": {
"mfd": {6.0: 2.0e-4, 6.5: 1.0e-4, 7.0: 5.0e-5},
"rups_include": [0, 2, 3],
"rup_fractions": [1.0, 0.5, 0.5],
},
"f2": {
"mfd": {6.0: 2.0e-4, 6.5: 1.0e-4, 7.0: 5.0e-5},
"rups_include": [1, 2, 3],
"rup_fractions": [1.0, 0.5, 0.5],
},
}
lhs_fault, rhs_fault, err_fault, _ = make_ridge_regularization_eqns_from_fault_abs_mfds(
fault_abs_mfds=fault_abs_mfds,
rups=rups,
ridge=4.0,
)
lhs_global, rhs_global, err_global, _ = make_ridge_regularization_eqns(
rups=rups,
ridge=4.0,
)
lhs_expected = np.vstack([lhs_fault.todense(), lhs_global.todense()])
rhs_expected = np.concatenate([rhs_fault, rhs_global])
err_expected = np.concatenate([err_fault, err_global])
lhs, rhs, err = make_eqns(
rups=rups,
faults=None,
slip_rate_eqns=False,
mfd=None,
fault_abs_mfds=fault_abs_mfds,
fault_abs_mfd_mode="ridge",
fault_abs_mfd_ridge=4.0,
ridge=4.0,
return_sparse=False,
)
np.testing.assert_allclose(lhs, lhs_expected)
np.testing.assert_allclose(rhs, rhs_expected)
np.testing.assert_allclose(err, err_expected)
[docs]
def test_make_abs_mfd_eqns_faults():
total_mfd = hz.mfd.TruncatedGRMFD(5.0, 7.1, 0.1, 3.61759073, 1.0)
f0_mfd = hz.mfd.TruncatedGRMFD.from_moment(
5.0, 7.1, 0.1, 1.0, total_mfd._get_total_moment_rate() / 2
)
f1_mfd = hz.mfd.TruncatedGRMFD.from_moment(
5.0, 7.1, 0.1, 1.0, total_mfd._get_total_moment_rate() / 2
)
fault_mfds = {
'f1': {
'mfd': f0_mfd,
'rups_include': [0, 2, 3],
'rup_fractions': [1.0, 0.5, 0.5],
},
'f2': {
'mfd': f1_mfd,
'rups_include': [1, 2, 3],
'rup_fractions': [1.0, 0.5, 0.5],
},
}
lhs0, rhs0, err0, _ = make_abs_mfd_eqns(
simple_test_rups,
fault_mfds['f1']['mfd'],
rup_include_list=fault_mfds['f1']['rups_include'],
rup_fractions=fault_mfds['f1']['rup_fractions'],
)
lhs0 = lhs0.todense()
lhs1, rhs1, err1, _ = make_abs_mfd_eqns(
simple_test_rups,
fault_mfds['f2']['mfd'],
rup_include_list=fault_mfds['f2']['rups_include'],
rup_fractions=fault_mfds['f2']['rup_fractions'],
)
lhs1 = lhs1.todense()
np.testing.assert_equal(
lhs0,
np.array(
[[1.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.5], [0.0, 0.0, 0.5, 0.0]]
),
)
np.testing.assert_allclose(
rhs0, np.array([3.46094034e-04, 1.09444543e-04, 3.46094034e-05])
)
np.testing.assert_equal(
lhs1,
np.array(
[[0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.5], [0.0, 0.0, 0.5, 0.0]]
),
)
np.testing.assert_allclose(
rhs1, np.array([3.46094034e-04, 1.09444543e-04, 3.46094034e-05])
)
lhsm = np.vstack((lhs0, lhs1))
rhsm = np.hstack((rhs0, rhs1))
np.testing.assert_equal(
lhsm,
np.array(
[
[1.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.5],
[0.0, 0.0, 0.5, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.5],
[0.0, 0.0, 0.5, 0.0],
]
),
)
np.testing.assert_allclose(
rhsm,
np.array(
[
3.46094034e-04,
1.09444543e-04,
3.46094034e-05,
3.46094034e-04,
1.09444543e-04,
3.46094034e-05,
]
),
)
[docs]
def test_make_eqns_fault_mfds_only():
total_mfd = hz.mfd.TruncatedGRMFD(5.0, 7.1, 0.1, 3.61759073, 1.0)
f0_mfd = hz.mfd.TruncatedGRMFD.from_moment(
5.0, 7.1, 0.1, 1.0, total_mfd._get_total_moment_rate() / 2
)
f1_mfd = hz.mfd.TruncatedGRMFD.from_moment(
5.0, 7.1, 0.1, 1.0, total_mfd._get_total_moment_rate() / 2
)
fault_mfds = {
'f0': {
'mfd': f0_mfd,
'rups_include': [0, 2, 3],
'rup_fractions': [1.0, 0.5, 0.5],
},
'f1': {
'mfd': f1_mfd,
'rups_include': [1, 2, 3],
'rup_fractions': [1.0, 0.5, 0.5],
},
}
lhs, rhs, err = make_eqns(
simple_test_rups,
faults=None,
mfd=None,
slip_rate_eqns=None,
fault_abs_mfds=fault_mfds,
return_sparse=False,
)
np.testing.assert_equal(
lhs,
np.array(
[
[1.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.5],
[0.0, 0.0, 0.5, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.5],
[0.0, 0.0, 0.5, 0.0],
]
),
)
np.testing.assert_allclose(
rhs,
np.array(
[
3.46094034e-04,
1.09444543e-04,
3.46094034e-05,
3.46094034e-04,
1.09444543e-04,
3.46094034e-05,
]
),
)
[docs]
def test_make_eqns_abs_and_fault_mfds():
total_mfd = hz.mfd.TruncatedGRMFD(5.0, 7.1, 0.1, 3.61759073, 1.0)
f0_mfd = hz.mfd.TruncatedGRMFD.from_moment(
5.0, 7.1, 0.1, 1.0, total_mfd._get_total_moment_rate() / 2
)
f1_mfd = hz.mfd.TruncatedGRMFD.from_moment(
5.0, 7.1, 0.1, 1.0, total_mfd._get_total_moment_rate() / 2
)
fault_mfds = {
'f0': {
'mfd': f0_mfd,
'rups_include': [0, 2, 3],
'rup_fractions': [1.0, 0.5, 0.5],
},
'f1': {
'mfd': f1_mfd,
'rups_include': [1, 2, 3],
'rup_fractions': [1.0, 0.5, 0.5],
},
}
lhs, rhs, err = make_eqns(
simple_test_rups,
faults=None,
mfd=total_mfd,
slip_rate_eqns=None,
fault_abs_mfds=fault_mfds,
return_sparse=False,
)
np.testing.assert_equal(
lhs,
np.array(
[
[1.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 1.0, 0.0],
[1.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.5],
[0.0, 0.0, 0.5, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.5],
[0.0, 0.0, 0.5, 0.0],
]
),
)
np.testing.assert_allclose(
rhs,
np.array(
[
8.52639416e-04,
2.69628258e-04,
8.52639416e-05,
3.46094034e-04,
1.09444543e-04,
3.46094034e-05,
3.46094034e-04,
1.09444543e-04,
3.46094034e-05,
]
),
)
[docs]
class TestEqnsFromLilFaults(unittest.TestCase):
[docs]
def setUp(self):
TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data")
FAULT_FILE = os.path.join(TEST_DATA_DIR, "lil_test_faults.geojson")
settings = {
"subsection_size": [12.0, 10.0],
"lower_seis_depth": 10.0,
"calculate_rates_from_slip_rates": True,
"filter_by_plausibility": False,
"export_fault_mfds": True,
# Avoid multiprocessing in unit tests; some CI/sandboxed
# environments disallow POSIX semaphores used by ProcessPool.
"parallel_subfault_build": False,
}
self.fault_network = build_fault_network(
fault_geojson=FAULT_FILE, settings=settings
)
self.fault_network['subfault_df']['moment'] = self.fault_network[
'subfault_df'
].apply(get_fault_moment_rate, axis=1)
self.rups = rup_df_to_rupture_dicts(
self.fault_network['rupture_df'],
mag_col="mag",
displacement_col="displacement",
)
self.faults = subsection_df_to_fault_dicts(
self.fault_network["subfault_df"],
slip_rate_col="net_slip_rate",
slip_rate_err_col="net_slip_rate_err",
)
[docs]
def test_make_equations_just_slip_rates(self):
lhs, rhs, err = make_eqns(
rups=self.rups,
faults=self.faults,
return_sparse=False,
)
np.testing.assert_almost_equal(
lhs,
np.array(
[
[0.397, 0.559, 0.0, 0.0, 0.564],
[0.0, 0.559, 0.397, 0.0, 0.564],
[0.0, 0.0, 0.0, 0.351, 0.564],
]
),
)
np.testing.assert_almost_equal(rhs, np.array([0.001, 0.001, 0.001]))
np.testing.assert_almost_equal(
err, np.array([2000.0, 2000.0, 10000.0])
)
[docs]
def test_make_fault_mfd_equation_components_no_scale(self):
fault_abs_mfds = make_fault_mfd_equation_components(
self.fault_network['fault_mfds'],
self.rups,
self.fault_network,
fault_key='subfaults',
rup_key='rupture_df',
seismic_slip_rate_frac=1.0,
full_counting=False,
)
fault_abs_mfds_correct = {
0: {
'mfd': {
5.05: 0.0012835881512141293,
5.1499999999999995: 0.001019590310266925,
5.249999999999999: 0.0008098893712963095,
5.349999999999999: 0.0006433179946237562,
5.449999999999998: 0.0005110056470358534,
5.549999999999998: 0.0004059062135441289,
5.649999999999998: 0.00032242276606812554,
5.749999999999997: 0.0002561095066058142,
5.849999999999997: 0.00020343501227830345,
5.949999999999997: 0.00016159417418413742,
6.049999999999996: 0.00012835881512141402,
6.149999999999996: 0.0001019590310266933,
6.249999999999996: 8.098893712963159e-05,
6.349999999999995: 6.433179946237612e-05,
6.449999999999995: 5.110056470358578e-05,
},
'rups_include': [0, 1, 4],
'rup_fractions': [1.0, 0.4997, 0.3569],
},
1: {
'mfd': {
5.05: 0.0012851614241037607,
5.1499999999999995: 0.001020840005344084,
5.249999999999999: 0.0008108820393808937,
5.349999999999999: 0.0006441064989110507,
5.449999999999998: 0.0005116319782544529,
5.549999999999998: 0.0004064037261153517,
5.649999999999998: 0.00032281795435057815,
5.749999999999997: 0.0002564234158165986,
5.849999999999997: 0.00020368435922756922,
5.949999999999997: 0.00016179223750618173,
6.049999999999996: 0.00012851614241037707,
6.149999999999996: 0.00010208400053440928,
6.249999999999996: 8.108820393808999e-05,
6.349999999999995: 6.441064989110565e-05,
6.449999999999995: 5.116319782544568e-05,
},
'rups_include': [1, 2, 4],
'rup_fractions': [0.5003, 1.0, 0.3573],
},
2: {
'mfd': {
5.05: 0.0010281237538428759,
5.1499999999999995: 0.0008166677264681136,
5.249999999999999: 0.0006487022335217099,
5.349999999999999: 0.0005152825000149987,
5.449999999999998: 0.0004093034386212287,
5.549999999999998: 0.0003251212778665792,
5.649999999999998: 0.00025825301071906274,
5.749999999999997: 0.00020513765811670413,
5.849999999999997: 0.000162946633847315,
5.949999999999997: 0.00012943311201820166,
6.049999999999996: 0.00010281237538428841,
6.149999999999996: 8.166677264681202e-05,
6.249999999999996: 6.487022335217152e-05,
6.349999999999995: 5.152825000150028e-05,
6.449999999999995: 4.093034386212321e-05,
},
'rups_include': [3, 4],
'rup_fractions': [1.0, 0.2858],
},
}
for fault_key, mfd_stuff in fault_abs_mfds.items():
for key, test_value in mfd_stuff.items():
if key == 'mfd':
test_mfd = get_mfd_occurrence_rates(
fault_abs_mfds[fault_key][key]
)
np.testing.assert_almost_equal(
np.array(
sorted(test_mfd.keys())
),
np.array(
sorted(
fault_abs_mfds_correct[fault_key][key].keys()
)
),
)
np.testing.assert_almost_equal(
np.array(
sorted(test_mfd.values())
),
np.array(
sorted(
fault_abs_mfds_correct[fault_key][key].values()
)
),
)
else:
assert (
fault_abs_mfds[fault_key][key]
== fault_abs_mfds_correct[fault_key][key]
)
[docs]
def test_make_fault_mfd_equation_components_full_counting(self):
fault_abs_mfds = make_fault_mfd_equation_components(
self.fault_network['fault_mfds'],
self.rups,
self.fault_network,
fault_key='subfaults',
rup_key='rupture_df',
seismic_slip_rate_frac=1.0,
full_counting=True,
)
for _, mfd_stuff in fault_abs_mfds.items():
assert all(frac == 1.0 for frac in mfd_stuff['rup_fractions'])
[docs]
def test_make_fault_rel_mfd_equation_components_no_scale(self):
fault_rel_mfds = make_fault_rel_mfd_equation_components(
self.rups,
self.fault_network,
fault_key='subfaults',
rup_key='rupture_df',
full_counting=False,
)
expected = {
0: {
'b_value': 1.0,
'rups_include': [0, 1, 4],
'rup_fractions': [1.0, 0.4997, 0.3569],
},
1: {
'b_value': 1.0,
'rups_include': [1, 2, 4],
'rup_fractions': [0.5003, 1.0, 0.3573],
},
2: {
'b_value': 1.0,
'rups_include': [3, 4],
'rup_fractions': [1.0, 0.2858],
},
}
assert fault_rel_mfds == expected
[docs]
def test_make_fault_rel_mfd_equation_components_full_counting(self):
fault_rel_mfds = make_fault_rel_mfd_equation_components(
self.rups,
self.fault_network,
fault_key='subfaults',
rup_key='rupture_df',
full_counting=True,
)
for _, mfd_stuff in fault_rel_mfds.items():
assert all(frac == 1.0 for frac in mfd_stuff['rup_fractions'])
[docs]
def test_make_fault_rel_mfd_equation_components_b_value_scalar(self):
fault_rel_mfds = make_fault_rel_mfd_equation_components(
self.rups,
self.fault_network,
fault_key='subfaults',
rup_key='rupture_df',
b_value=0.9,
full_counting=False,
)
for _, mfd_stuff in fault_rel_mfds.items():
assert mfd_stuff['b_value'] == 0.9
[docs]
def test_make_fault_rel_mfd_equation_components_b_value_sequence(self):
fault_rel_mfds = make_fault_rel_mfd_equation_components(
self.rups,
self.fault_network,
fault_key='subfaults',
rup_key='rupture_df',
b_value=[0.8, 1.0, 1.2],
full_counting=False,
)
assert fault_rel_mfds[0]['b_value'] == 0.8
assert fault_rel_mfds[1]['b_value'] == 1.0
assert fault_rel_mfds[2]['b_value'] == 1.2
[docs]
def test_make_equations_from_fault_mfds(self):
total_fault_moment = self.fault_network['subfault_df']['moment'].sum()
fault_abs_mfds = make_fault_mfd_equation_components(
self.fault_network['fault_mfds'],
self.rups,
self.fault_network,
fault_key='subfaults',
rup_key='rupture_df',
seismic_slip_rate_frac=1.0,
full_counting=False,
)
lhs, rhs, err = make_eqns(
rups=self.rups,
faults=None,
slip_rate_eqns=False,
mfd=None,
fault_abs_mfds=fault_abs_mfds,
return_sparse=False,
)
np.testing.assert_almost_equal(
lhs,
np.array(
[
[0.0, 0.0, 0.0, 0.0, 0.0],
[1.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.4997, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.3569],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0, 0.0],
[0.0, 0.5003, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.3573],
[0.0, 0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.2858],
]
),
),
np.testing.assert_almost_equal(
rhs,
np.array(
[
1.28358815e-04,
1.01959031e-04,
5.11005647e-05,
0.00000000e00,
1.28516142e-04,
1.02084001e-04,
5.11631978e-05,
0.00000000e00,
1.02812375e-04,
8.16667726e-05,
4.09303439e-05,
0.00000000e00,
]
),
)
np.testing.assert_almost_equal(
err,
np.array(
[
8.82647205e01,
9.90346453e01,
1.39890155e02,
1.00000000e05,
8.82106779e01,
9.89740084e01,
1.39804503e02,
1.00000000e05,
9.86227943e01,
1.10656595e02,
1.56306595e02,
1.00000000e05,
]
),
decimal=3,
)
[docs]
def test_make_equations_from_fault_abs_mfds(self):
total_fault_moment = self.fault_network['subfault_df']['moment'].sum()
total_abs_mfd = hz.mfd.TruncatedGRMFD.from_moment(
min_mag=5.9,
max_mag=6.6,
bin_width=0.1,
b_val=1.0,
moment_rate=total_fault_moment,
)
fault_abs_mfds = make_fault_mfd_equation_components(
self.fault_network['fault_mfds'],
self.rups,
self.fault_network,
fault_key='subfaults',
rup_key='rupture_df',
seismic_slip_rate_frac=1.0,
full_counting=False,
)
lhs, rhs, err = make_eqns(
rups=self.rups,
faults=None,
slip_rate_eqns=False,
mfd=total_abs_mfd,
fault_abs_mfds=fault_abs_mfds,
return_sparse=False,
)
np.testing.assert_almost_equal(
lhs,
np.array(
[
[0.0, 0.0, 0.0, 1.0, 0.0],
[1.0, 0.0, 1.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[1.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.4997, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.3569],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0, 0.0],
[0.0, 0.5003, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.3573],
[0.0, 0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.2858],
]
),
)
np.testing.assert_almost_equal(
rhs,
np.array(
[
4.57959562e-04,
3.63770211e-04,
1.82316986e-04,
1.44819529e-04,
1.28358815e-04,
1.01959031e-04,
5.11005647e-05,
0.00000000e00,
1.28516142e-04,
1.02084001e-04,
5.11631978e-05,
0.00000000e00,
1.02812375e-04,
8.16667726e-05,
4.09303439e-05,
0.00000000e00,
]
),
decimal=3,
)
np.testing.assert_almost_equal(
err,
np.array(
[
4.67289943e01,
5.24307939e01,
7.40604649e01,
8.30972084e01,
8.82647205e01,
9.90346453e01,
1.39890155e02,
1.00000000e05,
8.82106779e01,
9.89740084e01,
1.39804503e02,
1.00000000e05,
9.86227943e01,
1.10656595e02,
1.56306595e02,
1.00000000e05,
]
),
decimal=3,
)
[docs]
def test_mean_slip_rate(self):
msr = mean_slip_rate(self.rups[4]['faults'], self.faults)
np.testing.assert_approx_equal(msr, 1.0)
[docs]
def test_get_fault_moment(self):
fault_moment = get_fault_moment(self.faults)
np.testing.assert_approx_equal(
fault_moment, 1.1186199511831996e16, significant=4
)
[docs]
def test_get_slip_rate_fraction(self):
fault_moment = get_fault_moment(self.faults)
print(fault_moment)
total_abs_mfd = hz.mfd.TaperedGRMFD.from_moment(
min_mag=5.9,
max_mag=6.6,
corner_mag=6.3,
bin_width=0.1,
b_val=1.0,
moment_rate=fault_moment,
)
np.testing.assert_approx_equal(
get_slip_rate_fraction(self.faults, total_abs_mfd),
1.0,
significant=3,
)
# this is for reference
rups = [
{
'idx': 0,
'M': 6.1,
'D': 0.397,
'faults': [0],
'faults_orig': {'f1': 1.0},
'subfault_fracs': {0: 1.0},
},
{
'idx': 1,
'M': 6.4,
'D': 0.559,
'faults': [0, 1],
'faults_orig': {'f1': 1.0},
'subfault_fracs': {0: 0.4997, 1: 0.5003},
},
{
'idx': 2,
'M': 6.1,
'D': 0.397,
'faults': [1],
'faults_orig': {'f1': 1.0},
'subfault_fracs': {1: 1.0},
},
{
'idx': 3,
'M': 6.0,
'D': 0.351,
'faults': [2],
'faults_orig': {'f2': 1.0},
'subfault_fracs': {2: 1.0},
},
{
'idx': 4,
'M': 6.5,
'D': 0.564,
'faults': [0, 1, 2],
'faults_orig': {'f1': 0.7, 'f2': 0.3},
'subfault_fracs': {0: 0.3569, 1: 0.3573, 2: 0.2858},
},
]
faults = [
{
'id': 0,
'slip_rate': 1.0,
'slip_rate_err': 0.5,
'trace': [
[-122.6737, 45.48704, 0.0],
[-122.69758583405802, 45.520564357112974, 0.0],
[-122.72921077819535, 45.55061628429187, 0.0],
[-122.762795159138, 45.5797933832881, 0.0],
],
'area': 124.74786985561391,
},
{
'id': 1,
'slip_rate': 1.0,
'slip_rate_err': 0.5,
'trace': [
[-122.762795159138, 45.5797933832881, 0.0],
[-122.79641974866986, 45.60895764186604, 0.0],
[-122.83010988063222, 45.63809472923184, 0.0],
[-122.86391574284539, 45.66717618379236, 0.0],
],
'area': 124.90077126834305,
},
{
'id': 2,
'slip_rate': 1.0,
'slip_rate_err': 0.1,
'trace': [
[-122.51594000000001, 45.47618, 0.0],
[-122.58006299150225, 45.47668892736773, 0.0],
[-122.64418710170465, 45.477161977220895, 0.0],
],
'area': 99.9200936207929,
},
]