Skip to content

Commit 5a802a2

Browse files
authored
Merge branch 'forecast-class' into add_io_hdf5_imp_forecast
2 parents a3eb399 + bb99794 commit 5a802a2

File tree

6 files changed

+658
-107
lines changed

6 files changed

+658
-107
lines changed

climada/engine/impact.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2226,9 +2226,12 @@ def stack_attribute(attr_name: str) -> np.ndarray:
22262226
imp_mat = sparse.vstack(imp_mats)
22272227

22282228
# Concatenate other attributes
2229-
kwargs = {
2230-
attr: stack_attribute(attr) for attr in ("date", "frequency", "at_event")
2231-
}
2229+
concat_attrs = {
2230+
name.lstrip("_") # Private attributes with getter/setter
2231+
for name, value in first_imp.__dict__.items()
2232+
if isinstance(value, np.ndarray)
2233+
}.difference(("event_id", "coord_exp", "eai_exp", "aai_agg"))
2234+
kwargs = {attr: stack_attribute(attr) for attr in concat_attrs}
22322235

22332236
# Get remaining attributes from first impact object in list
22342237
return cls(

climada/engine/impact_forecast.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from typing import Union
2525

2626
import numpy as np
27+
import scipy.sparse as sparse
2728

2829
from ..util import log_level
2930
from ..util.checker import size
@@ -242,3 +243,172 @@ def _check_sizes(self):
242243
num_entries = len(self.event_id)
243244
size(exp_len=num_entries, var=self.member, var_name="Forecast.member")
244245
size(exp_len=num_entries, var=self.lead_time, var_name="Forecast.lead_time")
246+
247+
def _reduce_attrs(self, event_name: str):
248+
"""
249+
Reduce the attributes of an ImpactForecast to a single value.
250+
251+
Attributes are modified as follows:
252+
- lead_time: set to NaT
253+
- member: set to -1
254+
- event_id: set to 0
255+
- event_name: set to the name of the reduction method (default)
256+
- date: set to 0
257+
- frequency: set to 1
258+
259+
Parameters
260+
----------
261+
event_name : str
262+
The event name given to the reduced data.
263+
"""
264+
reduced_attrs = {
265+
"lead_time": np.array([np.timedelta64("NaT")]),
266+
"member": np.array([-1]),
267+
"event_id": np.array([0]),
268+
"event_name": np.array([event_name]),
269+
"date": np.array([0]),
270+
"frequency": np.array([1]),
271+
}
272+
273+
return reduced_attrs
274+
275+
def min(self):
276+
"""
277+
Reduce the impact matrix and at_event of an ImpactForecast to the minimum
278+
value.
279+
280+
Parameters
281+
----------
282+
None
283+
284+
Returns
285+
-------
286+
ImpactForecast
287+
An ImpactForecast object with the min impact matrix and at_event.
288+
"""
289+
red_imp_mat = self.imp_mat.min(axis=0).tocsr()
290+
red_at_event = np.array([red_imp_mat.sum()])
291+
return ImpactForecast(
292+
frequency_unit=self.frequency_unit,
293+
coord_exp=self.coord_exp,
294+
crs=self.crs,
295+
eai_exp=self.eai_exp,
296+
at_event=red_at_event,
297+
tot_value=self.tot_value,
298+
aai_agg=self.aai_agg,
299+
unit=self.unit,
300+
imp_mat=red_imp_mat,
301+
haz_type=self.haz_type,
302+
**self._reduce_attrs("min"),
303+
)
304+
305+
def max(self):
306+
"""
307+
Reduce the impact matrix and at_event of an ImpactForecast to the maximum
308+
value.
309+
310+
Parameters
311+
----------
312+
None
313+
314+
Returns
315+
-------
316+
ImpactForecast
317+
An ImpactForecast object with the max impact matrix and at_event.
318+
"""
319+
red_imp_mat = self.imp_mat.max(axis=0).tocsr()
320+
red_at_event = np.array([red_imp_mat.sum()])
321+
return ImpactForecast(
322+
frequency_unit=self.frequency_unit,
323+
coord_exp=self.coord_exp,
324+
crs=self.crs,
325+
eai_exp=self.eai_exp,
326+
at_event=red_at_event,
327+
tot_value=self.tot_value,
328+
aai_agg=self.aai_agg,
329+
unit=self.unit,
330+
imp_mat=red_imp_mat,
331+
haz_type=self.haz_type,
332+
**self._reduce_attrs("max"),
333+
)
334+
335+
def mean(self):
336+
"""
337+
Reduce the impact matrix and at_event of an ImpactForecast to the mean value.
338+
339+
Parameters
340+
----------
341+
None
342+
343+
Returns
344+
-------
345+
ImpactForecast
346+
An ImpactForecast object with the mean impact matrix and at_event.
347+
"""
348+
red_imp_mat = sparse.csr_matrix(self.imp_mat.mean(axis=0))
349+
red_at_event = np.array([red_imp_mat.sum()])
350+
return ImpactForecast(
351+
frequency_unit=self.frequency_unit,
352+
coord_exp=self.coord_exp,
353+
crs=self.crs,
354+
eai_exp=self.eai_exp,
355+
at_event=red_at_event,
356+
tot_value=self.tot_value,
357+
aai_agg=self.aai_agg,
358+
unit=self.unit,
359+
imp_mat=red_imp_mat,
360+
haz_type=self.haz_type,
361+
**self._reduce_attrs("mean"),
362+
)
363+
364+
def select(
365+
self,
366+
event_ids=None,
367+
event_names=None,
368+
dates=None,
369+
coord_exp=None,
370+
reset_frequency=False,
371+
member=None,
372+
lead_time=None,
373+
):
374+
"""Select entries based on the parameters and return a new instance.
375+
The selection will contain the intersection of all given parameters.
376+
377+
Parameters
378+
----------
379+
member : Sequence of ints
380+
Ensemble members to select
381+
lead_time : Sequence of numpy.timedelta64
382+
Lead times to select
383+
384+
See Also
385+
--------
386+
:py:meth:`~climada.engine.impact.Impact.select`
387+
"""
388+
if member is not None or lead_time is not None:
389+
mask_member = (
390+
self.idx_member(member)
391+
if member is not None
392+
else np.full_like(self.member, True, dtype=bool)
393+
)
394+
mask_lead_time = (
395+
self.idx_lead_time(lead_time)
396+
if lead_time is not None
397+
else np.full_like(self.lead_time, True, dtype=bool)
398+
)
399+
event_id_from_forecast_mask = np.asarray(self.event_id)[
400+
(mask_member & mask_lead_time)
401+
]
402+
event_ids = (
403+
np.intersect1d(event_ids, event_id_from_forecast_mask)
404+
if event_ids is not None
405+
else event_id_from_forecast_mask
406+
)
407+
408+
return super().select(
409+
event_ids=event_ids,
410+
event_names=event_names,
411+
dates=dates,
412+
coord_exp=coord_exp,
413+
reset_frequency=reset_frequency,
414+
)

0 commit comments

Comments
 (0)