Skip to content

Commit 38ceb4a

Browse files
author
Graeme Weatherill
authored
Merge pull request #67 from g-weatherill/trellis_test
Adds unit tests for trellis plots
2 parents 20ff502 + bf9e643 commit 38ceb4a

9 files changed

+284
-17
lines changed

smtk/trellis/configure.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -558,13 +558,11 @@ def get_gsim_contexts(self):
558558
setattr(dctx, "rvolc", np.zeros_like(self.target_sites.mesh.lons))
559559
# Sites
560560
sctx = SitesContext()
561-
key_list = ['_vs30', '_vs30measured', '_z1pt0', '_z2pt5', '_backarc']
561+
key_list = ["lons", "lats", "vs30", "vs30measured", "z1pt0", "z2pt5",
562+
"backarc"]
562563
for key in key_list:
563-
setattr(sctx, key[1:], getattr(self.target_sites, key))
564-
for key in ['lons', 'lats']:
565-
setattr(sctx, key, getattr(self.target_sites, key))
564+
setattr(sctx, key, self.target_sites.array[key])
566565

567-
568566
# Rupture
569567
rctx = RuptureContext()
570568
setattr(rctx, 'mag', self.magnitude)
@@ -727,7 +725,7 @@ def get_target_sites_point(self, distance, distance_type, vs30,
727725
:param floar z2pt5:
728726
Depth to 2.5 km/s interface
729727
"""
730-
if not distance_type in POINT_AT_MAPPING.keys():
728+
if not distance_type in list(POINT_AT_MAPPING.keys()):
731729
raise ValueError("Distance type must be one of: Rupture ('rrup'), "
732730
"Joyner-Boore ('rjb'), Epicentral ('repi') or "
733731
"Hypocentral ('rhyp')")
@@ -799,7 +797,7 @@ def filter_hanging_wall(self, filter_type=None):
799797
if not filter_type:
800798
# Considers both footwall and hanging wall
801799
return self.target_sites
802-
elif not filter_type in ['HW', 'FW']:
800+
elif not filter_type in ('HW', 'FW'):
803801
raise ValueError('Hanging wall filter must be either "HW" or "FW"')
804802
else:
805803
pass

smtk/trellis/trellis_plots.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ def from_rupture_model(cls, rupture, gsims, imts, stddevs='Total',
361361
kwargs.setdefault('distance_type', "rjb")
362362
kwargs.setdefault('xlim', None)
363363
kwargs.setdefault('ylim', None)
364+
print(rupture.__class__)
364365
assert isinstance(rupture, GSIMRupture)
365366
magnitudes = [rupture.magnitude]
366367
sctx, rctx, dctx = rupture.get_gsim_contexts()
@@ -548,9 +549,9 @@ def _get_ylabel(self, i_m):
548549
units = PLOT_UNITS[i_m]
549550
return "Mean {:s} ({:s})".format(i_m, units)
550551

551-
def to_json(self):
552+
def to_dict(self):
552553
"""
553-
Serializes the ground motion values to json
554+
Parse the ground motion values to a dictionary
554555
"""
555556
gmvs = self.get_ground_motion_values()
556557
gmv_dict = OrderedDict([
@@ -572,7 +573,13 @@ def to_json(self):
572573
iml_to_list.append(val)
573574
gmv_dict["figures"][imt].append((gsim, iml_to_list))
574575
gmv_dict["figures"][imt] = OrderedDict(gmv_dict["figures"][imt])
575-
return json.dumps(gmv_dict)
576+
return gmv_dict
577+
578+
def to_json(self):
579+
"""
580+
Serializes the ground motion values to json
581+
"""
582+
return json.dumps(self.to_dict())
576583

577584
def get_ground_motion_values(self):
578585
"""
@@ -773,7 +780,7 @@ class DistanceIMTTrellis(MagnitudeIMTTrellis):
773780
def __init__(self, magnitudes, distances, gsims, imts, params,
774781
stddevs="Total", **kwargs):
775782
"""
776-
Instandi
783+
Instantiation
777784
"""
778785
if isinstance(magnitudes, float):
779786
magnitudes = [magnitudes]
@@ -881,9 +888,9 @@ def _get_ylabel(self, i_m):
881888
units = PLOT_UNITS[i_m]
882889
return "Mean {:s} ({:s})".format(i_m, units)
883890

884-
def to_json(self):
891+
def to_dict(self):
885892
"""
886-
Exports ground motion values to json
893+
Parses the ground motion values to a dictionary
887894
"""
888895
gmvs = self.get_ground_motion_values()
889896
dist_label = "{:s} (km)".format(DISTANCE_LABEL_MAP[self.distance_type])
@@ -900,7 +907,13 @@ def to_json(self):
900907
imt_dict["yvalues"].append((gsim, data))
901908
imt_dict["yvalues"] = OrderedDict(imt_dict["yvalues"])
902909
gmv_dict["figures"][imt] = imt_dict
903-
return json.dumps(gmv_dict)
910+
return gmv_dict
911+
912+
def to_json(self):
913+
"""
914+
Exports ground motion values to json
915+
"""
916+
return json.dumps(self.to_dict())
904917

905918
def pretty_print(self, filename=None, sep=","):
906919
"""
@@ -1370,9 +1383,9 @@ def _set_labels(self, i_m, ax):
13701383
ax.set_xlabel("Period (s)", fontsize=14)
13711384
ax.set_ylabel("Sa (g)", fontsize=14)
13721385

1373-
def to_json(self):
1386+
def to_dict(self):
13741387
"""
1375-
Export ground motion values dictionary to json
1388+
Export ground motion values to a dictionary
13761389
"""
13771390
gmvs = self.get_ground_motion_values()
13781391
periods = [float(val.split("SA(")[1].rstrip(")"))
@@ -1405,7 +1418,13 @@ def to_json(self):
14051418
else:
14061419
gmv_dict["figures"][pos_name]["yvalues"][gsim].\
14071420
append(None)
1408-
return json.dumps(gmv_dict)
1421+
return gmv_dict
1422+
1423+
def to_json(self):
1424+
"""
1425+
Exports the ground motion values to json
1426+
"""
1427+
return json.dumps(self.to_dict())
14091428

14101429
def _get_ylabel(self, i_m):
14111430
"""

tests/trellis/data/test_distance_imt_trellis.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

tests/trellis/data/test_distance_sigma_imt_trellis.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

tests/trellis/data/test_magnitude_distance_spectra_sigma_trellis.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

tests/trellis/data/test_magnitude_distance_spectra_trellis.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

tests/trellis/data/test_magnitude_imt_trellis.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

tests/trellis/data/test_magnitude_sigma_imt_trellis.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

tests/trellis/trellis_test.py

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
# vim: tabstop=4 shiftwidth=4 softtabstop=4
4+
#
5+
# Copyright (C) 2014-2017 GEM Foundation and G. Weatherill
6+
#
7+
# OpenQuake is free software: you can redistribute it and/or modify it
8+
# under the terms of the GNU Affero General Public License as published
9+
# by the Free Software Foundation, either version 3 of the License, or
10+
# (at your option) any later version.
11+
#
12+
# OpenQuake is distributed in the hope that it will be useful,
13+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
# GNU Affero General Public License for more details.
16+
#
17+
# You should have received a copy of the GNU Affero General Public License
18+
# along with OpenQuake. If not, see <http://www.gnu.org/licenses/>.
19+
"""
20+
Tests for generation of data for trellis plots
21+
"""
22+
import unittest
23+
import os
24+
import json
25+
import numpy as np
26+
import smtk.trellis.trellis_plots as trpl
27+
import smtk.trellis.configure as rcfg
28+
29+
30+
BASE_DATA_PATH = os.path.join(os.path.dirname(__file__), "data")
31+
32+
33+
class BaseTrellisTest(unittest.TestCase):
34+
"""
35+
This core test is designed to run a series of trellis plot calculations
36+
and ensure compatibility with previously generated results
37+
"""
38+
39+
TEST_FILE = None
40+
41+
def setUp(self):
42+
self.imts = ["PGA", "SA(0.2)", "SA(2.0)", "SA(3.0)"]
43+
self.periods = [0.05, 0.075, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16,
44+
0.17, 0.18, 0.19, 0.20, 0.22, 0.24, 0.26, 0.28, 0.30,
45+
0.32, 0.34, 0.36, 0.38, 0.40, 0.42, 0.44, 0.46, 0.48,
46+
0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95,
47+
1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0,
48+
3.0, 4.001, 5.0, 7.5, 10.0]
49+
50+
self.gsims = ["AkkarBommer2010", "CauzziFaccioli2008",
51+
"ChiouYoungs2008", "ZhaoEtAl2006Asc", "AkkarEtAlRjb2014",
52+
"BindiEtAl2014Rjb", "CauzziEtAl2014", "DerrasEtAl2014",
53+
"AbrahamsonEtAl2014", "BooreEtAl2014", "ChiouYoungs2014",
54+
"CampbellBozorgnia2014", "KothaEtAl2016Italy",
55+
"KothaEtAl2016Other", "KothaEtAl2016Turkey",
56+
"ZhaoEtAl2016Asc", "BindiEtAl2017Rjb"]
57+
58+
59+
class DistanceTrellisTest(BaseTrellisTest):
60+
TEST_FILE = "test_distance_imt_trellis.json"
61+
62+
def compare_jsons(self, old, new):
63+
"""
64+
Compares the json data from file with the new data from trellis plot
65+
"""
66+
# Check x-labels are the same
67+
self.assertEqual(old["xlabel"], new["xlabel"])
68+
# Check x-values are the same
69+
np.testing.assert_array_almost_equal(old["xvalues"], new["xvalues"], 7)
70+
for imt in old["figures"]:
71+
self.assertEqual(old["figures"][imt]["ylabel"],
72+
new["figures"][imt]["ylabel"])
73+
for gsim in old["figures"][imt]["yvalues"]:
74+
np.testing.assert_array_almost_equal(
75+
old["figures"][imt]["yvalues"][gsim],
76+
new["figures"][imt]["yvalues"][gsim], 7)
77+
78+
def _run_trellis(self, rupture):
79+
"""
80+
Executes the trellis plotting - for mean
81+
"""
82+
return trpl.DistanceIMTTrellis.from_rupture_model(rupture,
83+
self.gsims,
84+
self.imts,
85+
distance_type="rrup")
86+
87+
def test_distance_imt_trellis(self):
88+
"""
89+
Tests the DistanceIMT trellis data generation
90+
"""
91+
reference = json.load(open(
92+
os.path.join(BASE_DATA_PATH, self.TEST_FILE), "r"))
93+
# Setup rupture
94+
rupture = rcfg.GSIMRupture(6.5, 60., 1.5,
95+
hypocentre_location=(0.5, 0.5))
96+
_ = rupture.get_target_sites_line(250.0, 1.0, 800.0)
97+
# Get trellis calculations
98+
trl = self._run_trellis(rupture)
99+
# Parse the json formatted string to a dictionary string
100+
results = json.loads(trl.to_json())
101+
# Compare the two dictionaries
102+
self.compare_jsons(reference, results)
103+
104+
105+
class DistanceSigmaTrellisTest(DistanceTrellisTest):
106+
TEST_FILE = "test_distance_sigma_imt_trellis.json"
107+
108+
def _run_trellis(self, rupture):
109+
"""
110+
Executes the trellis plotting - for standard deviation
111+
"""
112+
return trpl.DistanceSigmaIMTTrellis.from_rupture_model(
113+
rupture,
114+
self.gsims,
115+
self.imts,
116+
distance_type="rrup")
117+
118+
119+
class MagnitudeTrellisTest(BaseTrellisTest):
120+
TEST_FILE = "test_magnitude_imt_trellis.json"
121+
122+
def _run_trellis(self, magnitudes, distance, properties):
123+
"""
124+
Executes the trellis plotting - for mean
125+
"""
126+
return trpl.MagnitudeIMTTrellis.from_rupture_model(properties,
127+
magnitudes,
128+
distance,
129+
self.gsims,
130+
self.imts)
131+
132+
def compare_jsons(self, old, new):
133+
"""
134+
Compares the json data from file with the new data from trellis plot
135+
"""
136+
self.assertEqual(old["xlabel"], new["xlabel"])
137+
np.testing.assert_array_almost_equal(old["xvalues"], new["xvalues"], 7)
138+
for imt in old["figures"]:
139+
for gsim in old["figures"][imt]:
140+
if gsim == u"ylabel":
141+
self.assertEqual(old["figures"][imt][gsim],
142+
new["figures"][imt][gsim])
143+
else:
144+
np.testing.assert_array_almost_equal(
145+
old["figures"][imt][gsim],
146+
new["figures"][imt][gsim], 7)
147+
148+
def test_magnitude_imt_trellis(self):
149+
"""
150+
Tests the MagnitudeIMT trellis data generation
151+
"""
152+
reference = json.load(open(
153+
os.path.join(BASE_DATA_PATH, self.TEST_FILE), "r"))
154+
magnitudes = np.arange(4., 8.1, 0.1)
155+
distance = 20.
156+
properties = {"dip": 60.0, "rake": -90.0, "aspect": 1.5, "ztor": 0.0,
157+
"vs30": 800.0, "backarc": False, "z1pt0": 50.0,
158+
"z2pt5": 1.0, "line_azimuth": 90.0}
159+
trl = self._run_trellis(magnitudes, distance, properties)
160+
results = json.loads(trl.to_json())
161+
self.compare_jsons(reference, results)
162+
163+
164+
class MagnitudeSigmaTrellisTest(MagnitudeTrellisTest):
165+
TEST_FILE = "test_magnitude_sigma_imt_trellis.json"
166+
167+
def _run_trellis(self, magnitudes, distance, properties):
168+
"""
169+
Executes the trellis plotting - for standard deviation
170+
"""
171+
return trpl.MagnitudeSigmaIMTTrellis.from_rupture_model(properties,
172+
magnitudes,
173+
distance,
174+
self.gsims,
175+
self.imts)
176+
177+
178+
class MagnitudeDistanceSpectraTrellisTest(BaseTrellisTest):
179+
TEST_FILE = "test_magnitude_distance_spectra_trellis.json"
180+
181+
def compare_jsons(self, old, new):
182+
"""
183+
Compares the MagnitudeDistanceSpectra jsons
184+
"""
185+
self.assertEqual(old["xlabel"], new["xlabel"])
186+
self.assertEqual(old["ylabel"], new["ylabel"])
187+
np.testing.assert_array_almost_equal(old["xvalues"], new["xvalues"], 7)
188+
for key in old["figures"]:
189+
self.assertAlmostEqual(old["figures"][key]["magnitude"],
190+
new["figures"][key]["magnitude"], 7)
191+
self.assertAlmostEqual(old["figures"][key]["distance"],
192+
new["figures"][key]["distance"], 7)
193+
self.assertEqual(old["figures"][key]["row"],
194+
new["figures"][key]["row"])
195+
self.assertEqual(old["figures"][key]["column"],
196+
new["figures"][key]["column"])
197+
for gsim in old["figures"][key]["yvalues"]:
198+
old_vals = np.array(old["figures"][key]["yvalues"][gsim])
199+
new_vals = np.array(new["figures"][key]["yvalues"][gsim])
200+
if old_vals.dtype == "O":
201+
# Has None Values - compare element by element
202+
for old_val, new_val in zip(old_vals, new_vals):
203+
if old_val and new_val:
204+
self.assertAlmostEqual(old_val, new_val, 7)
205+
else:
206+
self.assertEqual(old_val, new_val)
207+
else:
208+
np.testing.assert_array_almost_equal(old_vals, new_vals, 7)
209+
210+
def _run_trellis(self, magnitudes, distances, properties):
211+
"""
212+
Executes the trellis plotting - for mean
213+
"""
214+
return trpl.MagnitudeDistanceSpectraTrellis.from_rupture_model(
215+
properties, magnitudes, distances, self.gsims, self.periods,
216+
distance_type="rrup")
217+
218+
def test_magnitude_distance_spectra_trellis(self):
219+
"""
220+
Tests the MagnitudeDistanceSpectra Trellis data generation
221+
"""
222+
reference = json.load(open(
223+
os.path.join(BASE_DATA_PATH, self.TEST_FILE), "r"))
224+
properties = {"dip": 60.0, "rake": -90.0, "aspect": 1.5, "ztor": 0.0,
225+
"vs30": 800.0, "backarc": False, "z1pt0": 50.0,
226+
"z2pt5": 1.0}
227+
magnitudes = [4.0, 5.0, 6.0, 7.0]
228+
distances = [5., 20., 50., 150.0]
229+
trl = self._run_trellis(magnitudes, distances, properties)
230+
results = json.loads(trl.to_json())
231+
self.compare_jsons(reference, results)
232+
233+
234+
class MagnitudeDistanceSpectraSigmaTrellisTest(
235+
MagnitudeDistanceSpectraTrellisTest):
236+
TEST_FILE = "test_magnitude_distance_spectra_sigma_trellis.json"
237+
238+
def _run_trellis(self, magnitudes, distances, properties):
239+
"""
240+
Executes the trellis plotting - for standard deviation
241+
"""
242+
return trpl.MagnitudeDistanceSpectraSigmaTrellis.from_rupture_model(
243+
properties, magnitudes, distances, self.gsims, self.periods,
244+
distance_type="rrup")

0 commit comments

Comments
 (0)