Skip to content

Commit 6e12608

Browse files
committed
improves docstring for calc strategies
1 parent 26bb85d commit 6e12608

File tree

2 files changed

+154
-52
lines changed

2 files changed

+154
-52
lines changed

climada/trajectories/impact_calc_strat.py

Lines changed: 153 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16,51 +16,119 @@
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

2323
import copy
2424
from abc import ABC, abstractmethod
25+
from typing import Any, Optional, Union
2526

2627
import numpy as np
28+
from scipy import sparse
2729

2830
from climada.engine.impact import Impact
2931
from climada.engine.impact_calc import ImpactCalc
3032
from climada.entity.exposures.base import Exposures
3133
from climada.entity.impact_funcs.impact_func_set import ImpactFuncSet
3234
from climada.hazard.base import Hazard
33-
from climada.trajectories.snapshot import Snapshot
3435

3536

3637
class 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

5290
class 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, \text{Total Impact} - \text{attachement}), \text{cover})$$
231+
3. **Residual Risk per Event**:
232+
$$\text{Total Impact} - \text{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
)

climada/trajectories/riskperiod.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ def impact_computation_strategy(self) -> ImpactComputationStrategy:
454454
@impact_computation_strategy.setter
455455
def impact_computation_strategy(self, value, /):
456456
if not isinstance(value, ImpactComputationStrategy):
457-
raise ValueError("Not an interpolation strategy")
457+
raise ValueError("Not an impact computation strategy")
458458

459459
self._impact_computation_strategy = value
460460
self._reset_impact_data()

0 commit comments

Comments
 (0)