Skip to content

Commit 6a156ac

Browse files
luseverinChahan Kropfpeanutfun
authored
Impact calc return impact forecast (#1179)
* Return impactForecast object in _return_impact * Return impactForecast object in _return_empty * Add full impactcalc test for impactForecast * Raise value error when computing impact with impact forecast without saving impact matrix * Cosmetics: Improve error message, move test to own class * Write nans for eai_exp and aai_agg when forecast is used --------- Co-authored-by: luseverin <[email protected]> Co-authored-by: Chahan Kropf <[email protected]> Co-authored-by: Lukas Riedel <[email protected]>
1 parent 64da484 commit 6a156ac

File tree

4 files changed

+279
-18
lines changed

4 files changed

+279
-18
lines changed

climada/engine/impact_calc.py

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
from climada import CONFIG
3131
from climada.engine.impact import Impact
32+
from climada.engine.impact_forecast import ImpactForecast
33+
from climada.hazard.forecast import HazardForecast
3234

3335
LOGGER = logging.getLogger(__name__)
3436

@@ -217,7 +219,7 @@ def _return_impact(self, imp_mat_gen, save_mat):
217219
218220
Returns
219221
-------
220-
Impact
222+
Impact or ImpactForecast
221223
Impact Object initialize from the impact matrix
222224
223225
See Also
@@ -230,12 +232,31 @@ def _return_impact(self, imp_mat_gen, save_mat):
230232
at_event, eai_exp, aai_agg = self.risk_metrics(
231233
imp_mat, self.hazard.frequency
232234
)
235+
if isinstance(self.hazard, HazardForecast):
236+
eai_exp = np.full_like(eai_exp, np.nan, dtype=eai_exp.dtype)
237+
aai_agg = np.full_like(aai_agg, np.nan, dtype=aai_agg.dtype)
238+
LOGGER.warning(
239+
"eai_exp and aai_agg are undefined with forecasts. "
240+
"Setting them to NaN arrays."
241+
)
242+
233243
else:
244+
if isinstance(self.hazard, HazardForecast):
245+
raise ValueError(
246+
"Saving impact matrix is required when using HazardForecast."
247+
"Please set save_mat=True."
248+
)
234249
imp_mat = None
235250
at_event, eai_exp, aai_agg = self.stitch_risk_metrics(imp_mat_gen)
236-
return Impact.from_eih(
251+
252+
impact = Impact.from_eih(
237253
self.exposures, self.hazard, at_event, eai_exp, aai_agg, imp_mat
238254
)
255+
if isinstance(self.hazard, HazardForecast):
256+
return ImpactForecast.from_impact(
257+
impact, self.hazard.lead_time, self.hazard.member
258+
)
259+
return impact
239260

240261
def _return_empty(self, save_mat):
241262
"""
@@ -248,21 +269,37 @@ def _return_empty(self, save_mat):
248269
249270
Returns
250271
-------
251-
Impact
272+
Impact or ImpactForecast
252273
Empty impact object with correct array sizes.
253274
"""
254275
at_event = np.zeros(self.n_events)
255-
eai_exp = np.zeros(self.n_exp_pnt)
256-
aai_agg = 0.0
276+
if isinstance(self.hazard, HazardForecast):
277+
eai_exp = np.full(self.n_exp_pnt, np.nan)
278+
aai_agg = np.nan
279+
else:
280+
eai_exp = np.zeros(self.n_exp_pnt)
281+
aai_agg = 0.0
282+
257283
if save_mat:
258284
imp_mat = sparse.csr_matrix(
259285
(self.n_events, self.n_exp_pnt), dtype=np.float64
260286
)
261287
else:
288+
if isinstance(self.hazard, HazardForecast):
289+
raise ValueError(
290+
"Saving impact matrix is required when using HazardForecast. "
291+
"Please set save_mat=True."
292+
)
262293
imp_mat = None
263-
return Impact.from_eih(
294+
295+
impact = Impact.from_eih(
264296
self.exposures, self.hazard, at_event, eai_exp, aai_agg, imp_mat
265297
)
298+
if isinstance(self.hazard, HazardForecast):
299+
return ImpactForecast.from_impact(
300+
impact, self.hazard.lead_time, self.hazard.member
301+
)
302+
return impact
266303

267304
def minimal_exp_gdf(
268305
self, impf_col, assign_centroids, ignore_cover, ignore_deductible

climada/engine/impact_forecast.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,88 @@ def from_impact(
9090
haz_type=impact.haz_type,
9191
)
9292

93+
@property
94+
def at_event(self):
95+
"""Get the total impact for each member/lead_time combination."""
96+
LOGGER.warning(
97+
"at_event gives the total impact for one specific combination of member and "
98+
"lead_time."
99+
)
100+
return self._at_event
101+
102+
@at_event.setter
103+
def at_event(self, value):
104+
"""Set the total impact for each member/lead_time combination."""
105+
self._at_event = value
106+
107+
def local_exceedance_impact(
108+
self,
109+
return_periods=(25, 50, 100, 250),
110+
method="interpolate",
111+
min_impact=0,
112+
log_frequency=True,
113+
log_impact=True,
114+
bin_decimals=None,
115+
):
116+
"""Compution of local exceedance impact for given return periods is not
117+
implemented for ImpactForecast.
118+
119+
See Also
120+
--------
121+
See :py:meth:`~climada.engine.impact.Impact.local_exceedance_impact`
122+
123+
Raises
124+
------
125+
NotImplementedError
126+
"""
127+
128+
LOGGER.error("local_exceedance_impact is not defined for ImpactForecast")
129+
raise NotImplementedError(
130+
"local_exceedance_impact is not defined for ImpactForecast"
131+
)
132+
133+
def local_return_period(
134+
self,
135+
threshold_impact=(1000.0, 10000.0),
136+
method="interpolate",
137+
min_impact=0,
138+
log_frequency=True,
139+
log_impact=True,
140+
bin_decimals=None,
141+
):
142+
"""Compution of local return period for given impact thresholds is not
143+
implemented for ImpactForecast.
144+
145+
See Also
146+
--------
147+
See :py:meth:`~climada.engine.impact.Impact.local_return_period`
148+
149+
Raises
150+
-------
151+
NotImplementedError
152+
"""
153+
154+
LOGGER.error("local_return_period is not defined for ImpactForecast")
155+
raise NotImplementedError(
156+
"local_return_period is not defined for ImpactForecast"
157+
)
158+
159+
def calc_freq_curve(self, return_per=None):
160+
"""Computation of the impact exceedance frequency curve is not
161+
implemented for ImpactForecast.
162+
163+
See Also
164+
--------
165+
See :py:meth:`~climada.engine.impact.Impact.calc_freq_curve`
166+
167+
Raises
168+
------
169+
NotImplementedError
170+
"""
171+
172+
LOGGER.error("calc_freq_curve is not defined for ImpactForecast")
173+
raise NotImplementedError("calc_freq_curve is not defined for ImpactForecast")
174+
93175
def _check_sizes(self):
94176
"""Check sizes of forecast data vs. impact data.
95177

0 commit comments

Comments
 (0)