Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/frequency unit #532

Merged
merged 25 commits into from
Sep 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
96cef24
climada.hazard.base: nominal introduction of frequency_unit attribute
emanuel-schmid Aug 17, 2022
4b5b6e1
climada.engine.impact: nominal introduction of frequency_unit attribute
emanuel-schmid Aug 18, 2022
ab36f17
engine.impact: deal with frequency unit in member <> df.column mapping
emanuel-schmid Aug 29, 2022
b7c1881
white space cosmetics
emanuel-schmid Aug 29, 2022
128e941
Merge branch 'develop' into feature/frequency_unit
emanuel-schmid Aug 29, 2022
941d930
engine.impact search for 'annual' and replace
emanuel-schmid Aug 29, 2022
4e8134a
engine.impact search for 'annual' and replace
emanuel-schmid Sep 1, 2022
e0f2795
Merge branch 'develop' into feature/frequency_unit
emanuel-schmid Sep 1, 2022
cfdfe62
impact tutorial: add frequency unit description to input attributes
emanuel-schmid Sep 2, 2022
2af9711
impact-plotting: move eaI_title out of util.plot
emanuel-schmid Sep 2, 2022
6b90c04
fixit
emanuel-schmid Sep 2, 2022
511fbce
fixit
emanuel-schmid Sep 2, 2022
138e528
pylint adherence, add warning about setting frequency when frequency …
emanuel-schmid Sep 2, 2022
c093bde
hazard.test.test_base: adapt tests to new attribute frequency_unit
emanuel-schmid Sep 2, 2022
e3f24a6
engine.impact.test_impact: adapt tests to new attribute frequency_unit
emanuel-schmid Sep 2, 2022
d61640e
ImpactFreqCurve: defaults for tag, return_per and impact fields
emanuel-schmid Sep 5, 2022
166aaaa
cosmetics
emanuel-schmid Sep 5, 2022
eacd6bc
engine.impact_data.emdat_to_impact: explicit frequency_unit
emanuel-schmid Sep 5, 2022
cf56266
cosmetics
emanuel-schmid Sep 5, 2022
51cd8b6
read-the-docs config: include impact_calc
emanuel-schmid Sep 15, 2022
4284223
Merge branch 'develop' into feature/frequency_unit
emanuel-schmid Sep 15, 2022
4d9ad15
enging.impact: introduce "monthly" as special case for `_eai_title`
emanuel-schmid Sep 15, 2022
edf0cd0
Merge branch 'feature/frequency_unit' of github.com:CLIMADA-project/c…
emanuel-schmid Sep 15, 2022
9e3e955
Merge branch 'develop' into feature/frequency_unit
emanuel-schmid Sep 19, 2022
e1d4680
hazard.base: change warning about frequency reset
emanuel-schmid Sep 19, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 83 additions & 60 deletions climada/engine/impact.py

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions climada/engine/impact_calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

---

Define Impact and ImpactFreqCurve classes.
Define ImpactCalc class.
"""

__all__ = ['ImpactCalc']
Expand Down Expand Up @@ -448,9 +448,9 @@ def stitch_risk_metrics(self, imp_mat_gen):
at_event : np.array
Accumulated damage for each event
eai_exp : np.array
Expected annual impact for each exposure point
Expected impact within a period of 1/frequency_unit for each exposure point
aai_agg : float
Average annual impact aggregated
Average impact within a period of 1/frequency_unit aggregated
"""
at_event = np.zeros(self.n_events)
eai_exp = np.zeros(self.n_exp_pnt)
Expand Down Expand Up @@ -530,11 +530,11 @@ def eai_exp_from_mat(mat, freq):
imp_mat : sparse.csr_matrix
matrix num_events x num_exp with impacts.
frequency : np.array
annual frequency of events
frequency of events within a period of 1/frequency_unit
Returns
-------
eai_exp : np.array
expected annual impact for each exposure
expected impact within a period of 1/frequency_unit for each exposure
"""
n_events = freq.size
freq_csr = sparse.csr_matrix( #vector n_events x 1
Expand Down Expand Up @@ -565,12 +565,12 @@ def aai_agg_from_eai_exp(eai_exp):
Parameters
----------
eai_exp : np.array
expected annual impact for each exposure point
expected impact within a period of 1/frequency_unit for each exposure point

Returns
-------
float
average annual impact aggregated
average aggregated impact within a period of 1/frequency_unit
"""
return np.sum(eai_exp)

Expand All @@ -590,11 +590,11 @@ def risk_metrics(cls, mat, freq):
Returns
-------
eai_exp: np.array
expected annual impact at each exposure point
expected impact within a period of 1/frequency_unit at each exposure point
at_event: np.array()
total impact for each event
aai_agg : float
average annual impact aggregated over all exposure points
average impact within a period of 1/frequency_unit aggregated over all exposure points
"""
eai_exp = cls.eai_exp_from_mat(mat, freq)
at_event = cls.at_event_from_mat(mat)
Expand Down
18 changes: 10 additions & 8 deletions climada/engine/impact_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -867,14 +867,15 @@ def emdat_to_impact(emdat_file_csv, hazard_type_climada, year_range=None, countr

Returns
-------
impact_instance : instance of climada.engine.Impact
impact object of same format as output from CLIMADA
impact computation.
Values scaled with GDP to reference_year if reference_year is given.
i.e. current US$ for imp_str="Total Damages ('000 US$) scaled" (factor 1000 is applied)
impact_instance.eai_exp holds expected annual impact for each country.
impact_instance.coord_exp holds rough central coordinates for each country.
countries (list): ISO3-codes of countries in same order as in impact_instance.eai_exp
impact_instance : climada.engine.Impact
impact object of same format as output from CLIMADA
impact computation.
Values scaled with GDP to reference_year if reference_year is given.
i.e. current US$ for imp_str="Total Damages ('000 US$) scaled" (factor 1000 is applied)
impact_instance.eai_exp holds expected impact for each country (within 1/frequency_unit).
impact_instance.coord_exp holds rough central coordinates for each country.
countries : list of str
ISO3-codes of countries in same order as in impact_instance.eai_exp
"""
if "Total Damages" in imp_str:
imp_str = "Total Damages ('000 US$)"
Expand Down Expand Up @@ -945,6 +946,7 @@ def emdat_to_impact(emdat_file_csv, hazard_type_climada, year_range=None, countr
if not year_range:
year_range = [em_data['Year'].min(), em_data['Year'].max()]
impact_instance.frequency = np.ones(em_data.shape[0]) / (1 + np.diff(year_range))
impact_instance.frequency_unit = '1/year'
impact_instance.tot_value = 0
impact_instance.aai_agg = np.nansum(impact_instance.at_event * impact_instance.frequency)
impact_instance.unit = 'USD'
Expand Down
23 changes: 22 additions & 1 deletion climada/engine/test/test_impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ def test_from_eih_pass(self):
self.assertEqual(imp.aai_agg, fake_aai_agg)
self.assertEqual(imp.imp_mat.size, 0)
self.assertEqual(imp.unit, exp.value_unit)
self.assertEqual(imp.frequency_unit, HAZ.frequency_unit)
self.assertEqual(imp.tot_value, tot_value)
np.testing.assert_array_almost_equal(imp.event_id, HAZ.event_id)
np.testing.assert_array_almost_equal(imp.event_name, HAZ.event_name)
Expand Down Expand Up @@ -97,6 +98,7 @@ def test_ref_value_pass(self):
imp.at_event[8] = 0.569142464157450e9
imp.at_event[9] = 0.467572545849132e9
imp.unit = 'USD'
imp.frequency_unit = '1/day'

ifc = imp.calc_freq_curve()
self.assertEqual(10, len(ifc.return_per))
Expand All @@ -123,6 +125,7 @@ def test_ref_value_pass(self):
self.assertEqual(0, ifc.impact[0])
self.assertEqual('Exceedance frequency curve', ifc.label)
self.assertEqual('USD', ifc.unit)
self.assertEqual('1/day', ifc.frequency_unit)

def test_ref_value_rp_pass(self):
"""Test result against reference value with given return periods"""
Expand All @@ -140,6 +143,7 @@ def test_ref_value_rp_pass(self):
imp.at_event[8] = 0.569142464157450e9
imp.at_event[9] = 0.467572545849132e9
imp.unit = 'USD'
imp.frequency_unit = '1/week'

ifc = imp.calc_freq_curve(np.array([100, 500, 1000]))
self.assertEqual(3, len(ifc.return_per))
Expand All @@ -152,6 +156,7 @@ def test_ref_value_rp_pass(self):
self.assertEqual(3287314329.129928, ifc.impact[2])
self.assertEqual('Exceedance frequency curve', ifc.label)
self.assertEqual('USD', ifc.unit)
self.assertEqual('1/week', ifc.frequency_unit)

class TestImpactPerYear(unittest.TestCase):
"""Test calc_impact_year_set method"""
Expand All @@ -171,7 +176,6 @@ def test_impact_per_year_sum(self):
imp.at_event[7] = 0.381063674256423e9
imp.at_event[8] = 0.569142464157450e9
imp.at_event[9] = 0.467572545849132e9
imp.unit = 'USD'
emanuel-schmid marked this conversation as resolved.
Show resolved Hide resolved
imp.date = np.array([732801, 716160, 718313, 712468, 732802,
729285, 732931, 715419, 722404, 718351])

Expand Down Expand Up @@ -233,6 +237,7 @@ def test_write_read_ev_test(self):
imp_write.tot_value = 1000
imp_write.aai_agg = 1001
imp_write.unit = 'USD'
imp_write.frequency_unit = '1/month'

file_name = DATA_FOLDER.joinpath('test.csv')
imp_write.write_csv(file_name)
Expand All @@ -247,6 +252,7 @@ def test_write_read_ev_test(self):
self.assertEqual(imp_write.tot_value, imp_read.tot_value)
self.assertEqual(imp_write.aai_agg, imp_read.aai_agg)
self.assertEqual(imp_write.unit, imp_read.unit)
self.assertEqual(imp_write.frequency_unit, imp_read.frequency_unit)
self.assertEqual(
0, len([i for i, j in zip(imp_write.event_name, imp_read.event_name) if i != j]))

Expand All @@ -271,6 +277,7 @@ def test_write_read_exp_test(self):
imp_write.tot_value = 1000
imp_write.aai_agg = 1001
imp_write.unit = 'USD'
imp_write.frequency_unit = '1/month'

file_name = DATA_FOLDER.joinpath('test.csv')
imp_write.write_csv(file_name)
Expand All @@ -285,6 +292,7 @@ def test_write_read_exp_test(self):
self.assertEqual(imp_write.tot_value, imp_read.tot_value)
self.assertEqual(imp_write.aai_agg, imp_read.aai_agg)
self.assertEqual(imp_write.unit, imp_read.unit)
self.assertEqual(imp_write.frequency_unit, imp_read.frequency_unit)
self.assertEqual(
0, len([i for i, j in zip(imp_write.event_name, imp_read.event_name) if i != j]))
self.assertIsInstance(imp_read.crs, str)
Expand Down Expand Up @@ -313,6 +321,7 @@ def test_excel_io(self):
self.assertEqual(imp_write.tot_value, imp_read.tot_value)
self.assertEqual(imp_write.aai_agg, imp_read.aai_agg)
self.assertEqual(imp_write.unit, imp_read.unit)
self.assertEqual(imp_write.frequency_unit, imp_read.frequency_unit)
self.assertEqual(
0, len([i for i, j in zip(imp_write.event_name, imp_read.event_name) if i != j]))
self.assertIsInstance(imp_read.crs, str)
Expand Down Expand Up @@ -377,10 +386,12 @@ def test_risk_trans_pass(self):
imp.tot_value = 10
imp.aai_agg = 100
imp.unit = 'USD'
imp.frequency_unit = '1/month'
imp.imp_mat = sparse.csr_matrix(np.empty((0, 0)))

new_imp, imp_rt = imp.calc_risk_transfer(2, 10)
self.assertEqual(new_imp.unit, imp.unit)
self.assertEqual(new_imp.frequency_unit, imp.frequency_unit)
self.assertEqual(new_imp.tot_value, imp.tot_value)
np.testing.assert_array_equal(new_imp.imp_mat.toarray(), imp.imp_mat.toarray())
self.assertEqual(new_imp.event_name, imp.event_name)
Expand All @@ -393,6 +404,7 @@ def test_risk_trans_pass(self):
self.assertAlmostEqual(new_imp.aai_agg, 4.0)

self.assertEqual(imp_rt.unit, imp.unit)
self.assertEqual(imp_rt.frequency_unit, imp.frequency_unit)
self.assertEqual(imp_rt.tot_value, imp.tot_value)
np.testing.assert_array_equal(imp_rt.imp_mat.toarray(), imp.imp_mat.toarray())
self.assertEqual(imp_rt.event_name, imp.event_name)
Expand Down Expand Up @@ -436,6 +448,7 @@ def dummy_impact():
imp.tot_value = 7
imp.aai_agg = 14.4
imp.unit = 'USD'
imp.frequency_unit = '1/month'
imp.imp_mat = sparse.csr_matrix(np.array([
[0,0], [1,1], [2,2], [3,3], [30,30], [31,31]
]))
Expand All @@ -452,6 +465,7 @@ def test_select_event_id_pass(self):

self.assertTrue(u_coord.equal_crs(sel_imp.crs, imp.crs))
self.assertEqual(sel_imp.unit, imp.unit)
self.assertEqual(sel_imp.frequency_unit, imp.frequency_unit)

np.testing.assert_array_equal(sel_imp.event_id, [10, 11, 12])
self.assertEqual(sel_imp.event_name, [0, 1, 'two'])
Expand All @@ -477,6 +491,7 @@ def test_select_event_name_pass(self):

self.assertTrue(u_coord.equal_crs(sel_imp.crs, imp.crs))
self.assertEqual(sel_imp.unit, imp.unit)
self.assertEqual(sel_imp.frequency_unit, imp.frequency_unit)

np.testing.assert_array_equal(sel_imp.event_id, [10, 11, 12])
self.assertEqual(sel_imp.event_name, [0, 1, 'two'])
Expand All @@ -502,6 +517,7 @@ def test_select_dates_pass(self):

self.assertTrue(u_coord.equal_crs(sel_imp.crs, imp.crs))
self.assertEqual(sel_imp.unit, imp.unit)
self.assertEqual(sel_imp.frequency_unit, imp.frequency_unit)

np.testing.assert_array_equal(sel_imp.event_id, [10, 11, 12])
self.assertEqual(sel_imp.event_name, [0, 1, 'two'])
Expand All @@ -527,6 +543,7 @@ def test_select_coord_exp_pass(self):

self.assertTrue(u_coord.equal_crs(sel_imp.crs, imp.crs))
self.assertEqual(sel_imp.unit, imp.unit)
self.assertEqual(sel_imp.frequency_unit, imp.frequency_unit)

np.testing.assert_array_equal(sel_imp.event_id, imp.event_id)
self.assertEqual(sel_imp.event_name, imp.event_name)
Expand Down Expand Up @@ -570,6 +587,7 @@ def test_select_event_identity_pass(self):

self.assertTrue(u_coord.equal_crs(sel_imp.crs, imp.crs))
self.assertEqual(sel_imp.unit, imp.unit)
self.assertEqual(sel_imp.frequency_unit, imp.frequency_unit)

np.testing.assert_array_equal(sel_imp.event_id, imp.event_id)
self.assertEqual(sel_imp.event_name, imp.event_name)
Expand Down Expand Up @@ -599,6 +617,7 @@ def test_select_new_attributes(self):

self.assertTrue(u_coord.equal_crs(sel_imp.crs, imp.crs))
self.assertEqual(sel_imp.unit, imp.unit)
self.assertEqual(sel_imp.frequency_unit, imp.frequency_unit)

np.testing.assert_array_equal(sel_imp.event_id, [10, 11, 12])
self.assertEqual(sel_imp.event_name, [0, 1, 'two'])
Expand All @@ -624,6 +643,7 @@ def test_select_nothing(self):
self.assertIsInstance(sel_imp.imp_mat, sparse.csr_matrix)
self.assertTrue(u_coord.equal_crs(sel_imp.crs, imp.crs))
self.assertEqual(sel_imp.unit, imp.unit)
self.assertEqual(sel_imp.frequency_unit, imp.frequency_unit)
self.assertEqual(sel_imp.event_id.size, 0)
self.assertEqual(len(sel_imp.event_name), 0)
self.assertEqual(sel_imp.date.size, 0)
Expand All @@ -640,6 +660,7 @@ def test_select_id_name_dates_pass(self):

self.assertTrue(u_coord.equal_crs(sel_imp.crs, imp.crs))
self.assertEqual(sel_imp.unit, imp.unit)
self.assertEqual(sel_imp.frequency_unit, imp.frequency_unit)

np.testing.assert_array_equal(sel_imp.event_id, [10, 11, 12])
self.assertEqual(sel_imp.event_name, [0, 1, 'two'])
Expand Down
2 changes: 1 addition & 1 deletion climada/engine/unsequa/calc_impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def uncertainty(self,
"""
Computes the impact for each sample in unc_data.sample_df.

By default, the aggregated average annual impact
By default, the aggregated average impact within a period of 1/frequency_unit
(impact.aai_agg) and the excees impact at return periods rp
(imppact.calc_freq_curve(self.rp).impact) is computed.
Optionally, eai_exp and at_event is computed (this may require
Expand Down
Loading