Skip to content

Commit d66c8dd

Browse files
authored
Impact forecast class (#1168)
* Add ImpactForecast * Add unit tests
1 parent 1688641 commit d66c8dd

File tree

4 files changed

+147
-18
lines changed

4 files changed

+147
-18
lines changed

climada/engine/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@
2222
from .cost_benefit import *
2323
from .impact import *
2424
from .impact_calc import *
25+
from .impact_forecast import ImpactForecast

climada/engine/impact_forecast.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,73 @@
1818
1919
Define Forecast variant of Impact.
2020
"""
21+
22+
import logging
23+
24+
import numpy as np
25+
26+
from ..util import log_level
27+
from ..util.forecast import Forecast
28+
from .impact import Impact
29+
30+
LOGGER = logging.getLogger(__name__)
31+
32+
33+
class ImpactForecast(Forecast, Impact):
34+
"""An impact object with forecast information"""
35+
36+
def __init__(
37+
self,
38+
*,
39+
lead_time: np.ndarray | None,
40+
member: np.ndarray | None,
41+
**impact_kwargs,
42+
):
43+
"""Initialize the impact forecast.
44+
45+
Parameters
46+
----------
47+
lead_time : np.ndarray, optional
48+
The lead time associated with each event entry
49+
member : np.ndarray, optional
50+
The ensemble member associated with each event entry
51+
impact_kwargs
52+
Keyword-arguments passed to ~:py:class`climada.engine.impact.Impact`.
53+
"""
54+
# TODO: Maybe assert array lengths?
55+
super().__init__(lead_time=lead_time, member=member, **impact_kwargs)
56+
57+
@classmethod
58+
def from_impact(
59+
cls, impact: Impact, lead_time: np.ndarray | None, member: np.ndarray | None
60+
):
61+
"""Create an impact forecast from an impact object and forecast information.
62+
63+
Parameters
64+
----------
65+
impact : climada.engine.impact.Impact
66+
The impact object whose data to use in the forecast object
67+
lead_time : np.ndarray, optional
68+
The lead time associated with each event entry
69+
member : np.ndarray, optional
70+
The ensemble member associated with each event entry
71+
"""
72+
with log_level("WARNING", "climada.engine.impact"):
73+
return cls(
74+
lead_time=lead_time,
75+
member=member,
76+
event_id=impact.event_id,
77+
event_name=impact.event_name,
78+
date=impact.date,
79+
frequency=impact.frequency,
80+
frequency_unit=impact.frequency_unit,
81+
coord_exp=impact.coord_exp,
82+
crs=impact.crs,
83+
eai_exp=impact.eai_exp,
84+
at_event=impact.at_event,
85+
tot_value=impact.tot_value,
86+
aai_agg=impact.aai_agg,
87+
unit=impact.unit,
88+
imp_mat=impact.imp_mat,
89+
haz_type=impact.haz_type,
90+
)

climada/engine/test/test_impact.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,26 +47,30 @@
4747
STR_DT = h5py.special_dtype(vlen=str)
4848

4949

50-
def dummy_impact():
51-
"""Return an impact object for testing"""
52-
return Impact(
53-
event_id=np.arange(6) + 10,
54-
event_name=[0, 1, "two", "three", 30, 31],
55-
date=np.arange(6),
56-
coord_exp=np.array([[1, 2], [1.5, 2.5]]),
57-
crs=DEF_CRS,
58-
eai_exp=np.array([7.2, 7.2]),
59-
at_event=np.array([0, 2, 4, 6, 60, 62]),
60-
frequency=np.array([1 / 6, 1 / 6, 1, 1, 1 / 30, 1 / 30]),
61-
tot_value=7,
62-
aai_agg=14.4,
63-
unit="USD",
64-
frequency_unit="1/month",
65-
imp_mat=sparse.csr_matrix(
50+
def impact_kwargs():
51+
return {
52+
"event_id": np.arange(6) + 10,
53+
"event_name": [0, 1, "two", "three", 30, 31],
54+
"date": np.arange(6),
55+
"coord_exp": np.array([[1, 2], [1.5, 2.5]]),
56+
"crs": DEF_CRS,
57+
"eai_exp": np.array([7.2, 7.2]),
58+
"at_event": np.array([0, 2, 4, 6, 60, 62]),
59+
"frequency": np.array([1 / 6, 1 / 6, 1, 1, 1 / 30, 1 / 30]),
60+
"tot_value": 7,
61+
"aai_agg": 14.4,
62+
"unit": "USD",
63+
"frequency_unit": "1/month",
64+
"imp_mat": sparse.csr_matrix(
6665
np.array([[0, 0], [1, 1], [2, 2], [3, 3], [30, 30], [31, 31]])
6766
),
68-
haz_type="TC",
69-
)
67+
"haz_type": "TC",
68+
}
69+
70+
71+
def dummy_impact():
72+
"""Return an impact object for testing"""
73+
return Impact(**impact_kwargs())
7074

7175

7276
def dummy_impact_yearly():

climada/engine/test/test_impact_forecast.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,57 @@
1818
1919
Tests for Impact Forecast.
2020
"""
21+
22+
import numpy as np
23+
import numpy.testing as npt
24+
import pandas as pd
25+
import pytest
26+
from scipy.sparse import csr_matrix
27+
28+
from climada.engine import Impact, ImpactForecast
29+
30+
from .test_impact import impact_kwargs as imp_kwargs
31+
32+
33+
@pytest.fixture
34+
def impact_kwargs():
35+
return imp_kwargs()
36+
37+
38+
@pytest.fixture
39+
def impact(impact_kwargs):
40+
return Impact(**impact_kwargs)
41+
42+
43+
def assert_impact_kwargs(impact: Impact, **kwargs):
44+
for key, value in kwargs.items():
45+
attr = getattr(impact, key)
46+
if isinstance(value, (np.ndarray, list)):
47+
npt.assert_array_equal(attr, value)
48+
elif isinstance(value, csr_matrix):
49+
npt.assert_array_equal(attr.todense(), value.todense())
50+
else:
51+
assert attr == value
52+
53+
54+
class TestImpactForecastInit:
55+
lead_time = pd.date_range("2000-01-01", "2000-01-02", periods=6).to_numpy()
56+
member = np.arange(6)
57+
58+
def test_impact_forecast_init(self, impact_kwargs):
59+
forecast1 = ImpactForecast(
60+
lead_time=self.lead_time,
61+
member=self.member,
62+
**impact_kwargs,
63+
)
64+
npt.assert_array_equal(forecast1.lead_time, self.lead_time)
65+
npt.assert_array_equal(forecast1.member, self.member)
66+
assert_impact_kwargs(forecast1, **impact_kwargs)
67+
68+
def test_impact_forecast_from_impact(self, impact, impact_kwargs):
69+
forecast = ImpactForecast.from_impact(
70+
impact, lead_time=self.lead_time, member=self.member
71+
)
72+
npt.assert_array_equal(forecast.lead_time, self.lead_time)
73+
npt.assert_array_equal(forecast.member, self.member)
74+
assert_impact_kwargs(forecast, **impact_kwargs)

0 commit comments

Comments
 (0)