Skip to content

Commit

Permalink
raise ModelError in predictions and residuals
Browse files Browse the repository at this point in the history
  • Loading branch information
rizac committed Nov 24, 2024
1 parent 2cc3f44 commit 0240e18
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 32 deletions.
51 changes: 27 additions & 24 deletions egsim/smtk/residuals.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from .flatfile import (FlatfileError, MissingColumnError, FlatfileMetadata,
ColumnDataError, IncompatibleColumnError, EVENT_ID_COLUMN_NAME)
from .validators import (validate_inputs, harmonize_input_gsims, sa_period,
harmonize_input_imts, validate_imt_sa_limits)
harmonize_input_imts, validate_imt_sa_limits, ModelError)
from .registry import (get_ground_motion_values, Clabel,
ground_motion_properties_required_by)
from .converters import vs30_to_z1pt0_cy14, vs30_to_z2pt5_cb14
Expand Down Expand Up @@ -226,29 +226,32 @@ def get_expected_motions(
columns = []
# Period range for GSIM
for gsim_name, gsim in gsims.items():
# validate SA periods:
imts_ok = validate_imt_sa_limits(gsim, imts)
if not imts_ok:
continue
imt_names, imt_vals = list(imts_ok.keys()), list(imts_ok.values())
cmaker = ContextMaker('*', [gsim], {'imtls': {i: [0] for i in imt_names}})
# TODO above is imtls relevant, or should we use PGA: [0] as in trellis?
# maybe harmonize and document why we do the line above?
mean, total, inter, intra = get_ground_motion_values(
gsim, imt_vals, cmaker.recarray([ctx]))
# assign data to our tmp lists:
columns.extend(product(imt_names, [Clabel.mean], [gsim_name]))
data.append(mean)
stddev_types = gsim.DEFINED_FOR_STANDARD_DEVIATION_TYPES
if const.StdDev.TOTAL in stddev_types:
columns.extend((i, Clabel.total_std, gsim_name) for i in imt_names)
data.append(total)
if const.StdDev.INTER_EVENT in stddev_types:
columns.extend((i, Clabel.inter_ev_std, gsim_name) for i in imt_names)
data.append(inter)
if const.StdDev.INTRA_EVENT in stddev_types:
columns.extend((i, Clabel.intra_ev_std, gsim_name) for i in imt_names)
data.append(intra)
try:
# validate SA periods:
imts_ok = validate_imt_sa_limits(gsim, imts)
if not imts_ok:
continue
imt_names, imt_vals = list(imts_ok.keys()), list(imts_ok.values())
cmaker = ContextMaker('*', [gsim], {'imtls': {i: [0] for i in imt_names}})
# TODO above is imtls relevant, or should we use PGA: [0] as in trellis?
# maybe harmonize and document why we do the line above?
mean, total, inter, intra = get_ground_motion_values(
gsim, imt_vals, cmaker.recarray([ctx]))
# assign data to our tmp lists:
columns.extend(product(imt_names, [Clabel.mean], [gsim_name]))
data.append(mean)
stddev_types = gsim.DEFINED_FOR_STANDARD_DEVIATION_TYPES
if const.StdDev.TOTAL in stddev_types:
columns.extend((i, Clabel.total_std, gsim_name) for i in imt_names)
data.append(total)
if const.StdDev.INTER_EVENT in stddev_types:
columns.extend((i, Clabel.inter_ev_std, gsim_name) for i in imt_names)
data.append(inter)
if const.StdDev.INTRA_EVENT in stddev_types:
columns.extend((i, Clabel.intra_ev_std, gsim_name) for i in imt_names)
data.append(intra)
except Exception as exc:
raise ModelError(f'{gsim_name}: ({exc.__class__.__name__}) {str(exc)}')

return pd.DataFrame(columns=pd.MultiIndex.from_tuples(columns),
data=np.hstack(data), index=ctx.sids)
Expand Down
10 changes: 5 additions & 5 deletions egsim/smtk/scenarios.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from .flatfile import FlatfileMetadata
from .converters import vs30_to_z1pt0_cy14, vs30_to_z2pt5_cb14
from .validators import (validate_inputs, harmonize_input_gsims,
harmonize_input_imts, validate_imt_sa_limits)
harmonize_input_imts, validate_imt_sa_limits, ModelError)


@dataclass
Expand Down Expand Up @@ -103,19 +103,19 @@ class RuptureProperties)
# Get the ground motion values
data = []
columns = []
for gsim_label, gsim in gsims.items():
for gsim_name, gsim in gsims.items():
imts_ok = validate_imt_sa_limits(gsim, imts)
if not imts_ok:
continue
imt_names, imt_vals = list(imts_ok.keys()), list(imts_ok.values())
try:
median, sigma, tau, phi = get_ground_motion_values(gsim, imt_vals, ctxts)
data.append(median)
columns.extend((i, Clabel.median, gsim_label) for i in imt_names)
columns.extend((i, Clabel.median, gsim_name) for i in imt_names)
data.append(sigma)
columns.extend((i, Clabel.std, gsim_label) for i in imt_names)
columns.extend((i, Clabel.std, gsim_name) for i in imt_names)
except Exception as exc:
raise ValueError(f'Error in {gsim_label}: {str(exc)}')
raise ModelError(f'{gsim_name}: ({exc.__class__.__name__}) {str(exc)}')

# distances:
columns.append((
Expand Down
6 changes: 4 additions & 2 deletions tests/django/test_api_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ def test_model_info():
content_type="application/json")
assert response.status_code == 200
assert sorted(response.json().keys()) == ['CauzziEtAl2014']
assert all(isinstance(v, dict) and sorted(v.keys()) == ['doc', 'imts', 'props']
assert all(isinstance(v, dict) and
sorted(v.keys()) == ['doc', 'imts', 'props', 'sa_limits']
for v in response.json().values())

data = {'model': ['BindiEtAl2014Rjb', 'CauzziEtAl2014']}
Expand All @@ -45,7 +46,8 @@ def test_model_info():
content_type="application/json")
assert response.status_code == 200
assert sorted(response.json().keys()) == ['BindiEtAl2014Rjb', 'CauzziEtAl2014']
assert all(isinstance(v, dict) and sorted(v.keys()) == ['doc', 'imts', 'props']
assert all(isinstance(v, dict) and
sorted(v.keys()) == ['doc', 'imts', 'props', 'sa_limits']
for v in response.json().values())

data = {'model': ['x', 'CauzziEtAl2014']}
Expand Down
15 changes: 14 additions & 1 deletion tests/smtk/residuals/test_residuals.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
test residuals computations (standard and likelihood)
"""
import json
from unittest.mock import patch

import numpy as np
import os
import pandas as pd
Expand All @@ -12,7 +14,7 @@
from egsim.smtk.flatfile import read_flatfile, ColumnType
from scipy.constants import g
from egsim.smtk.registry import Clabel

from egsim.smtk.validators import ModelError

# load flatfile once:
BASE_DATA_PATH = os.path.join(os.path.dirname(__file__), "data")
Expand Down Expand Up @@ -212,3 +214,14 @@ def test_assign_series():
raise AssertionError('series are identical, they should not be')
except AssertionError:
pass


@patch('egsim.smtk.residuals.get_ground_motion_values', side_effect=ValueError('a'))
def test_model_error(mock_get_gmv):
with pytest.raises(ModelError) as err:
gsims, imts, flatfile = get_gsims_imts_flatfile()
res_df = get_residuals(gsims, imts, flatfile, likelihood=True)
assert mock_get_gmv.called
# the expected model is the first among the gsims (sorted), so:
expected_model = sorted(gsims)[0]
assert f'{expected_model}: (ValueError) a' in str(err.value)
24 changes: 24 additions & 0 deletions tests/smtk/scenarios/test_scenarios_predictions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
Tests for generation of data for trellis plots
"""
import os
from unittest.mock import patch

import numpy as np
import pandas as pd
import pytest

from openquake.hazardlib.gsim.akkar_2014 import AkkarEtAlRjb2014
from openquake.hazardlib.gsim.bindi_2014 import BindiEtAl2014Rjb
Expand All @@ -13,6 +16,7 @@
from egsim.smtk.registry import Clabel
from egsim.smtk.flatfile import ColumnType, FlatfileMetadata
from egsim.smtk import scenarios
from egsim.smtk.validators import ModelError

BASE_DATA_PATH = os.path.join(os.path.dirname(__file__), "data")

Expand Down Expand Up @@ -297,3 +301,23 @@ def open_ref_hdf(file_name) -> pd.DataFrame:
new_ref = pd.DataFrame({v: ref[c] for c, v in c_mapping.items()})
new_ref.columns = pd.MultiIndex.from_tuples(new_ref.columns)
return new_ref


@patch('egsim.smtk.scenarios.get_ground_motion_values', side_effect=ValueError('a'))
def test_model_error(mock_get_gmv):
magnitudes = np.arange(4., 8.1, 0.1)
distance = 20.

with pytest.raises(ModelError) as err:
dfr = get_scenarios_predictions(
gsims,
imts,
magnitudes,
distance,
scenarios.RuptureProperties(dip=60.0, aspect=1.5, rake=-90),
scenarios.SiteProperties(vs30=800, z1pt0=50., z2pt5=1.)
)
assert mock_get_gmv.called
# the expected model is the first among the gsims (sorted), so:
expected_model = sorted(gsims)[0]
assert f'{expected_model}: (ValueError) a' in str(err.value)

0 comments on commit 0240e18

Please sign in to comment.