diff --git a/doc/api-reference/openquake.hazardlib.gsim.rst b/doc/api-reference/openquake.hazardlib.gsim.rst
index 1a5d506504bb..a9881592529b 100644
--- a/doc/api-reference/openquake.hazardlib.gsim.rst
+++ b/doc/api-reference/openquake.hazardlib.gsim.rst
@@ -536,6 +536,14 @@ coeffs_table
:undoc-members:
:show-inheritance:
+conditional_gmpe
+-----------------------------------------------
+
+.. automodule:: openquake.hazardlib.gsim.conditional_gmpe
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
convertito_2012
-----------------------------------------------
@@ -880,6 +888,14 @@ manea_2021
:undoc-members:
:show-inheritance:
+macedo_2019
+----------------------------------------------------
+
+.. automodule:: openquake.hazardlib.gsim.macedo_2019
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
mcverry_2006
--------------------------------------------
diff --git a/openquake/calculators/tests/classical_test.py b/openquake/calculators/tests/classical_test.py
index 1e306635da69..15e8b8e7c01a 100644
--- a/openquake/calculators/tests/classical_test.py
+++ b/openquake/calculators/tests/classical_test.py
@@ -39,7 +39,7 @@
case_50, case_51, case_53, case_54, case_55, case_57,
case_60, case_61, case_62, case_63, case_64, case_65, case_66,
case_67, case_69, case_70, case_72, case_74, case_75, case_76, case_77,
- case_78, case_80, case_81, case_82, case_83, case_84, case_86)
+ case_78, case_80, case_81, case_82, case_83, case_84, case_85, case_86)
ae = numpy.testing.assert_equal
aac = numpy.testing.assert_allclose
@@ -781,6 +781,12 @@ def test_case_84(self):
[f] = export(('mean_rates_by_src', 'csv'), self.calc.datastore)
self.assertEqualFiles('expected/rbs.csv', f)
+ def test_case_85(self):
+ # Conditional GMM (Macedo et al. (2019)) for Arias Inensity
+ self.run_calc(case_85.__file__, 'job.ini')
+ [f1] = export(('hcurves/mean', 'csv'), self.calc.datastore)
+ self.assertEqualFiles('expected/hazard_curve-mean-IA.csv', f1)
+
def test_case_86(self):
# Comparing the revised indirect GMPE and the direct AvgSA GMPE
# for AvgSA at multiple spectral periods
diff --git a/openquake/hazardlib/gsim/base.py b/openquake/hazardlib/gsim/base.py
index cd46323c0277..aa3e4dbdaf14 100644
--- a/openquake/hazardlib/gsim/base.py
+++ b/openquake/hazardlib/gsim/base.py
@@ -81,7 +81,7 @@ class AdaptedWarning(UserWarning):
OK_METHODS = ('compute', 'get_mean_and_stddevs', 'set_poes', 'requires',
- 'set_parameters', 'set_tables')
+ 'set_parameters', 'set_tables', 'get_conditioning_ground_motions')
def bad_methods(clsdict):
diff --git a/openquake/hazardlib/gsim/conditional_gmpe.py b/openquake/hazardlib/gsim/conditional_gmpe.py
new file mode 100644
index 000000000000..1718e5727782
--- /dev/null
+++ b/openquake/hazardlib/gsim/conditional_gmpe.py
@@ -0,0 +1,163 @@
+# -*- coding: utf-8 -*-
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright (C) 2015-2023 GEM Foundation
+#
+# OpenQuake 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.
+#
+# OpenQuake 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 OpenQuake. If not, see .
+"""
+Module exports: :class:`ConditionalGMPE`
+"""
+
+
+from typing import Tuple
+import numpy as np
+from openquake.hazardlib import const
+from openquake.hazardlib.imt import from_string
+from openquake.hazardlib.gsim.base import GMPE, registry
+
+
+class ConditionalGMPE(GMPE):
+ """Base Class for a form of GMPE in which the output ground
+ motion level is conditional upon other measures of ground motion.
+
+ This class functions for two cases:
+
+ 1. The case that the conditioning ground motion values (e.g. PGA, Sa(T)
+ etc.) are known a priori and input via the context array. If so, these must
+ be specified in the `ctx` recarray with both the MEAN and TOTAL_STDDEV (the
+ TOTAL_STDDEV can be 0), e.g. PGA_MEAN, PGA_TOTAL_STDDEV, SA(0.2)_MEAN,
+ SA(0.2)_TOTAL_STDDEV etc. The IMT string must be such that it can be
+ transformed into an IMT object via the `from_string` function. Optionally,
+ the between- and within-event standard deviation of the input ground
+ motions can also be specified using the same syntax, i.e.
+ PGA_INTER_EVENT_STDDEV, SA(1.0)_INTRA_EVENT_STDDEV etc.
+
+ 2. The case that the conditioning groung motion values are not known a
+ priori and must therefore be calculated using a GMPE, which the user passes
+ as input to the function.
+
+ If no conditioning ground motion values are input in `ctx` and no GMPE is
+ specified then an error will be raised.
+ """
+ REQUIRES_SITES_PARAMETERS = set()
+ REQUIRES_DISTANCES = set()
+ REQUIRES_RUPTURE_PARAMETERS = set()
+ DEFINED_FOR_INTENSITY_MEASURE_TYPES = set()
+ DEFINED_FOR_INTENSITY_MEASURE_COMPONENT = ''
+ DEFINED_FOR_STANDARD_DEVIATION_TYPES = {const.StdDev.TOTAL}
+ DEFINED_FOR_TECTONIC_REGION_TYPE = ''
+ DEFINED_FOR_REFERENCE_VELOCITY = None
+
+ # Specific to the Conditional GMPE class. Should be a set
+ # containing string representations of the required IMTs
+ REQUIRES_IMTS = set()
+
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+
+ if "gmpe" in kwargs:
+ # Create the original GMPE
+ [(gmpe_name, kw)] = kwargs.pop('gmpe').items()
+ self.params = kwargs # non-gmpe parameters
+ g = globals()
+ for k in self.params:
+ if k not in g:
+ raise ValueError('Unknown %r in ModifiableGMPE' % k)
+ self.gmpe = registry[gmpe_name](**kw)
+ if hasattr(self.gmpe, 'gmpe_table'):
+ self.gmpe_table = self.gmpe.gmpe_table
+ self.REQUIRES_DISTANCES = frozenset(self.REQUIRES_DISTANCES |
+ self.gmpe.REQUIRES_DISTANCES)
+ self.REQUIRES_RUPTURE_PARAMETERS = frozenset(
+ self.REQUIRES_RUPTURE_PARAMETERS |
+ self.gmpe.REQUIRES_RUPTURE_PARAMETERS)
+ self.REQUIRES_SITES_PARAMETERS = frozenset(
+ self.REQUIRES_SITES_PARAMETERS |
+ self.gmpe.REQUIRES_SITES_PARAMETERS)
+ self.DEFINED_FOR_INTENSITY_MEASURE_TYPES = frozenset(
+ self.DEFINED_FOR_INTENSITY_MEASURE_TYPES |
+ self.gmpe.DEFINED_FOR_INTENSITY_MEASURE_TYPES)
+ else:
+ self.gmpe = None
+
+ def get_conditioning_ground_motions(
+ self,
+ ctx: np.recarray
+ ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
+ """Retreives the ground motions upon which the model
+ is conditioned. If the MEAN and TOTAL_STDDEV of the ground
+ motion are found in the ctx then these are taken directly,
+ otherwise the mean and total standard deviation are determined
+ from the specified GMPE
+ """
+ imt_dtypes = np.dtype([(imt, float) for imt in self.REQUIRES_IMTS])
+
+ nimts = len(self.REQUIRES_IMTS)
+ n = len(ctx)
+ mean_gms = np.recarray(n, imt_dtypes)
+ sigma_gms = np.recarray(n, imt_dtypes)
+ tau_gms = np.recarray(n, imt_dtypes)
+ phi_gms = np.recarray(n, imt_dtypes)
+
+ for imt_string in self.REQUIRES_IMTS:
+ # Get the mean ground motions and total standard deviations
+ # for the IMT from the ctx if they are provided
+ available_gms = []
+ for imt_string in self.REQUIRES_IMTS:
+ for param in ["MEAN", "TOTAL_STDDEV"]:
+ label = f"{imt_string}_{param}"
+ available_gms.append(label in ctx.dtype.names)
+ if all(available_gms):
+ # The required info about the ground motions
+ # is in the ctx - therefore this can be taken directly
+ for imt_string in self.REQUIRES_IMTS:
+ mean_gms[imt_string] = ctx[f"{imt_string}_MEAN"]
+ sigma_gms[imt_string] = ctx[f"{imt_string}_TOTAL_STDDEV"]
+ # Optionally, get the between and within-event stddev
+ if (f"{imt_string}_INTER_EVENT_STDDEV") in ctx.dtype.names:
+ tau_gms[imt_string] = ctx[
+ f"{imt_string}_INTER_EVENT_STDDEV"]
+ else:
+ tau_gms[imt_string] = np.zeros(n)
+ if (f"{imt_string}_INTRA_EVENT_STDDEV") in ctx.dtype.names:
+ phi_gms[imt_string] = ctx[
+ f"{imt_string}_INTRA_EVENT_STDDEV"]
+ else:
+ phi_gms[imt_string] = np.zeros(n)
+ else:
+ # Not conditioned on observations found in ctx, so
+ # calculate from GMPE
+ if self.gmpe is None:
+ raise ValueError("Conditioning ground motions must be "
+ "specified in ctx or a GMPE must be "
+ "provided")
+
+ mean = np.zeros([nimts, n])
+ sigma = np.zeros_like(mean)
+ tau = np.zeros_like(mean)
+ phi = np.zeros_like(mean)
+ self.gmpe.compute(
+ ctx,
+ [from_string(imt) for imt in self.REQUIRES_IMTS],
+ mean,
+ sigma,
+ tau,
+ phi
+ )
+ for i, imt in enumerate(self.REQUIRES_IMTS):
+ mean_gms[imt] = np.exp(mean[i, :])
+ sigma_gms[imt] = sigma[i, :]
+ tau_gms[imt] = tau[i, :]
+ phi_gms[imt] = phi[i, :]
+ return mean_gms, sigma_gms, tau_gms, phi_gms
diff --git a/openquake/hazardlib/gsim/macedo_2019.py b/openquake/hazardlib/gsim/macedo_2019.py
new file mode 100644
index 000000000000..6d9ff2097afc
--- /dev/null
+++ b/openquake/hazardlib/gsim/macedo_2019.py
@@ -0,0 +1,251 @@
+# -*- coding: utf-8 -*-
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright (C) 2015-2023 GEM Foundation
+#
+# OpenQuake 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.
+#
+# OpenQuake 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 OpenQuake. If not, see .
+"""
+Module exports: :class:`MacedoEtAl2019SInter`,
+ :class:`MacedoEtAl2019SSlab`
+"""
+
+from typing import Dict, List, Tuple
+import numpy as np
+from openquake.hazardlib import const
+from openquake.hazardlib.imt import IMT, IA, PGA, SA
+from openquake.hazardlib.gsim.base import add_alias
+from openquake.hazardlib.gsim.conditional_gmpe import ConditionalGMPE
+
+
+# The Macedo et al. (2019) GMM is period-independent, so all constants
+# can be set as a standard dictionary
+CONSTANTS = {
+ "sinter": {
+ # Global from Table 1
+ "Global": {
+ "c1": 0.85, "c2": -0.36, "c3": 0.53, "c4": 1.54,
+ "c5": 0.17, "phi": 0.30, "tau_region": 0.04, "tau": 0.18,
+ },
+ # Regional coefficients for Interface events from Table 2
+ "Japan": {
+ "c1": 0.98, "c2": -0.38, "c3": 0.53, "c4": 1.54,
+ "c5": 0.17, "phi": 0.3, "tau_region": 0.0, "tau": 0.16,
+ },
+ "Taiwan": {
+ "c1": 0.75, "c2": -0.35, "c3": 0.53, "c4": 1.54,
+ "c5": 0.17, "phi": 0.27, "tau_region": 0.0, "tau": 0.15,
+ },
+ "South America": {
+ "c1": 0.95, "c2": -0.36, "c3": 0.53, "c4": 1.54,
+ "c5": 0.17, "phi": 0.32, "tau_region": 0.0, "tau": 0.19,
+ },
+ "New Zealand": {
+ "c1": 0.82, "c2": -0.36, "c3": 0.53, "c4": 1.54,
+ "c5": 0.17, "phi": 0.28, "tau_region": 0.0, "tau": 0.17,
+ },
+ },
+ "sslab": {
+ # Global from Table 1
+ "Global": {
+ "c1": -0.74, "c2": -0.24, "c3": 0.66, "c4": 1.58,
+ "c5": 0.14, "phi": 0.28, "tau_region": 0.03, "tau": 0.16
+ },
+ # Regional coefficients for Interface events from Table 3
+ "Japan": {
+ "c1": -0.22, "c2": -0.32, "c3": 0.66, "c4": 1.58,
+ "c5": 0.14, "phi": 0.26, "tau_region": 0.0, "tau": 0.15,
+ },
+ "Taiwan": {
+ "c1": -1.02, "c2": -0.20, "c3": 0.66, "c4": 1.58,
+ "c5": 0.14, "phi": 0.29, "tau_region": 0.0, "tau": 0.17,
+ },
+ "South America": {
+ "c1": -0.75, "c2": -0.24, "c3": 0.66, "c4": 1.58,
+ "c5": 0.14, "phi": 0.30, "tau_region": 0.0, "tau": 0.14,
+ },
+ "New Zealand": {
+ "c1": -0.84, "c2": -0.22, "c3": 0.66, "c4": 1.58,
+ "c5": 0.14, "phi": 0.3, "tau_region": 0.0, "tau": 0.13,
+ },
+ }
+}
+
+
+def get_mean_conditional_arias_intensity(
+ C: Dict,
+ ctx: np.recarray,
+ imt: IMT,
+ mean_gms: np.recarray
+) -> np.ndarray:
+ """Returns the Arias Intensity (Equation 2)
+ """
+ assert str(imt) == "IA"
+ return C["c1"] + C["c2"] * np.log(ctx.vs30) +\
+ C["c3"] * ctx.mag + C["c4"] * np.log(mean_gms["PGA"]) +\
+ C["c5"] * np.log(mean_gms["SA(1.0)"])
+
+
+def get_stddev_component(
+ C: Dict,
+ sig_ia_cond: float,
+ sig_pga: float,
+ sig_sa1: float,
+ rho: float
+) -> float:
+ """Returns the standard deviation using Equation 6. Assume
+ this can apply to all three components of stddev
+ """
+ return np.sqrt(
+ sig_ia_cond ** 2.0 +
+ (sig_pga ** 2.0) * (C["c4"] ** 2.0) +
+ (sig_sa1 ** 2.0) * (C["c5"] ** 2.0) +
+ (2.0 * (rho * sig_pga * sig_sa1) * C["c4"] * C["c5"])
+ )
+
+
+def get_standard_deviations(
+ C: Dict,
+ kind: str,
+ rho_pga_sa1: float,
+ sigma_gms: np.recarray,
+ tau_gms: np.recarray,
+ phi_gms: np.recarray
+) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:
+ """Returns the total standard deviation and, if specified
+ by the input ground motions, the between and within-event
+ standard deviations
+ """
+ sigma_ia_cond = 0.36 if kind == "sinter" else 0.33
+ # Gets the total standard deviation
+ sigma = get_stddev_component(C, sigma_ia_cond, sigma_gms["PGA"],
+ sigma_gms["SA(1.0)"], rho_pga_sa1)
+ if np.any(tau_gms["PGA"] >= 0.0) or np.any(tau_gms["SA(1.0)"] > 0.0):
+ # If provided by the conditioning ground motion, get the
+ # between-event standard deviation
+ tau = get_stddev_component(
+ C,
+ np.sqrt(C["tau"] ** 2.0 + C["tau_region"] ** 2.0),
+ tau_gms["PGA"],
+ tau_gms["SA(1.0)"],
+ rho_pga_sa1)
+ else:
+ tau = 0.0
+ if np.any(phi_gms["PGA"] >= 0.0) or np.any(phi_gms["SA(1.0)"] > 0.0):
+ # If provided by the conditioning ground motion, get the
+ # within-event standard deviation
+ phi = get_stddev_component(C, C["phi"], phi_gms["PGA"],
+ phi_gms["SA(1.0)"], rho_pga_sa1)
+ else:
+ phi = 0.0
+ return sigma, tau, phi
+
+
+class MacedoEtAl2019SInter(ConditionalGMPE):
+ """Implementation of a conditional GMPE of Macedo, Abrahamson & Bray (2019)
+ for Arias Intensity, applied to subduction interface earthquakes. This
+ requires characterisation of the PGA and SA(1.0), in addition to magnitude
+ and vs30, for defining Arias intensity, and propagates uncertainty
+ accordingly. The model includes specific regionalisations for "Global"
+ application (default), "Japan", "Taiwan", "New Zealand" and
+ "South America", as well as a user customisable coefficient of correlation
+ between PGA and SA(1.0).
+
+ Macedo J, Abrahamson N, Bray JD (2019) "Arias Intensity Conditional
+ Scaling Ground-Motion Models for Subduction Zones", Bulletin of the
+ Seismological Society of America, 109(4): 1343 - 1357
+
+ """
+ DEFINED_FOR_TECTONIC_REGION_TYPE = const.TRT.SUBDUCTION_INTERFACE
+ DEFINED_FOR_INTENSITY_MEASURE_TYPES = {IA, PGA, SA}
+
+ # It is unclear to me if the CGMM is for a specific component of Arias
+ # Intensity; however it's fit using NGA Subduction data, which assumes
+ # PGA and SA are in terms of RotD50
+ DEFINED_FOR_INTENSITY_MEASURE_COMPONENT = const.IMC.RotD50
+
+ DEFINED_FOR_STANDARD_DEVIATION_TYPES = {
+ const.StdDev.TOTAL,
+ const.StdDev.INTER_EVENT,
+ const.StdDev.INTRA_EVENT,
+ }
+
+ REQUIRES_SITES_PARAMETERS = {"vs30", }
+ REQUIRES_RUPTURE_PARAMETERS = {"mag", }
+ REQUIRES_DISTANCES = set()
+
+ # Subduction interface
+ kind = "sinter"
+
+ # Conditional upon PGA and Sa (1.0 s)
+ REQUIRES_IMTS = {"PGA", "SA(1.0)"}
+
+ # GMPE not verified against an independent implementation
+ non_verified = True
+
+ def __init__(self, region: str = "Global", rho_pga_sa1: float = 0.52,
+ **kwargs):
+ """
+ Args:
+ region: Region of application. Must be either "Global" (default)
+ or one of "Japan", "Taiwan", "South America", "New Zealand"
+ rho_pga_sa1: Coefficient of correlation in total standard deviation
+ between PGA and Sa (1.0 s). In the original paper this
+ is taken as 0.52, based on the cross-correlation model
+ of Baker & Jayaram (2008). The coefficient could be
+ configured by the user if they wish to adopt an
+ alternative cross-correlation model
+ """
+ super().__init__(**kwargs)
+ # Check that the region is one of those supported
+ assert region in ("Global", "Japan", "Taiwan", "South America",
+ "New Zealand"),\
+ "Region %s not recognised for Macedo et al (2019) GMPE" % region
+ self.region = region
+ self.rho_pga_sa1 = rho_pga_sa1
+
+ def compute(self, ctx: np.recarray, imts: List, mean: np.ndarray,
+ sig: np.ndarray, tau: np.ndarray, phi: np.ndarray):
+ """Calculates the mean Arias Intensity and the standard deviations
+ """
+ mean_gms, sigma_gms, tau_gms, phi_gms =\
+ self.get_conditioning_ground_motions(ctx)
+ C = CONSTANTS[self.kind][self.region]
+ for m, imt in enumerate(imts):
+ mean[m] = get_mean_conditional_arias_intensity(
+ C, ctx, imt, mean_gms)
+ sigma_m, tau_m, phi_m = get_standard_deviations(
+ C, self.kind, self.rho_pga_sa1, sigma_gms,
+ tau_gms, phi_gms
+ )
+ sig[m] += sigma_m
+ tau[m] += tau_m
+ phi[m] += phi_m
+
+
+class MacedoEtAl2019SSlab(MacedoEtAl2019SInter):
+ """Macedo et al. (2019) GMPE for application to
+ subduction in-slab earthquakes
+ """
+ DEFINED_FOR_TECTONIC_REGION_TYPE = const.TRT.SUBDUCTION_INTRASLAB
+ kind = "sslab"
+
+
+# Create alias classes for the Macedo et al. (2019) regionalisations
+for region_name in ["Japan", "Taiwan", "South America", "New Zealand"]:
+ sinter_alias = "MacedoEtAl2019SInter{:s}".format(
+ region_name.replace(" ", ""))
+ sslab_alias = "MacedoEtAl2019SSlab{:s}".format(
+ region_name.replace(" ", ""))
+ add_alias(sinter_alias, MacedoEtAl2019SInter, region=region_name)
+ add_alias(sslab_alias, MacedoEtAl2019SSlab, region=region_name)
diff --git a/openquake/hazardlib/tests/gsim/conditional_gmpe_test.py b/openquake/hazardlib/tests/gsim/conditional_gmpe_test.py
new file mode 100644
index 000000000000..dcf4da9842aa
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/conditional_gmpe_test.py
@@ -0,0 +1,220 @@
+# -*- coding: utf-8 -*-
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright (C) 2015-2023 GEM Foundation
+#
+# OpenQuake 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.
+#
+# OpenQuake 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 OpenQuake. If not, see .
+"""
+Module contains the test class for the conditional GMPE
+"""
+from typing import List
+import unittest
+import numpy as np
+from openquake.hazardlib import const
+from openquake.hazardlib.imt import PGA, SA
+from openquake.hazardlib.gsim.base import GMPE
+from openquake.hazardlib.gsim.conditional_gmpe import ConditionalGMPE
+
+
+imt_dtypes_basic = np.dtype([
+ ("PGA_MEAN", float),
+ ("PGA_TOTAL_STDDEV", float),
+ ("SA(1.0)_MEAN", float),
+ ("SA(1.0)_TOTAL_STDDEV", float)
+])
+
+
+imt_dtypes_inter_only = np.dtype([
+ ("PGA_MEAN", float),
+ ("PGA_TOTAL_STDDEV", float),
+ ("PGA_INTER_EVENT_STDDEV", float),
+ ("SA(1.0)_MEAN", float),
+ ("SA(1.0)_TOTAL_STDDEV", float),
+ ("SA(1.0)_INTER_EVENT_STDDEV", float),
+])
+
+
+imt_dtypes_full = np.dtype([
+ ("PGA_MEAN", float),
+ ("PGA_TOTAL_STDDEV", float),
+ ("PGA_INTER_EVENT_STDDEV", float),
+ ("PGA_INTRA_EVENT_STDDEV", float),
+ ("SA(1.0)_MEAN", float),
+ ("SA(1.0)_TOTAL_STDDEV", float),
+ ("SA(1.0)_INTER_EVENT_STDDEV", float),
+ ("SA(1.0)_INTRA_EVENT_STDDEV", float),
+])
+
+
+class ConditionalGMPETestCase(unittest.TestCase):
+ """Tests the main ConditionalGMPE for both cases when ground motion values
+ are supplied and ground motion models are called
+ """
+ def setUp(self):
+ class DummyGMPE(GMPE):
+ """
+ """
+ DEFINED_FOR_TECTONIC_REGION_TYPE = ''
+ DEFINED_FOR_INTENSITY_MEASURE_COMPONENT = ''
+ DEFINED_FOR_INTENSITY_MEASURE_TYPES = {PGA, SA}
+ DEFINED_FOR_STANDARD_DEVIATION_TYPES = {
+ const.StdDev.TOTAL,
+ }
+
+ REQUIRES_SITES_PARAMETERS = set()
+ REQUIRES_RUPTURE_PARAMETERS = {"mag", }
+ REQUIRES_DISTANCES = {"rrup", }
+
+ def compute(self, ctx: np.recarray, imts: List, mean: np.ndarray,
+ sig: np.ndarray, tau: np.ndarray, phi: np.ndarray):
+ for m, imt in enumerate(imts):
+ if str(imt) == "PGA":
+ mean[m] += np.log(1.0)
+ sig[m] += 0.8
+ else:
+ mean[m] += np.log(0.1)
+ sig[m] += 0.6
+ return
+ self.dummy_gmpe = DummyGMPE
+
+ def test_instantiation_unknown_gmpe(self):
+ # Should raise a basic KeyError
+ with self.assertRaises(KeyError) as ke:
+ _ = ConditionalGMPE(gmpe={"XYZ": {}})
+ self.assertEqual(str(ke.exception), "'XYZ'")
+
+ def test_usage_no_gmpe_minimum(self):
+ # Should return the GMVs in the ctx for mean and sigma only
+ ctx = np.recarray(3, dtype=imt_dtypes_basic)
+ ctx["PGA_MEAN"] = np.array([0.1, 0.2, 0.3])
+ ctx["PGA_TOTAL_STDDEV"] = np.array([0.1, 0.1, 0.1])
+ ctx["SA(1.0)_MEAN"] = np.array([0.05, 0.1, 0.15])
+ ctx["SA(1.0)_TOTAL_STDDEV"] = np.array([0.08, 0.1, 0.12])
+ cgmm = ConditionalGMPE()
+ cgmm.REQUIRES_IMTS = {"PGA", "SA(1.0)"}
+ mean_gms, sigma_gms, tau_gms, phi_gms =\
+ cgmm.get_conditioning_ground_motions(ctx)
+ for imt_string in ["PGA", "SA(1.0)"]:
+ np.testing.assert_array_almost_equal(mean_gms[imt_string],
+ ctx[f"{imt_string}_MEAN"])
+ np.testing.assert_array_almost_equal(
+ sigma_gms[imt_string],
+ ctx[f"{imt_string}_TOTAL_STDDEV"]
+ )
+ np.testing.assert_array_almost_equal(tau_gms[imt_string],
+ np.zeros(3))
+ np.testing.assert_array_almost_equal(phi_gms[imt_string],
+ np.zeros(3))
+
+ def test_usage_no_gmpe_inter_only(self):
+ # Should return the GMVs in the ctx for mean, sigma and tau,
+ # but not for phi
+ ctx = np.recarray(3, dtype=imt_dtypes_inter_only)
+ ctx["PGA_MEAN"] = np.array([0.1, 0.2, 0.3])
+ ctx["PGA_TOTAL_STDDEV"] = np.array([0.1, 0.1, 0.1])
+ ctx["PGA_INTER_EVENT_STDDEV"] = np.array([0.05, 0.05, 0.05])
+ ctx["SA(1.0)_MEAN"] = np.array([0.05, 0.1, 0.15])
+ ctx["SA(1.0)_TOTAL_STDDEV"] = np.array([0.08, 0.1, 0.12])
+ ctx["SA(1.0)_INTER_EVENT_STDDEV"] = np.array([0.04, 0.05, 0.06])
+ cgmm = ConditionalGMPE()
+ cgmm.REQUIRES_IMTS = {"PGA", "SA(1.0)"}
+ mean_gms, sigma_gms, tau_gms, phi_gms =\
+ cgmm.get_conditioning_ground_motions(ctx)
+ print(tau_gms["PGA"], tau_gms["SA(1.0)"])
+ for imt_string in ["PGA", "SA(1.0)"]:
+ np.testing.assert_array_almost_equal(mean_gms[imt_string],
+ ctx[f"{imt_string}_MEAN"])
+ np.testing.assert_array_almost_equal(
+ sigma_gms[imt_string],
+ ctx[f"{imt_string}_TOTAL_STDDEV"]
+ )
+ np.testing.assert_array_almost_equal(
+ tau_gms[imt_string],
+ ctx[f"{imt_string}_INTER_EVENT_STDDEV"]
+ )
+ np.testing.assert_array_almost_equal(phi_gms[imt_string],
+ np.zeros(3))
+
+ def test_usage_no_gmpe_full(self):
+ # Should return the GMVs in the ctx for mean, sigma, tau and phi
+ ctx = np.recarray(3, dtype=imt_dtypes_full)
+ ctx["PGA_MEAN"] = np.array([0.1, 0.2, 0.3])
+ ctx["PGA_TOTAL_STDDEV"] = np.array([0.1, 0.1, 0.1])
+ ctx["PGA_INTER_EVENT_STDDEV"] = np.array([0.07, 0.07, 0.07])
+ ctx["PGA_INTRA_EVENT_STDDEV"] = np.array([0.03, 0.03, 0.03])
+ ctx["SA(1.0)_MEAN"] = np.array([0.05, 0.1, 0.15])
+ ctx["SA(1.0)_TOTAL_STDDEV"] = np.array([0.08, 0.1, 0.12])
+ ctx["SA(1.0)_INTER_EVENT_STDDEV"] = np.array([0.06, 0.07, 0.08])
+ ctx["SA(1.0)_INTRA_EVENT_STDDEV"] = np.array([0.02, 0.03, 0.04])
+ cgmm = ConditionalGMPE()
+ cgmm.REQUIRES_IMTS = {"PGA", "SA(1.0)"}
+ mean_gms, sigma_gms, tau_gms, phi_gms =\
+ cgmm.get_conditioning_ground_motions(ctx)
+ for imt_string in ["PGA", "SA(1.0)"]:
+ np.testing.assert_array_almost_equal(mean_gms[imt_string],
+ ctx[f"{imt_string}_MEAN"])
+ np.testing.assert_array_almost_equal(
+ sigma_gms[imt_string],
+ ctx[f"{imt_string}_TOTAL_STDDEV"]
+ )
+ np.testing.assert_array_almost_equal(
+ tau_gms[imt_string],
+ ctx[f"{imt_string}_INTER_EVENT_STDDEV"]
+ )
+ np.testing.assert_array_almost_equal(
+ phi_gms[imt_string],
+ ctx[f"{imt_string}_INTRA_EVENT_STDDEV"]
+ )
+
+ def test_usage_gmpe_empty_ctx(self):
+ # Should raise an error saying it needs a GMPE if not GMVs are defined
+ # Provide data but no GMVs and no GMPE
+ ctx_empty = np.recarray(3, dtype=np.dtype([("mag", float),
+ ("rrup", float)]))
+ ctx_empty["mag"] = np.array([5., 6., 7.])
+ ctx_empty["rrup"] = np.array([10., 20., 50.0])
+ with self.assertRaises(ValueError) as ve:
+ cgmm = ConditionalGMPE()
+ cgmm.REQUIRES_IMTS = {"PGA", "SA(1.0)"}
+ _ = cgmm.get_conditioning_ground_motions(ctx_empty)
+ self.assertEqual(
+ str(ve.exception),
+ "Conditioning ground motions must be specified in ctx "
+ "or a GMPE must be provided"
+ )
+
+ def test_usage_with_gmpe(self):
+ # Tests to retrieve values from a GMPE
+ ctx_empty = np.recarray(3, dtype=np.dtype([("mag", float),
+ ("rrup", float)]))
+ ctx_empty["mag"] = np.array([5., 6., 7.])
+ ctx_empty["rrup"] = np.array([10., 20., 50.0])
+ cgmm = ConditionalGMPE(gmpe={"DummyGMPE": {}})
+ cgmm.REQUIRES_IMTS = {"PGA", "SA(1.0)"}
+ mean_gms, sigma_gms, tau_gms, phi_gms = \
+ cgmm.get_conditioning_ground_motions(ctx_empty)
+ null_array = np.zeros(3, dtype=float)
+ # Mean and sigma take constant values from Dummy GMPE
+ np.testing.assert_array_almost_equal(mean_gms["PGA"],
+ null_array + 1.0)
+ np.testing.assert_array_almost_equal(mean_gms["SA(1.0)"],
+ null_array + 0.1)
+
+ np.testing.assert_array_almost_equal(sigma_gms["PGA"],
+ null_array + 0.8)
+ np.testing.assert_array_almost_equal(sigma_gms["SA(1.0)"],
+ null_array + 0.6)
+ for imt_str in ["PGA", "SA(1.0)"]:
+ np.testing.assert_array_almost_equal(tau_gms[imt_str], null_array)
+ np.testing.assert_array_almost_equal(phi_gms[imt_str], null_array)
diff --git a/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_conditioning_gmvs.csv b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_conditioning_gmvs.csv
new file mode 100644
index 000000000000..01c5f012d3bb
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_conditioning_gmvs.csv
@@ -0,0 +1,11 @@
+mag,vs30,PGA_MEAN,PGA_TOTAL_STDDEV,SA(1.0)_MEAN,SA(1.0)_TOTAL_STDDEV,Global_MEAN,Global_SIG,Global_TAU,Global_PHI,Japan_MEAN,Japan_SIG,Japan_TAU,Japan_PHI,Taiwan_MEAN,Taiwan_SIG,Taiwan_TAU,Taiwan_PHI,South America_MEAN,South America_SIG,South America_TAU,South America_PHI,New Zealand_MEAN,New Zealand_SIG,New Zealand_TAU,New Zealand_PHI
+8.5,450.0,0.01,0.0,0.006,0.0,0.008180430479675075,0.36,0.18439088914585774,0.3,0.008244611401202853,0.36,0.16,0.3,0.007868262405385323,0.36,0.15,0.27,0.00904077386347651,0.36,0.19,0.32,0.007938662221504748,0.36,0.17,0.28
+8.5,450.0,0.016681005372000592,0.0,0.010008603223200355,0.0,0.01962348592639495,0.36,0.18439088914585774,0.3,0.01977744523372879,0.36,0.16,0.3,0.018874646873521788,0.36,0.15,0.27,0.02168730595711845,0.36,0.19,0.32,0.019043524269921983,0.36,0.17,0.28
+8.5,450.0,0.027825594022071243,0.0,0.016695356413242744,0.0,0.04707346402615188,0.36,0.18439088914585774,0.3,0.047442786680773764,0.36,0.16,0.3,0.04527712425507237,0.36,0.15,0.27,0.052024223454783254,0.36,0.19,0.32,0.04568223291283546,0.36,0.17,0.28
+8.5,450.0,0.046415888336127774,0.0,0.027849533001676665,0.0,0.11292137511821289,0.36,0.18439088914585774,0.3,0.11380731845985953,0.36,0.16,0.3,0.10861225614160341,0.36,0.15,0.27,0.12479741980975995,0.36,0.19,0.32,0.10958404412562532,0.36,0.17,0.28
+8.5,450.0,0.0774263682681127,0.0,0.046455820960867616,0.0,0.27087951189451764,0.36,0.18439088914585774,0.3,0.2730047419468457,0.36,0.16,0.3,0.2605426554414553,0.36,0.15,0.27,0.2993681588483474,0.36,0.19,0.32,0.2628738124478346,0.36,0.17,0.28
+8.5,450.0,0.1291549665014884,0.0,0.07749297990089303,0.0,0.6497946902205004,0.36,0.18439088914585774,0.3,0.6548927620305145,0.36,0.16,0.3,0.6249982986817153,0.36,0.15,0.27,0.7181341943516716,0.36,0.19,0.32,0.630590354848021,0.36,0.17,0.28
+8.5,450.0,0.21544346900318834,0.0,0.129266081401913,0.0,1.5587488935050087,0.36,0.18439088914585774,0.3,1.5709783159863957,0.36,0.16,0.3,1.4992664932088746,0.36,0.15,0.27,1.7226839456843306,0.36,0.19,0.32,1.5126809016256144,0.36,0.17,0.28
+8.5,450.0,0.3593813663804626,0.0,0.21562881982827756,0.0,3.739178158225018,0.36,0.18439088914585774,0.3,3.7685144994539663,0.36,0.16,0.3,3.596489818292357,0.36,0.15,0.27,4.132430957973953,0.36,0.19,0.32,3.628668742791605,0.36,0.17,0.28
+8.5,450.0,0.5994842503189409,0.0,0.35969055019136453,0.0,8.96966365602885,0.36,0.18439088914585774,0.3,9.04003663709244,0.36,0.16,0.3,8.627378169038138,0.36,0.15,0.27,9.913011417563174,0.36,0.19,0.32,8.704570032425577,0.36,0.17,0.28
+8.5,450.0,1.0,0.0,0.6,0.0,21.516724450614728,0.36,0.18439088914585774,0.3,21.685537474199602,0.36,0.16,0.3,20.695638756718793,0.36,0.15,0.27,23.779658115066617,0.36,0.19,0.32,20.880809139693035,0.36,0.17,0.28
diff --git a/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_inter_event_stddev.csv b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_inter_event_stddev.csv
new file mode 100644
index 000000000000..dba76d99026a
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_inter_event_stddev.csv
@@ -0,0 +1,61 @@
+rup_mag,rup_hypo_depth,dist_rrup,site_vs30,site_backarc,damping,result_type,ia
+7.0,20.0,10.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,30.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,50.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,70.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,90.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,110.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,130.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,150.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,170.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,190.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,210.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,230.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,250.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,270.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.0,20.0,290.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,10.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,30.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,50.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,70.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,90.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,110.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,130.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,150.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,170.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,190.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,210.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,230.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,250.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,270.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+7.5,20.0,290.0,300.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,10.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,30.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,50.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,70.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,90.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,110.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,130.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,150.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,170.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,190.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,210.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,230.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,250.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,270.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.0,20.0,290.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,10.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,30.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,50.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,70.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,90.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,110.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,130.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,150.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,170.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,190.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,210.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,230.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,250.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,270.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
+8.5,20.0,290.0,760.0,0,5,INTER_EVENT_STDDEV,0.7267706259886952
diff --git a/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_intra_event_stddev.csv b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_intra_event_stddev.csv
new file mode 100644
index 000000000000..293738678af7
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_intra_event_stddev.csv
@@ -0,0 +1,61 @@
+rup_mag,rup_hypo_depth,dist_rrup,site_vs30,site_backarc,damping,result_type,ia
+7.0,20.0,10.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,30.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,50.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,70.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,90.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,110.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,130.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,150.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,170.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,190.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,210.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,230.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,250.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,270.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.0,20.0,290.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,10.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,30.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,50.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,70.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,90.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,110.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,130.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,150.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,170.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,190.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,210.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,230.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,250.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,270.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+7.5,20.0,290.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,10.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,30.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,50.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,70.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,90.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,110.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,130.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,150.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,170.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,190.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,210.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,230.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,250.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,270.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.0,20.0,290.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,10.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,30.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,50.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,70.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,90.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,110.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,130.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,150.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,170.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,190.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,210.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,230.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,250.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,270.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
+8.5,20.0,290.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0257669910852074
diff --git a/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_mean.csv b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_mean.csv
new file mode 100644
index 000000000000..f9b0a8296b6e
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_mean.csv
@@ -0,0 +1,61 @@
+rup_mag,rup_hypo_depth,dist_rrup,site_vs30,site_backarc,damping,result_type,ia
+7.0,20.0,10.0,300.0,0,5,MEAN,1.8111902865813347
+7.0,20.0,30.0,300.0,0,5,MEAN,0.5552971492434413
+7.0,20.0,50.0,300.0,0,5,MEAN,0.24270759657859495
+7.0,20.0,70.0,300.0,0,5,MEAN,0.12760441894163144
+7.0,20.0,90.0,300.0,0,5,MEAN,0.07517667530609753
+7.0,20.0,110.0,300.0,0,5,MEAN,0.04783766090054575
+7.0,20.0,130.0,300.0,0,5,MEAN,0.03218022783688855
+7.0,20.0,150.0,300.0,0,5,MEAN,0.022574425643609515
+7.0,20.0,170.0,300.0,0,5,MEAN,0.016363012561830637
+7.0,20.0,190.0,300.0,0,5,MEAN,0.012176328552618238
+7.0,20.0,210.0,300.0,0,5,MEAN,0.009258064026895326
+7.0,20.0,230.0,300.0,0,5,MEAN,0.007166829825997672
+7.0,20.0,250.0,300.0,0,5,MEAN,0.005633045494601161
+7.0,20.0,270.0,300.0,0,5,MEAN,0.004485683781690399
+7.0,20.0,290.0,300.0,0,5,MEAN,0.0036126843459842336
+7.5,20.0,10.0,300.0,0,5,MEAN,4.398378488575499
+7.5,20.0,30.0,300.0,0,5,MEAN,1.6873045456721938
+7.5,20.0,50.0,300.0,0,5,MEAN,0.8347157131273609
+7.5,20.0,70.0,300.0,0,5,MEAN,0.47440292442633175
+7.5,20.0,90.0,300.0,0,5,MEAN,0.29491693458852314
+7.5,20.0,110.0,300.0,0,5,MEAN,0.1952486508395245
+7.5,20.0,130.0,300.0,0,5,MEAN,0.13543198051534489
+7.5,20.0,150.0,300.0,0,5,MEAN,0.09737362668288506
+7.5,20.0,170.0,300.0,0,5,MEAN,0.07203188749790795
+7.5,20.0,190.0,300.0,0,5,MEAN,0.05453173858765408
+7.5,20.0,210.0,300.0,0,5,MEAN,0.04208115328398436
+7.5,20.0,230.0,300.0,0,5,MEAN,0.03300050591705778
+7.5,20.0,250.0,300.0,0,5,MEAN,0.026237266624396974
+7.5,20.0,270.0,300.0,0,5,MEAN,0.02110876088907778
+7.5,20.0,290.0,300.0,0,5,MEAN,0.017158972843205642
+8.0,20.0,10.0,760.0,0,5,MEAN,8.365021868354177
+8.0,20.0,30.0,760.0,0,5,MEAN,2.8354239733289406
+8.0,20.0,50.0,760.0,0,5,MEAN,1.3367489993126584
+8.0,20.0,70.0,760.0,0,5,MEAN,0.7453020620836475
+8.0,20.0,90.0,760.0,0,5,MEAN,0.46021860754450744
+8.0,20.0,110.0,760.0,0,5,MEAN,0.3044305144201686
+8.0,20.0,130.0,760.0,0,5,MEAN,0.2116125943196576
+8.0,20.0,150.0,760.0,0,5,MEAN,0.15270230700099968
+8.0,20.0,170.0,760.0,0,5,MEAN,0.11346275066511587
+8.0,20.0,190.0,760.0,0,5,MEAN,0.0863108723140777
+8.0,20.0,210.0,760.0,0,5,MEAN,0.06693524983184818
+8.0,20.0,230.0,760.0,0,5,MEAN,0.05275271660698126
+8.0,20.0,250.0,760.0,0,5,MEAN,0.042147552644619564
+8.0,20.0,270.0,760.0,0,5,MEAN,0.03407215341833518
+8.0,20.0,290.0,760.0,0,5,MEAN,0.027826224534234327
+8.5,20.0,10.0,760.0,0,5,MEAN,11.396202043176727
+8.5,20.0,30.0,760.0,0,5,MEAN,4.475737919256328
+8.5,20.0,50.0,760.0,0,5,MEAN,2.2816770348422013
+8.5,20.0,70.0,760.0,0,5,MEAN,1.3381463627356904
+8.5,20.0,90.0,760.0,0,5,MEAN,0.8568824686310381
+8.5,20.0,110.0,760.0,0,5,MEAN,0.5828531377740002
+8.5,20.0,130.0,760.0,0,5,MEAN,0.41431951184468413
+8.5,20.0,150.0,760.0,0,5,MEAN,0.3045801285132741
+8.5,20.0,170.0,760.0,0,5,MEAN,0.22991159098724132
+8.5,20.0,190.0,760.0,0,5,MEAN,0.17730007859326832
+8.5,20.0,210.0,760.0,0,5,MEAN,0.13916197933853897
+8.5,20.0,230.0,760.0,0,5,MEAN,0.11085701399281724
+8.5,20.0,250.0,760.0,0,5,MEAN,0.08942920830612426
+8.5,20.0,270.0,760.0,0,5,MEAN,0.07293079688618648
+8.5,20.0,290.0,760.0,0,5,MEAN,0.06004087690207515
diff --git a/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_total_stddev.csv b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_total_stddev.csv
new file mode 100644
index 000000000000..f9871a5d8cf3
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sinter_total_stddev.csv
@@ -0,0 +1,61 @@
+rup_mag,rup_hypo_depth,dist_rrup,site_vs30,site_backarc,damping,result_type,ia
+7.0,20.0,10.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,30.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,50.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,70.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,90.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,110.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,130.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,150.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,170.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,190.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,210.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,230.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,250.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,270.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.0,20.0,290.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,10.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,30.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,50.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,70.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,90.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,110.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,130.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,150.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,170.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,190.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,210.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,230.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,250.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,270.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+7.5,20.0,290.0,300.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,10.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,30.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,50.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,70.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,90.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,110.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,130.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,150.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,170.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,190.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,210.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,230.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,250.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,270.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.0,20.0,290.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,10.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,30.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,50.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,70.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,90.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,110.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,130.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,150.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,170.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,190.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,210.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,230.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,250.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,270.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
+8.5,20.0,290.0,760.0,0,5,TOTAL_STDDEV,1.2622242063912417
diff --git a/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_conditioning_gmvs.csv b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_conditioning_gmvs.csv
new file mode 100644
index 000000000000..6cd8886d1f67
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_conditioning_gmvs.csv
@@ -0,0 +1,11 @@
+mag,vs30,PGA_MEAN,PGA_TOTAL_STDDEV,SA(1.0)_MEAN,SA(1.0)_TOTAL_STDDEV,Global_MEAN,Global_SIG,Global_TAU,Global_PHI,Japan_MEAN,Japan_SIG,Japan_TAU,Japan_PHI,Taiwan_MEAN,Taiwan_SIG,Taiwan_TAU,Taiwan_PHI,South America_MEAN,South America_SIG,South America_TAU,South America_PHI,New Zealand_MEAN,New Zealand_SIG,New Zealand_TAU,New Zealand_PHI
+8.0,450.0,0.01,0.0,0.006,0.0,0.00730915412207633,0.33,0.16278820596099708,0.28,0.0075412484502578245,0.33,0.15,0.26,0.007053313138826174,0.33,0.17,0.29,0.007236426823408719,0.33,0.14,0.3,0.007473119409024828,0.33,0.13,0.3
+8.0,450.0,0.016681005372000592,0.0,0.010008603223200355,0.0,0.017623385523372528,0.33,0.16278820596099708,0.28,0.018182997176788937,0.33,0.15,0.26,0.017006517387170815,0.33,0.17,0.29,0.017448029907512472,0.33,0.14,0.3,0.0180187285981089,0.33,0.13,0.3
+8.0,450.0,0.027825594022071243,0.0,0.016695356413242744,0.0,0.04249242964618019,0.33,0.16278820596099708,0.28,0.043841731049162225,0.33,0.15,0.26,0.04100507491267805,0.33,0.17,0.29,0.04206962290679893,0.33,0.14,0.3,0.043445656695946584,0.33,0.13,0.3
+8.0,450.0,0.046415888336127774,0.0,0.027849533001676665,0.0,0.10245514829377922,0.33,0.16278820596099708,0.28,0.10570850133775973,0.33,0.15,0.26,0.09886892950009545,0.33,0.17,0.29,0.10143570253500249,0.33,0.14,0.3,0.10475351107403592,0.33,0.13,0.3
+8.0,450.0,0.0774263682681127,0.0,0.046455820960867616,0.0,0.24703358926062047,0.33,0.16278820596099708,0.28,0.2548778752039874,0.33,0.15,0.26,0.23838671777362283,0.33,0.17,0.29,0.24457556397793762,0.33,0.14,0.3,0.2525752610700429,0.33,0.13,0.3
+8.0,450.0,0.1291549665014884,0.0,0.07749297990089303,0.0,0.5956322863151841,0.33,0.16278820596099708,0.28,0.6145459489670612,0.33,0.15,0.26,0.5747834784721326,0.33,0.17,0.29,0.5897056460419849,0.33,0.14,0.3,0.6089940265535626,0.33,0.13,0.3
+8.0,450.0,0.21544346900318834,0.0,0.129266081401913,0.0,1.4361521506565762,0.33,0.16278820596099708,0.28,1.4817556176249747,0.33,0.15,0.26,1.3858827799217266,0.33,0.17,0.29,1.4218621979960537,0.33,0.14,0.3,1.4683691617567898,0.33,0.13,0.3
+8.0,450.0,0.3593813663804626,0.0,0.21562881982827756,0.0,3.462762256551184,0.33,0.16278820596099708,0.28,3.572718547821475,0.33,0.15,0.26,3.3415558234015803,0.33,0.17,0.29,3.4283071964113945,0.33,0.14,0.3,3.5404419439058334,0.33,0.13,0.3
+8.0,450.0,0.5994842503189409,0.0,0.35969055019136453,0.0,8.349200633034306,0.33,0.16278820596099708,0.28,8.614320519605544,0.33,0.15,0.26,8.056955092219036,0.33,0.17,0.29,8.266124698674064,0.33,0.14,0.3,8.536497145697954,0.33,0.13,0.3
+8.0,450.0,1.0,0.0,0.6,0.0,20.131082080145113,0.33,0.16278820596099708,0.28,20.770322940704563,0.33,0.15,0.26,19.426437500587266,0.33,0.17,0.29,19.93077446663853,0.33,0.14,0.3,20.58267997980974,0.33,0.13,0.3
diff --git a/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_inter_event_stddev.csv b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_inter_event_stddev.csv
new file mode 100644
index 000000000000..a6b01ca848f0
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_inter_event_stddev.csv
@@ -0,0 +1,49 @@
+rup_mag,rup_hypo_depth,dist_rhypo,site_vs30,site_backarc,damping,result_type,ia
+7.0,80.0,60.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.0,80.0,80.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.0,80.0,100.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.0,80.0,120.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.0,80.0,140.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.0,80.0,160.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.0,80.0,180.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.0,80.0,200.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.0,80.0,220.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.0,80.0,240.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.0,80.0,260.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.0,80.0,280.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,60.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,80.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,100.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,120.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,140.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,160.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,180.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,200.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,220.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,240.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,260.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+7.5,80.0,280.0,300.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,60.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,80.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,100.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,120.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,140.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,160.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,180.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,200.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,220.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,240.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,260.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.0,80.0,280.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,60.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,80.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,100.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,120.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,140.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,160.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,180.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,200.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,220.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,240.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,260.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
+8.5,80.0,280.0,760.0,0,5,INTER_EVENT_STDDEV,0.7309201565150601
diff --git a/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_intra_event_stddev.csv b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_intra_event_stddev.csv
new file mode 100644
index 000000000000..0817be5d1bc3
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_intra_event_stddev.csv
@@ -0,0 +1,49 @@
+rup_mag,rup_hypo_depth,dist_rhypo,site_vs30,site_backarc,damping,result_type,ia
+7.0,80.0,60.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.0,80.0,80.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.0,80.0,100.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.0,80.0,120.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.0,80.0,140.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.0,80.0,160.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.0,80.0,180.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.0,80.0,200.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.0,80.0,220.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.0,80.0,240.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.0,80.0,260.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.0,80.0,280.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,60.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,80.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,100.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,120.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,140.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,160.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,180.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,200.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,220.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,240.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,260.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+7.5,80.0,280.0,300.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,60.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,80.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,100.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,120.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,140.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,160.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,180.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,200.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,220.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,240.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,260.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.0,80.0,280.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,60.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,80.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,100.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,120.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,140.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,160.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,180.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,200.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,220.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,240.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,260.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
+8.5,80.0,280.0,760.0,0,5,INTRA_EVENT_STDDEV,1.0329459230763245
diff --git a/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_mean.csv b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_mean.csv
new file mode 100644
index 000000000000..b8a6811b9e67
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_mean.csv
@@ -0,0 +1,49 @@
+rup_mag,rup_hypo_depth,dist_rhypo,site_vs30,site_backarc,damping,result_type,ia
+7.0,80.0,60.0,300.0,0,5,MEAN,1.7650072236363659
+7.0,80.0,80.0,300.0,0,5,MEAN,0.9608553443316707
+7.0,80.0,100.0,300.0,0,5,MEAN,0.564078338831358
+7.0,80.0,120.0,300.0,0,5,MEAN,0.3508975232543459
+7.0,80.0,140.0,300.0,0,5,MEAN,0.22861398442908837
+7.0,80.0,160.0,300.0,0,5,MEAN,0.15466547811710182
+7.0,80.0,180.0,300.0,0,5,MEAN,0.10795311997578419
+7.0,80.0,200.0,300.0,0,5,MEAN,0.07734752311789289
+7.0,80.0,220.0,300.0,0,5,MEAN,0.05666434152433948
+7.0,80.0,240.0,300.0,0,5,MEAN,0.04231116151126752
+7.0,80.0,260.0,300.0,0,5,MEAN,0.03211986877969602
+7.0,80.0,280.0,300.0,0,5,MEAN,0.024737742199601835
+7.5,80.0,60.0,300.0,0,5,MEAN,5.429733413727579
+7.5,80.0,80.0,300.0,0,5,MEAN,3.2484141962144144
+7.5,80.0,100.0,300.0,0,5,MEAN,2.0616393333571312
+7.5,80.0,120.0,300.0,0,5,MEAN,1.3653257543554438
+7.5,80.0,140.0,300.0,0,5,MEAN,0.9352999153035143
+7.5,80.0,160.0,300.0,0,5,MEAN,0.6589861725102227
+7.5,80.0,180.0,300.0,0,5,MEAN,0.47553130948343
+7.5,80.0,200.0,300.0,0,5,MEAN,0.3502824167093182
+7.5,80.0,220.0,300.0,0,5,MEAN,0.2626792624542905
+7.5,80.0,240.0,300.0,0,5,MEAN,0.20009575110239552
+7.5,80.0,260.0,300.0,0,5,MEAN,0.15454343105403603
+7.5,80.0,280.0,300.0,0,5,MEAN,0.12083340897887566
+8.0,80.0,60.0,760.0,0,5,MEAN,8.36971870710561
+8.0,80.0,80.0,760.0,0,5,MEAN,4.336039222794261
+8.0,80.0,100.0,760.0,0,5,MEAN,2.4946793780308547
+8.0,80.0,120.0,760.0,0,5,MEAN,1.5446839871615496
+8.0,80.0,140.0,760.0,0,5,MEAN,1.0099967058341948
+8.0,80.0,160.0,760.0,0,5,MEAN,0.6887505817451277
+8.0,80.0,180.0,760.0,0,5,MEAN,0.4856639614388162
+8.0,80.0,200.0,760.0,0,5,MEAN,0.35192605363229346
+8.0,80.0,220.0,760.0,0,5,MEAN,0.26085729735579555
+8.0,80.0,240.0,760.0,0,5,MEAN,0.1970855924693731
+8.0,80.0,260.0,760.0,0,5,MEAN,0.15135782457002753
+8.0,80.0,280.0,760.0,0,5,MEAN,0.11789466711766128
+8.5,80.0,60.0,760.0,0,5,MEAN,15.034860652110956
+8.5,80.0,80.0,760.0,0,5,MEAN,8.185778479621575
+8.5,80.0,100.0,760.0,0,5,MEAN,4.887104671270402
+8.5,80.0,120.0,760.0,0,5,MEAN,3.1151371651987048
+8.5,80.0,140.0,760.0,0,5,MEAN,2.0854576871505475
+8.5,80.0,160.0,760.0,0,5,MEAN,1.4504418126269953
+8.5,80.0,180.0,760.0,0,5,MEAN,1.0401003088734464
+8.5,80.0,200.0,760.0,0,5,MEAN,0.7647641181530979
+8.5,80.0,220.0,760.0,0,5,MEAN,0.574191278998221
+8.5,80.0,240.0,760.0,0,5,MEAN,0.4388090628001443
+8.5,80.0,260.0,760.0,0,5,MEAN,0.340481212281078
+8.5,80.0,280.0,760.0,0,5,MEAN,0.26769170365797784
diff --git a/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_total_stddev.csv b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_total_stddev.csv
new file mode 100644
index 000000000000..6ef367c46a9a
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/data/macedo_2019/macedo_2019_sslab_total_stddev.csv
@@ -0,0 +1,49 @@
+rup_mag,rup_hypo_depth,dist_rhypo,site_vs30,site_backarc,damping,result_type,ia
+7.0,80.0,60.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.0,80.0,80.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.0,80.0,100.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.0,80.0,120.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.0,80.0,140.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.0,80.0,160.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.0,80.0,180.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.0,80.0,200.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.0,80.0,220.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.0,80.0,240.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.0,80.0,260.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.0,80.0,280.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,60.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,80.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,100.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,120.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,140.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,160.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,180.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,200.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,220.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,240.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,260.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+7.5,80.0,280.0,300.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,60.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,80.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,100.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,120.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,140.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,160.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,180.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,200.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,220.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,240.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,260.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.0,80.0,280.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,60.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,80.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,100.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,120.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,140.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,160.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,180.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,200.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,220.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,240.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,260.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
+8.5,80.0,280.0,760.0,0,5,TOTAL_STDDEV,1.2698960133806234
diff --git a/openquake/hazardlib/tests/gsim/macedo_2019_test.py b/openquake/hazardlib/tests/gsim/macedo_2019_test.py
new file mode 100644
index 000000000000..f6bc2a372f0d
--- /dev/null
+++ b/openquake/hazardlib/tests/gsim/macedo_2019_test.py
@@ -0,0 +1,139 @@
+# -*- coding: utf-8 -*-
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright (C) 2015-2023 GEM Foundation
+#
+# OpenQuake 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.
+#
+# OpenQuake 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 OpenQuake. If not, see .
+"""
+Module contains the test class for the Macedo et al. (2019) Subuction Interface
+and Inslab GMPEs
+"""
+import os
+import unittest
+import numpy as np
+import pandas as pd
+from openquake.hazardlib.imt import IA
+from openquake.hazardlib.tests.gsim.utils import BaseGSIMTestCase
+from openquake.hazardlib.gsim.macedo_2019 import MacedoEtAl2019SInter,\
+ MacedoEtAl2019SSlab
+
+
+DATA_PATH = os.path.join(os.path.dirname(__file__), "data/macedo_2019")
+
+
+class MacedoEtAl2019ConditionedTestCase(unittest.TestCase):
+ """Test cases for the Macedo et al (2019) GMM when conditioned on fixed
+ ground motion values
+ """
+ def setUp(self):
+ self.sinter_table_file = os.path.join(
+ DATA_PATH,
+ "macedo_2019_sinter_conditioning_gmvs.csv"
+ )
+
+ self.sslab_table_file = os.path.join(
+ DATA_PATH,
+ "macedo_2019_sslab_conditioning_gmvs.csv"
+ )
+ self.ctx_dtypes = np.dtype([
+ ("mag", float),
+ ("vs30", float),
+ ("PGA_MEAN", float),
+ ("PGA_TOTAL_STDDEV", float),
+ ("SA(1.0)_MEAN", float),
+ ("SA(1.0)_TOTAL_STDDEV", float)
+ ])
+
+ def _compare_gsim_by_region(self, data: pd.DataFrame, gsim_class):
+ """Sorts the values from the data file in context and target, and then
+ runs the GSIM for each region
+ """
+ n = data.shape[0]
+ # Build the context from the data
+ ctx = np.recarray(n, dtype=self.ctx_dtypes)
+ ctx["mag"] = data["mag"].to_numpy()
+ ctx["vs30"] = data["vs30"].to_numpy()
+ for imt in ["PGA", "SA(1.0)"]:
+ for res_type in ["MEAN", "TOTAL_STDDEV"]:
+ key = f"{imt}_{res_type}"
+ ctx[key] = data[key].to_numpy()
+ # Get the results for each region
+ for region in ["Global", "Japan", "Taiwan",
+ "South America", "New Zealand"]:
+ gsim = gsim_class(region=region)
+ mean = np.zeros([1, n])
+ sigma = np.zeros([1, n])
+ tau = np.zeros([1, n])
+ phi = np.zeros([1, n])
+ gsim.compute(ctx, [IA()], mean, sigma, tau, phi)
+ np.testing.assert_array_almost_equal(
+ np.exp(mean).flatten(),
+ data[f"{region}_MEAN"].to_numpy()
+ )
+ np.testing.assert_array_almost_equal(
+ sigma.flatten(),
+ data[f"{region}_SIG"].to_numpy()
+ )
+ np.testing.assert_array_almost_equal(
+ tau.flatten(),
+ data[f"{region}_TAU"].to_numpy()
+ )
+ np.testing.assert_array_almost_equal(
+ phi.flatten(),
+ data[f"{region}_PHI"].to_numpy()
+ )
+ return
+
+ def test_macedo_2019_sinter_conditioned(self):
+ """Tests execution of MacedoEtAl2019SInter conditioned on ground motion
+ """
+ data = pd.read_csv(self.sinter_table_file, sep=",")
+ gsim_sinter = MacedoEtAl2019SInter
+ self._compare_gsim_by_region(data, gsim_sinter)
+
+ def test_macedo_2019_sslab_conditioned(self):
+ """Tests execution of MacedoEtAl2019SSlab conditioned on ground motion
+ """
+ data = pd.read_csv(self.sslab_table_file, sep=",")
+ gsim_sslab = MacedoEtAl2019SSlab
+ self._compare_gsim_by_region(data, gsim_sslab)
+
+
+class MacedoEtAl2019SInterTestCase(BaseGSIMTestCase):
+ """Test case for the Macedo et al. (2019) GMM conditioned on GMVs generated
+ by the AbrahamsonEtAl2015SInter GSIM
+ """
+ GSIM_CLASS = MacedoEtAl2019SInter
+ MEAN_FILE = "macedo_2019/macedo_2019_sinter_mean.csv"
+ TOTAL_FILE = "macedo_2019/macedo_2019_sinter_total_stddev.csv"
+ INTER_FILE = "macedo_2019/macedo_2019_sinter_inter_event_stddev.csv"
+ INTRA_FILE = "macedo_2019/macedo_2019_sinter_intra_event_stddev.csv"
+ GMM_GMPE = {"AbrahamsonEtAl2015SInter": {}}
+
+ def test_all(self):
+ self.check(self.MEAN_FILE, self.TOTAL_FILE, self.INTER_FILE,
+ self.INTRA_FILE, max_discrep_percentage=0.01,
+ gmpe=self.GMM_GMPE)
+
+
+class MacedoEtAl2019SSlabTestCase(MacedoEtAl2019SInterTestCase):
+ """Test case for the Macedo et al. (2019) GMM conditioned on GMVs generated
+ by the AbrahamsonEtAl2015SInter GSIM
+ """
+ GSIM_CLASS = MacedoEtAl2019SSlab
+ MEAN_FILE = "macedo_2019/macedo_2019_sslab_mean.csv"
+ TOTAL_FILE = "macedo_2019/macedo_2019_sslab_total_stddev.csv"
+ INTER_FILE = "macedo_2019/macedo_2019_sslab_inter_event_stddev.csv"
+ INTRA_FILE = "macedo_2019/macedo_2019_sslab_intra_event_stddev.csv"
+ GMM_GMPE = {"AbrahamsonEtAl2015SSlab": {}}
diff --git a/openquake/qa_tests_data/classical/README.md b/openquake/qa_tests_data/classical/README.md
index 7682982abb9b..2d105ea15af5 100644
--- a/openquake/qa_tests_data/classical/README.md
+++ b/openquake/qa_tests_data/classical/README.md
@@ -49,5 +49,6 @@
| case_75 | Tests calculation with multi-fault source |
| case_76 | Tests for Canada SHM6 |
| case_77 | Tests Modifiable GMPE with Tabular GMM |
-| case_82 | tests two mps, only one with reqv that should collapse points
-| case_83 | Tests non-ergodic path effect modifications for Zhao et al. 2016 GMM |
+| case_82 | tests two mps, only one with reqv that should collapse points |
+| case_83 | Tests non-ergodic path effect modifications for Zhao et al. 2016 GMM |
+| case_85 | Tests Conditional GMPE (Macedo Et Al (2019)) in classical PSHA |
diff --git a/openquake/qa_tests_data/classical/case_85/PEERInterface_Characteristic.xml b/openquake/qa_tests_data/classical/case_85/PEERInterface_Characteristic.xml
new file mode 100644
index 000000000000..d2cdf936dd6a
--- /dev/null
+++ b/openquake/qa_tests_data/classical/case_85/PEERInterface_Characteristic.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+ -65.50625 -0.44967 25.0 -65.50625 0.44967 25.0
+
+
+
+
+
+
+ -65.0 -0.44967 57.5 -65.0 0.44967 57.5
+
+
+
+
+
+
+ -64.5866 -0.44967 103.46 -64.5866 0.44967 103.46
+
+
+
+
+
+
+
+ 8.0
+ 0.01
+
+
+ 90.0
+
+
+
+
+
diff --git a/openquake/qa_tests_data/classical/case_85/__init__.py b/openquake/qa_tests_data/classical/case_85/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/openquake/qa_tests_data/classical/case_85/expected/hazard_curve-mean-IA.csv b/openquake/qa_tests_data/classical/case_85/expected/hazard_curve-mean-IA.csv
new file mode 100644
index 000000000000..89bbd452a3b7
--- /dev/null
+++ b/openquake/qa_tests_data/classical/case_85/expected/hazard_curve-mean-IA.csv
@@ -0,0 +1,4 @@
+#,,,,,,,,,,"generated_by='OpenQuake engine 3.19.0-git81ad5a9a37', start_date='2023-11-08T09:50:09', checksum=2241114256, kind='mean', investigation_time=1.0, imt='IA'"
+custom_site_id,lon,lat,depth,poe-0.0010000,poe-0.1000000,poe-0.1500000,poe-0.2000000,poe-0.5000000,poe-1.0000000,poe-5.0000000
+1001,-65.28146,0.00000,0.00000,9.950161E-03,9.896666E-03,9.822041E-03,9.725034E-03,8.947998E-03,7.632077E-03,2.914548E-03
+1002,-64.38200,0.00000,0.00000,9.950161E-03,8.929521E-03,8.238643E-03,7.600158E-03,4.949212E-03,2.878815E-03,3.343523E-04
diff --git a/openquake/qa_tests_data/classical/case_85/gmpe_logic_tree_interface_macedo.xml b/openquake/qa_tests_data/classical/case_85/gmpe_logic_tree_interface_macedo.xml
new file mode 100644
index 000000000000..0e82743f23a4
--- /dev/null
+++ b/openquake/qa_tests_data/classical/case_85/gmpe_logic_tree_interface_macedo.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+ [MacedoEtAl2019SInter]
+ gmpe.AbrahamsonEtAl2015SInter = {}
+
+
+ 0.5
+
+
+
+
+ [MacedoEtAl2019SInter]
+ gmpe.AbrahamsonEtAl2015SInter = {}
+ region = "South America"
+
+
+ 0.5
+
+
+
+
+
diff --git a/openquake/qa_tests_data/classical/case_85/job.ini b/openquake/qa_tests_data/classical/case_85/job.ini
new file mode 100755
index 000000000000..31c779bde970
--- /dev/null
+++ b/openquake/qa_tests_data/classical/case_85/job.ini
@@ -0,0 +1,36 @@
+[general]
+
+description = Macedo et al. (2019) Conditional GMM for subduction interface
+calculation_mode = classical
+random_seed = 23
+
+[geometry]
+
+site_model_file = site_model.xml
+
+[logic_tree]
+
+number_of_logic_tree_samples = 0
+
+[erf]
+
+rupture_mesh_spacing = 1.0
+complex_fault_mesh_spacing = 10.0
+width_of_mfd_bin = 0.05
+area_source_discretization = 1.0
+
+[calculation]
+
+source_model_logic_tree_file = peer_source_model_logic_tree.xml
+gsim_logic_tree_file = gmpe_logic_tree_interface_macedo.xml
+investigation_time = 1.0
+intensity_measure_types_and_levels = {"IA": [0.001, 0.1, 0.15, 0.2, 0.5, 1.0, 5.0]}
+truncation_level = 20
+maximum_distance = 1000.0
+
+[output]
+
+export_dir = ./expected
+hazard_maps = false
+uniform_hazard_spectra = false
+poes =
diff --git a/openquake/qa_tests_data/classical/case_85/peer_source_model_logic_tree.xml b/openquake/qa_tests_data/classical/case_85/peer_source_model_logic_tree.xml
new file mode 100755
index 000000000000..ac2a84c3ae18
--- /dev/null
+++ b/openquake/qa_tests_data/classical/case_85/peer_source_model_logic_tree.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ PEERInterface_Characteristic.xml
+ 1.0
+
+
+
+
+
diff --git a/openquake/qa_tests_data/classical/case_85/site_model.xml b/openquake/qa_tests_data/classical/case_85/site_model.xml
new file mode 100644
index 000000000000..6c4f38529d0f
--- /dev/null
+++ b/openquake/qa_tests_data/classical/case_85/site_model.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+