Skip to content

Commit da997e7

Browse files
committed
Cherrypick from risk traj
1 parent 6d3c41a commit da997e7

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
"""
2+
This file is part of CLIMADA.
3+
4+
Copyright (C) 2017 ETH Zurich, CLIMADA contributors listed in AUTHORS.
5+
6+
CLIMADA is free software: you can redistribute it and/or modify it under the
7+
terms of the GNU General Public License as published by the Free
8+
Software Foundation, version 3.
9+
10+
CLIMADA is distributed in the hope that it will be useful, but WITHOUT ANY
11+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12+
PARTICULAR PURPOSE. See the GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License along
15+
with CLIMADA. If not, see <https://www.gnu.org/licenses/>.
16+
17+
---
18+
19+
This modules implements the impact computation strategy objects for risk
20+
trajectories.
21+
22+
"""
23+
24+
from abc import ABC, abstractmethod
25+
26+
from climada.engine.impact import Impact
27+
from climada.engine.impact_calc import ImpactCalc
28+
from climada.entity.exposures.base import Exposures
29+
from climada.entity.impact_funcs.impact_func_set import ImpactFuncSet
30+
from climada.hazard.base import Hazard
31+
32+
__all__ = ["ImpactCalcComputation"]
33+
34+
35+
class ImpactComputationStrategy(ABC):
36+
"""
37+
Interface for impact computation strategies.
38+
39+
This abstract class defines the contract for all concrete strategies
40+
responsible for calculating and optionally modifying with a risk transfer,
41+
the impact computation, based on a set of inputs (exposure, hazard, vulnerability).
42+
43+
It revolves around a `compute_impacts()` method that takes as arguments
44+
the three dimensions of risk (exposure, hazard, vulnerability) and return an
45+
Impact object.
46+
"""
47+
48+
@abstractmethod
49+
def compute_impacts(
50+
self,
51+
exp: Exposures,
52+
haz: Hazard,
53+
vul: ImpactFuncSet,
54+
) -> Impact:
55+
"""
56+
Calculates the total impact, including optional risk transfer application.
57+
58+
Parameters
59+
----------
60+
exp : Exposures
61+
The exposure data.
62+
haz : Hazard
63+
The hazard data (e.g., event intensity).
64+
vul : ImpactFuncSet
65+
The set of vulnerability functions.
66+
67+
Returns
68+
-------
69+
Impact
70+
An object containing the computed total impact matrix and metrics.
71+
72+
See Also
73+
--------
74+
ImpactCalcComputation : The default implementation of this interface.
75+
"""
76+
...
77+
78+
79+
class ImpactCalcComputation(ImpactComputationStrategy):
80+
r"""
81+
Default impact computation strategy using the core engine of climada.
82+
83+
This strategy first calculates the raw impact using the standard
84+
:class:`ImpactCalc` logic.
85+
86+
"""
87+
88+
def compute_impacts(
89+
self,
90+
exp: Exposures,
91+
haz: Hazard,
92+
vul: ImpactFuncSet,
93+
) -> Impact:
94+
"""
95+
Calculates the impact and applies the "global" risk transfer mechanism.
96+
97+
Parameters
98+
----------
99+
exp : Exposures
100+
The exposure data.
101+
haz : Hazard
102+
The hazard data.
103+
vul : ImpactFuncSet
104+
The set of vulnerability functions.
105+
106+
Returns
107+
-------
108+
Impact
109+
The final impact object.
110+
"""
111+
impact = self.compute_impacts_pre_transfer(exp, haz, vul)
112+
return impact
113+
114+
def compute_impacts_pre_transfer(
115+
self,
116+
exp: Exposures,
117+
haz: Hazard,
118+
vul: ImpactFuncSet,
119+
) -> Impact:
120+
"""
121+
Calculates the raw impact matrix before any risk transfer is applied.
122+
123+
Parameters
124+
----------
125+
exp : Exposures
126+
The exposure data.
127+
haz : Hazard
128+
The hazard data.
129+
vul : ImpactFuncSet
130+
The set of vulnerability functions.
131+
132+
Returns
133+
-------
134+
Impact
135+
An Impact object containing the raw, pre-transfer impact matrix.
136+
"""
137+
return ImpactCalc(exposures=exp, impfset=vul, hazard=haz).impact()
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
"""
2+
This file is part of CLIMADA.
3+
4+
Copyright (C) 2017 ETH Zurich, CLIMADA contributors listed in AUTHORS.
5+
6+
CLIMADA is free software: you can redistribute it and/or modify it under the
7+
terms of the GNU General Public License as published by the Free
8+
Software Foundation, version 3.
9+
10+
CLIMADA is distributed in the hope that it will be useful, but WITHOUT ANY
11+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
12+
PARTICULAR PURPOSE. See the GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License along
15+
with CLIMADA. If not, see <https://www.gnu.org/licenses/>.
16+
17+
---
18+
19+
Tests for impact_calc_strat
20+
21+
"""
22+
23+
import unittest
24+
from unittest.mock import MagicMock, patch
25+
26+
from climada.engine import Impact
27+
from climada.entity import ImpactFuncSet
28+
from climada.entity.exposures import Exposures
29+
from climada.hazard import Hazard
30+
from climada.trajectories import Snapshot
31+
from climada.trajectories.impact_calc_strat import ImpactCalcComputation
32+
33+
34+
class TestImpactCalcComputation(unittest.TestCase):
35+
def setUp(self):
36+
self.mock_snapshot0 = MagicMock(spec=Snapshot)
37+
self.mock_snapshot0.exposure = MagicMock(spec=Exposures)
38+
self.mock_snapshot0.hazard = MagicMock(spec=Hazard)
39+
self.mock_snapshot0.impfset = MagicMock(spec=ImpactFuncSet)
40+
self.mock_snapshot1 = MagicMock(spec=Snapshot)
41+
self.mock_snapshot1.exposure = MagicMock(spec=Exposures)
42+
self.mock_snapshot1.hazard = MagicMock(spec=Hazard)
43+
self.mock_snapshot1.impfset = MagicMock(spec=ImpactFuncSet)
44+
45+
self.impact_calc_computation = ImpactCalcComputation()
46+
47+
@patch.object(ImpactCalcComputation, "compute_impacts_pre_transfer")
48+
def test_compute_impacts(self, mock_calculate_impacts_for_snapshots):
49+
mock_impacts = MagicMock(spec=Impact)
50+
mock_calculate_impacts_for_snapshots.return_value = mock_impacts
51+
52+
result = self.impact_calc_computation.compute_impacts(
53+
exp=self.mock_snapshot0.exposure,
54+
haz=self.mock_snapshot0.hazard,
55+
vul=self.mock_snapshot0.impfset,
56+
)
57+
58+
self.assertEqual(result, mock_impacts)
59+
mock_calculate_impacts_for_snapshots.assert_called_once_with(
60+
self.mock_snapshot0.exposure,
61+
self.mock_snapshot0.hazard,
62+
self.mock_snapshot0.impfset,
63+
)
64+
65+
def test_calculate_impacts_for_snapshots(self):
66+
mock_imp_E0H0 = MagicMock(spec=Impact)
67+
68+
with patch(
69+
"climada.trajectories.impact_calc_strat.ImpactCalc"
70+
) as mock_impact_calc:
71+
mock_impact_calc.return_value.impact.side_effect = [mock_imp_E0H0]
72+
73+
result = self.impact_calc_computation.compute_impacts_pre_transfer(
74+
exp=self.mock_snapshot0.exposure,
75+
haz=self.mock_snapshot0.hazard,
76+
vul=self.mock_snapshot0.impfset,
77+
)
78+
79+
self.assertEqual(result, mock_imp_E0H0)
80+
81+
82+
if __name__ == "__main__":
83+
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestImpactCalcComputation)
84+
unittest.TextTestRunner(verbosity=2).run(TESTS)

0 commit comments

Comments
 (0)