2121
2222"""
2323
24- import copy
2524from abc import ABC , abstractmethod
26- from typing import Any , Optional , Union
27-
28- import numpy as np
29- from scipy import sparse
3025
3126from climada .engine .impact import Impact
3227from climada .engine .impact_calc import ImpactCalc
@@ -56,9 +51,6 @@ def compute_impacts(
5651 exp : Exposures ,
5752 haz : Hazard ,
5853 vul : ImpactFuncSet ,
59- risk_transf_attach : Optional [float ] = None ,
60- risk_transf_cover : Optional [float ] = None ,
61- calc_residual : bool = True ,
6254 ) -> Impact :
6355 """
6456 Calculates the total impact, including optional risk transfer application.
@@ -71,16 +63,6 @@ def compute_impacts(
7163 The hazard data (e.g., event intensity).
7264 vul : ImpactFuncSet
7365 The set of vulnerability functions.
74- risk_transf_attach : float, optional
75- The attachment point (deductible) for the global risk transfer mechanism.
76- If None, default to 0.
77- risk_transf_cover : float, optional
78- The cover limit for the risk transfer mechanism. If None, the cover
79- is assumed to be infinite (only the attachment applies).
80- calc_residual : bool, default=True
81- If True, the function returns the residual impact (after risk transfer).
82- If False, it returns the transferred impact (the part covered by the
83- risk transfer).
8466
8567 Returns
8668 -------
@@ -99,28 +81,7 @@ class ImpactCalcComputation(ImpactComputationStrategy):
9981 Default impact computation strategy using the core engine of climada.
10082
10183 This strategy first calculates the raw impact using the standard
102- :class:`ImpactCalc` logic and then applies a global risk transfer mechanism.
103-
104- This risk transfer is distinct and non-exclusive with the risk transfer
105- defined through the Exposures object. While in the Exposures case, the transfer
106- is defined on a per coordinate basis, this one is applied total impact on the whole region
107- considered, and proportionally rescales the impacts per coordinate accordingly.
108-
109- Notes
110- -----
111- The calculation is performed event-wise:
112-
113- 1. **Total Impact**: Calculate the total impact for each event
114- (sum of impacts across all exposure points).
115- 2. **Transferred Risk per Event**: Defined as:
116- $$\min(\max(0, \text{Total Impact} - \text{attachement}), \text{cover})$$
117- 3. **Residual Risk per Event**:
118- $$\text{Total Impact} - \text{Transferred Risk per Event}$$
119- 4. **Adjustment**: The original impact per exposure point is scaled
120- by the ratio of (Residual Risk / Total Impact) or
121- (Transferred Risk / Total Impact) for that event.
122- This ensures the risk transfer is shared proportionally among all
123- impacted exposure points.
84+ :class:`ImpactCalc` logic.
12485
12586 """
12687
@@ -129,9 +90,6 @@ def compute_impacts(
12990 exp : Exposures ,
13091 haz : Hazard ,
13192 vul : ImpactFuncSet ,
132- risk_transf_attach : Optional [float ] = None ,
133- risk_transf_cover : Optional [float ] = None ,
134- calc_residual : bool = False ,
13593 ) -> Impact :
13694 """
13795 Calculates the impact and applies the "global" risk transfer mechanism.
@@ -144,22 +102,13 @@ def compute_impacts(
144102 The hazard data.
145103 vul : ImpactFuncSet
146104 The set of vulnerability functions.
147- risk_transf_attach : float, optional
148- The attachment point (deductible) for the risk transfer mechanism.
149- risk_transf_cover : float, optional
150- The cover limit for the risk transfer mechanism.
151- calc_residual : bool, default=False
152- If True, returns the residual impact. If False, returns the transferred impact.
153105
154106 Returns
155107 -------
156108 Impact
157- The final impact object (either residual or transferred) .
109+ The final impact object.
158110 """
159111 impact = self .compute_impacts_pre_transfer (exp , haz , vul )
160- self ._apply_risk_transfer (
161- impact , risk_transf_attach , risk_transf_cover , calc_residual
162- )
163112 return impact
164113
165114 def compute_impacts_pre_transfer (
@@ -186,106 +135,3 @@ def compute_impacts_pre_transfer(
186135 An Impact object containing the raw, pre-transfer impact matrix.
187136 """
188137 return ImpactCalc (exposures = exp , impfset = vul , hazard = haz ).impact ()
189-
190- def _apply_risk_transfer (
191- self ,
192- impact : Impact ,
193- risk_transf_attach : Optional [float ],
194- risk_transf_cover : Optional [float ],
195- calc_residual : bool ,
196- ) -> None :
197- """
198- Applies risk transfer logic and modifies the Impact object in-place.
199-
200- Parameters
201- ----------
202- impact : Impact
203- The Impact object whose impact matrix will be modified.
204- risk_transf_attach : float, optional
205- The attachment point.
206- risk_transf_cover : float, optional
207- The cover limit.
208- calc_residual : bool
209- Determines whether to set the matrix to the residual or transferred impact.
210- """
211- if risk_transf_attach is not None or risk_transf_cover is not None :
212- impact .imp_mat = self .calculate_residual_or_risk_transfer_impact_matrix (
213- impact .imp_mat , risk_transf_attach , risk_transf_cover , calc_residual
214- )
215-
216- def calculate_residual_or_risk_transfer_impact_matrix (
217- self ,
218- imp_mat : sparse .csr_matrix ,
219- attachement : Optional [float ],
220- cover : Optional [float ],
221- calc_residual : bool ,
222- ) -> sparse .csr_matrix :
223- r"""
224- Calculates either the residual or the risk transfer impact matrix
225- based on a global risk transfer mechanism.
226-
227- This function modifies the original impact matrix values proportionally
228- based on the total event impact relative to the attachment and cover.
229-
230- Parameters
231- ----------
232- imp_mat : scipy.sparse.csr_matrix or object with .data
233- The original impact matrix (events x exposure points).
234- attachement : float, optional
235- The attachment point (deductible).
236- cover : float, optional
237- The cover limit.
238- calc_residual : bool
239- If True, the function returns the residual impact matrix.
240- If False, it returns the transferred risk impact matrix.
241-
242- Returns
243- -------
244- scipy.sparse.csr_matrix or object with .data
245- The adjusted impact matrix (residual or transferred).
246-
247- Notes
248- -----
249- The calculation is performed event-wise:
250-
251- 1. **Total Impact**: Calculate the total impact for each event
252- (sum of impacts across all exposure points).
253- 2. **Transferred Risk per Event**: Defined as:
254- $$\min(\max(0, \text{Total Impact} - \text{attachement}), \text{cover})$$
255- 3. **Residual Risk per Event**:
256- $$\text{Total Impact} - \text{Transferred Risk per Event}$$
257- 4. **Adjustment**: The original impact per exposure point is scaled
258- by the ratio of (Residual Risk / Total Impact) or
259- (Transferred Risk / Total Impact) for that event.
260- This ensures the risk transfer is shared proportionally among all
261- impacted exposure points.
262- """
263- imp_mat = copy .deepcopy (imp_mat )
264- # Calculate the total impact per event
265- total_at_event = imp_mat .sum (axis = 1 ).A1 # type: ignore
266- # Risk layer at event
267- attachement = 0 if attachement is None else attachement
268- cover = np .inf if cover is None else cover
269- transfer_at_event = np .minimum (
270- np .maximum (total_at_event - attachement , 0 ), cover
271- )
272- residual_at_event = np .maximum (total_at_event - transfer_at_event , 0 )
273-
274- # Calculate either the residual or transfer impact matrix
275- # Choose the denominator to rescale the impact values
276- if calc_residual :
277- numerator = residual_at_event
278- else :
279- numerator = transfer_at_event
280-
281- rescale_impact_values = np .divide (
282- numerator ,
283- total_at_event ,
284- out = np .zeros_like (numerator , dtype = float ),
285- where = total_at_event != 0 ,
286- )
287-
288- # The multiplication is broadcasted across the columns for each row
289- result_matrix = imp_mat .multiply (rescale_impact_values [:, np .newaxis ])
290-
291- return result_matrix
0 commit comments