1616
1717---
1818
19- This modules implements the Snapshot and SnapshotsCollection classes .
19+ This modules implements the impact computation strategy objects for trajectories .
2020
2121"""
2222
2323import copy
2424from abc import ABC , abstractmethod
25+ from typing import Any , Optional , Union
2526
2627import numpy as np
28+ from scipy import sparse
2729
2830from climada .engine .impact import Impact
2931from climada .engine .impact_calc import ImpactCalc
3032from climada .entity .exposures .base import Exposures
3133from climada .entity .impact_funcs .impact_func_set import ImpactFuncSet
3234from climada .hazard .base import Hazard
33- from climada .trajectories .snapshot import Snapshot
3435
3536
3637class ImpactComputationStrategy (ABC ):
37- """Interface for impact computation strategies."""
38+ """
39+ Interface for impact computation strategies.
40+
41+ This abstract class defines the contract for all concrete strategies
42+ responsible for calculating and optionally modifying the total impact
43+ based on a set of inputs (exposure, hazard, vulnerability).
44+ """
3845
3946 @abstractmethod
4047 def compute_impacts (
4148 self ,
4249 exp : Exposures ,
4350 haz : Hazard ,
4451 vul : ImpactFuncSet ,
45- risk_transf_attach : float | None = None ,
46- risk_transf_cover : float | None = None ,
52+ risk_transf_attach : Optional [ float ] = None ,
53+ risk_transf_cover : Optional [ float ] = None ,
4754 calc_residual : bool = True ,
4855 ) -> Impact :
49- """Method used to compute impact from the snapshots."""
56+ """
57+ Calculates the total impact, including optional risk transfer application.
58+
59+ Parameters
60+ ----------
61+ exp : Exposures
62+ The exposure data.
63+ haz : Hazard
64+ The hazard data (e.g., event intensity).
65+ vul : ImpactFuncSet
66+ The set of vulnerability functions.
67+ risk_transf_attach : float, optional
68+ The attachment point (deductible) for the global risk transfer mechanism.
69+ If None, default to 0.
70+ risk_transf_cover : float, optional
71+ The cover limit for the risk transfer mechanism. If None, the cover
72+ is assumed to be infinite (only the attachment applies).
73+ calc_residual : bool, default=True
74+ If True, the function returns the residual impact (after risk transfer).
75+ If False, it returns the transferred impact (the part covered by the
76+ risk transfer).
77+
78+ Returns
79+ -------
80+ Impact
81+ An object containing the computed total impact matrix and metrics.
82+
83+ See Also
84+ --------
85+ ImpactCalcComputation : The default implementation of this interface.
86+ """
87+ pass
5088
5189
5290class ImpactCalcComputation (ImpactComputationStrategy ):
53- """Default impact computation strategy."""
91+ """
92+ Default impact computation strategy.
93+
94+ This strategy first calculates the raw impact using the standard
95+ :class:`ImpactCalc` logic and then applies a global risk transfer mechanism.
96+ """
5497
5598 def compute_impacts (
5699 self ,
57100 exp : Exposures ,
58101 haz : Hazard ,
59102 vul : ImpactFuncSet ,
60- risk_transf_attach : float | None = None ,
61- risk_transf_cover : float | None = None ,
103+ risk_transf_attach : Optional [ float ] = None ,
104+ risk_transf_cover : Optional [ float ] = None ,
62105 calc_residual : bool = False ,
63- ):
106+ ) -> Impact :
107+ """
108+ Calculates the impact and applies the risk transfer mechanism.
109+
110+ This overrides the abstract method to implement the default strategy.
111+
112+ Parameters
113+ ----------
114+ exp : Exposures
115+ The exposure data.
116+ haz : Hazard
117+ The hazard data.
118+ vul : ImpactFuncSet
119+ The set of vulnerability functions.
120+ risk_transf_attach : float, optional
121+ The attachment point (deductible) for the risk transfer mechanism.
122+ risk_transf_cover : float, optional
123+ The cover limit for the risk transfer mechanism.
124+ calc_residual : bool, default=False
125+ If True, returns the residual impact. If False, returns the transferred impact.
126+
127+ Returns
128+ -------
129+ Impact
130+ The final impact object (either residual or transferred).
131+ """
64132 impact = self .compute_impacts_pre_transfer (exp , haz , vul )
65133 self ._apply_risk_transfer (
66134 impact , risk_transf_attach , risk_transf_cover , calc_residual
@@ -73,73 +141,107 @@ def compute_impacts_pre_transfer(
73141 haz : Hazard ,
74142 vul : ImpactFuncSet ,
75143 ) -> Impact :
144+ """
145+ Calculates the raw impact matrix before any risk transfer is applied.
146+
147+ Parameters
148+ ----------
149+ exp : Exposures
150+ The exposure data.
151+ haz : Hazard
152+ The hazard data.
153+ vul : ImpactFuncSet
154+ The set of vulnerability functions.
155+
156+ Returns
157+ -------
158+ Impact
159+ An Impact object containing the raw, pre-transfer impact matrix.
160+ """
76161 return ImpactCalc (exposures = exp , impfset = vul , hazard = haz ).impact ()
77162
78163 def _apply_risk_transfer (
79164 self ,
80165 impact : Impact ,
81- risk_transf_attach : float | None ,
82- risk_transf_cover : float | None ,
166+ risk_transf_attach : Optional [ float ] ,
167+ risk_transf_cover : Optional [ float ] ,
83168 calc_residual : bool ,
84- ):
85- """Apply risk transfer to the calculated impacts."""
169+ ) -> None :
170+ """
171+ Applies risk transfer logic and modifies the Impact object in-place.
172+
173+ Parameters
174+ ----------
175+ impact : Impact
176+ The Impact object whose impact matrix will be modified.
177+ risk_transf_attach : float, optional
178+ The attachment point.
179+ risk_transf_cover : float, optional
180+ The cover limit.
181+ calc_residual : bool
182+ Determines whether to set the matrix to the residual or transferred impact.
183+ """
86184 if risk_transf_attach is not None or risk_transf_cover is not None :
185+ # Assuming impact.imp_mat is a sparse matrix, which is a common pattern
87186 impact .imp_mat = self .calculate_residual_or_risk_transfer_impact_matrix (
88187 impact .imp_mat , risk_transf_attach , risk_transf_cover , calc_residual
89188 )
90189
91190 def calculate_residual_or_risk_transfer_impact_matrix (
92- self , imp_mat , attachement , cover , calc_residual
93- ):
94- """Calculate either the residual or the risk transfer impact matrix
95- from a global risk transfer mechanism.
96-
97- To compute the transfered risk, the function first computes for each event,
98- the (positive) difference between their total impact and `attachment`.
99- The transfered risk for each event is then defined as the minimum between
100- this value and `cover`. The residual impact is the total impact minus
101- the transfered risk.
102- The impact matrix is then adjusted by multiply impact per centroids
103- by the ratio between residual risk and total impact for each event.
104- As such, the risk transfer is shared among all impacted exposure points
105- and equaly distributed.
106-
107- If `calc_residual` is False, the function returns the transfered risk
108- at each points instead of the residual risk by using the ratio between
109- transfered risk and total impact instead.
191+ self ,
192+ imp_mat : Union [
193+ sparse .csr_matrix , Any
194+ ], # Use Any if sparse.csr_matrix is too restrictive
195+ attachement : Optional [float ],
196+ cover : Optional [float ],
197+ calc_residual : bool ,
198+ ) -> Union [sparse .csr_matrix , Any ]:
199+ """
200+ Calculates either the residual or the risk transfer impact matrix
201+ based on a global risk transfer mechanism.
202+
203+ This function modifies the original impact matrix values proportionally
204+ based on the total event impact relative to the attachment and cover.
110205
111206 Parameters
112207 ----------
113- imp_mat : scipy.sparse.csr_matrix
114- The original impact matrix to be scaled .
115- attachment : float
116- The attachment point for the risk layer .
117- cover : float
118- The maximum coverage for the risk layer .
119- calc_residual : bool, default=True
120- Determines if the function calculates the residual (if True) or the
121- risk layer (if False) .
208+ imp_mat : scipy.sparse.csr_matrix or object with .data
209+ The original impact matrix (events x exposure points) .
210+ attachement : float, optional
211+ The attachment point (deductible) .
212+ cover : float, optional
213+ The cover limit .
214+ calc_residual : bool
215+ If True, the function returns the residual impact matrix.
216+ If False, it returns the transferred risk impact matrix .
122217
123218 Returns
124219 -------
125- scipy.sparse.csr_matrix
126- The adjusted impact matrix, either residual or risk transfer.
127-
128- Warnings
129- --------
220+ scipy.sparse.csr_matrix or object with .data
221+ The adjusted impact matrix (residual or transferred).
130222
131- This transfer capability is different and not exclusive with the one
132- implemented in ImpactCalc, which is defined at the centroid level.
133- The mechanism here corresponds to a global cover applied to the whole
134- region studied.
223+ Notes
224+ -----
225+ The calculation is performed event-wise:
135226
227+ 1. **Total Impact**: Calculate the total impact for each event
228+ (sum of impacts across all exposure points).
229+ 2. **Transferred Risk per Event**: Defined as:
230+ $$\min(\max(0, \t ext{Total Impact} - \t ext{attachement}), \t ext{cover})$$
231+ 3. **Residual Risk per Event**:
232+ $$\t ext{Total Impact} - \t ext{Transferred Risk per Event}$$
233+ 4. **Adjustment**: The original impact per exposure point is scaled
234+ by the ratio of (Residual Risk / Total Impact) or
235+ (Transferred Risk / Total Impact) for that event.
236+ This ensures the risk transfer is shared proportionally among all
237+ impacted exposure points.
136238 """
137239 imp_mat = copy .deepcopy (imp_mat )
138240 # Calculate the total impact per event
139241 total_at_event = imp_mat .sum (axis = 1 ).A1
140242 # Risk layer at event
141243 attachement = 0 if attachement is None else attachement
142- cover = total_at_event if cover is None else cover
244+ cover = np . inf if cover is None else cover
143245 transfer_at_event = np .minimum (
144246 np .maximum (total_at_event - attachement , 0 ), cover
145247 )
0 commit comments