Skip to content
Closed
Changes from all commits
Commits
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
84 changes: 77 additions & 7 deletions climada/engine/impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,76 @@ def _cen_return_imp(imp, freq, imp_th, return_periods):

return imp_fit

def calc_imp_from_mat(self, imp_mat, freq):
"""
Set Impact attributes from the impact matrix. Returns a copy.
Overwrites eai_exp, at_event, aai_agg, imp_mat.

Parameters
----------
imp_mat : sparse.csr_matrix
matrix num_events x num_exp with impacts.
freq : np.array
array with the frequency per event
Returns
-------
imp : Impact
Copy of impact with eai_exp, at_event, aai_agg, imp_mat set.
"""
eai_exp = self._eai_exp_from_mat(imp_mat, freq)
at_event = self._at_event_from_mat(imp_mat)
aai_agg = self._aai_agg_from_at_event(at_event, freq)
return eai_exp, at_event, aai_agg

def _eai_exp_from_mat(self, imp_mat, freq):
"""
Compute impact for each exposures from the total impact matrix

Parameters
----------
imp_mat : sparse.csr_matrix
matrix num_events x num_exp with impacts.
frequency : np.array
annual frequency of events
Returns
-------
eai_exp : np.array
expected annual impact for each exposure
"""
freq_mat = freq.reshape(len(freq), 1)
return imp_mat.multiply(freq_mat).sum(axis=0).A1

def _at_event_from_mat(self, imp_mat):
"""
Compute impact for each hazard event from the total impact matrix

Parameters
----------
imp_mat : sparse.csr_matrix
matrix num_events x num_exp with impacts.
Returns
-------
at_event : np.array
impact for each hazard event
"""
return np.squeeze(np.asarray(np.sum(imp_mat, axis=1)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So currently the at_event is calculated from an impact variable (line 1033) which considers potential deductibles, covers etc. The imp_mat does not previously account for those, right? Aka the at_event would have to be modified afterwards, if such conditions exist?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(similary for eai_exp, etc.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is then to be added in this method, good point! My issue with the current implementation is that the at_event and eai_exp are computed in a very obscure manner inside of the .calc method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best would probably be to have a class method calc_impact and calc_residual_impact (if with insurance stuff).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I am not mistaken, the deductible all act on the impact matrix as implemented right now? Thus, the imp_mat is actually not the risk matrix, but the residual risk matrix after insurance deductibles have been applied.

My suggestion would thus be to disentangle that as it is unclear at the moment.


def _aai_agg_from_at_event(self, at_event, freq):
"""
Aggregate impact.at_event

Parameters
----------
at_event : np.array
impact for each hazard event
frequency : np.array
annual frequency of event
Returns
-------
float
average annual impact aggregated
"""
return sum(at_event * freq)

def select(self,
event_ids=None, event_names=None, dates=None,
Expand Down Expand Up @@ -1226,13 +1296,6 @@ def select(self,

return imp

def _selected_exposures_idx(self, coord_exp):
assigned_idx = u_coord.assign_coordinates(self.coord_exp, coord_exp, threshold=0)
sel_exp = (assigned_idx >= 0).nonzero()[0]
if sel_exp.size == 0:
LOGGER.warning("No exposure coordinates match the selection.")
return sel_exp

def _selected_events_idx(self, event_ids, event_names, dates, nb_events):
if all(var is None for var in [dates, event_ids, event_names]):
return None
Expand Down Expand Up @@ -1278,6 +1341,13 @@ def _selected_events_idx(self, event_ids, event_names, dates, nb_events):

return sel_ev

def _selected_exposures_idx(self, coord_exp):
assigned_idx = u_coord.assign_coordinates(self.coord_exp, coord_exp, threshold=0)
sel_exp = (assigned_idx >= 0).nonzero()[0]
if sel_exp.size == 0:
LOGGER.warning("No exposure coordinates match the selection.")
return sel_exp

class ImpactFreqCurve():
"""Impact exceedence frequency curve.

Expand Down