Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/haz type in step impf #675

Merged
merged 10 commits into from
Mar 29, 2023
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Removed:
[#602](https://github.com/CLIMADA-project/climada_python/pull/602)
- Modified the method to disaggregate lines in the `lines_polys_handler` utility module in order to better conserve the total length of all lines on average [#679](https://github.com/CLIMADA-project/climada_python/pull/679).
- Added test for non-default impact function id in the `lines_polys_handler` [#676](https://github.com/CLIMADA-project/climada_python/pull/676)
- The sigmoid and step impact functions now require the user to define the hazard type. [#675](https://github.com/CLIMADA-project/climada_python/pull/675)

### Fixed

Expand Down
39 changes: 32 additions & 7 deletions climada/entity/impact_funcs/base.py
leonie-villiger marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def __init__(
self.mdd = mdd if mdd is not None else np.array([])
self.paa = paa if paa is not None else np.array([])

def calc_mdr(self, inten):
def calc_mdr(self, inten: Union[float, np.ndarray]) -> np.ndarray:
leonie-villiger marked this conversation as resolved.
Show resolved Hide resolved
"""Interpolate impact function to a given intensity.

Parameters
Expand Down Expand Up @@ -162,7 +162,14 @@ def check(self):
return

@classmethod
def from_step_impf(cls, intensity, mdd=(0, 1), paa=(1, 1), impf_id=1):
def from_step_impf(
cls,
intensity: tuple[float, float, float],
haz_type: str,
mdd: tuple[float, float] = (0, 1),
paa: tuple[float, float] = (1, 1),
impf_id: int = 1,
**kwargs):

""" Step function type impact function.

Expand All @@ -176,12 +183,16 @@ def from_step_impf(cls, intensity, mdd=(0, 1), paa=(1, 1), impf_id=1):
----------
intensity: tuple(float, float, float)
tuple of 3-intensity numbers: (minimum, threshold, maximum)
haz_type: str
the reference string for the hazard (e.g., 'TC', 'RF', 'WS', ...)
mdd: tuple(float, float)
(min, max) mdd values. The default is (0, 1)
paa: tuple(float, float)
(min, max) paa values. The default is (1, 1)
impf_id : int, optional, default=1
impact function id
kwargs :
keyword arguments passed to ImpactFunc()

Return
------
Expand All @@ -196,7 +207,8 @@ def from_step_impf(cls, intensity, mdd=(0, 1), paa=(1, 1), impf_id=1):
mdd_min, mdd_max = mdd
mdd = np.array([mdd_min, mdd_min, mdd_max, mdd_max])

return cls(id=impf_id, intensity=intensity, mdd=mdd, paa=paa)
return cls(haz_type=haz_type, id=impf_id,
intensity=intensity, mdd=mdd, paa=paa, **kwargs)

def set_step_impf(self, *args, **kwargs):
"""This function is deprecated, use ImpactFunc.from_step_impf instead."""
Expand All @@ -205,7 +217,15 @@ def set_step_impf(self, *args, **kwargs):
self.__dict__ = ImpactFunc.from_step_impf(*args, **kwargs).__dict__

@classmethod
def from_sigmoid_impf(cls, intensity, L, k, x0, if_id=1):
def from_sigmoid_impf(
cls,
intensity: tuple[float, float, float],
L: float,
k: float,
x0: float,
haz_type: str,
impf_id: int = 1,
**kwargs):
"""Sigmoid type impact function hinging on three parameter.

This type of impact function is very flexible for any sort of study,
Expand All @@ -228,20 +248,25 @@ def from_sigmoid_impf(cls, intensity, L, k, x0, if_id=1):
"slope" of sigmoid
x0 : float
intensity value where f(x)==L/2
if_id : int, optional, default=1
haz_type: str
the reference string for the hazard (e.g., 'TC', 'RF', 'WS', ...)
impf_id : int, optional, default=1
impact function id
kwargs :
keyword arguments passed to ImpactFunc()

Return
leonie-villiger marked this conversation as resolved.
Show resolved Hide resolved
------
impf : climada.entity.impact_funcs.ImpactFunc
Step impact function
Sigmoid impact function
"""
inten_min, inten_max, inten_step = intensity
intensity = np.arange(inten_min, inten_max, inten_step)
paa = np.ones(len(intensity))
mdd = L / (1 + np.exp(-k * (intensity - x0)))

return cls(id=if_id, intensity=intensity, paa=paa, mdd=mdd)
return cls(haz_type=haz_type, id=impf_id, intensity=intensity,
paa=paa, mdd=mdd, **kwargs)

def set_sigmoid_impf(self, *args, **kwargs):
"""This function is deprecated, use LitPop.from_countries instead."""
Expand Down
18 changes: 11 additions & 7 deletions climada/entity/impact_funcs/test/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,31 @@ def test_calc_mdr_pass(self):
new_inten = 17.2
self.assertEqual(imp_fun.calc_mdr(new_inten), 0.029583999999999996)

def test_set_step(self):
def test_from_step(self):
"""Check default impact function: step function"""
inten = (0, 5, 10)
imp_fun = ImpactFunc.from_step_impf(inten)
imp_fun = ImpactFunc.from_step_impf(
intensity=inten, haz_type='TC', impf_id=2)
self.assertTrue(np.array_equal(imp_fun.paa, np.ones(4)))
self.assertTrue(np.array_equal(imp_fun.mdd, np.array([0, 0, 1, 1])))
self.assertTrue(np.array_equal(imp_fun.intensity, np.array([0, 5, 5, 10])))
self.assertEqual(imp_fun.haz_type, 'TC')
self.assertEqual(imp_fun.id, 2)

def test_set_sigmoid(self):

def test_from_sigmoid(self):
"""Check default impact function: sigmoid function"""
inten = (0, 100, 5)
imp_fun = ImpactFunc.from_sigmoid_impf(inten, L=1.0, k=2., x0=50.)
imp_fun = ImpactFunc.from_sigmoid_impf(
inten, L=1.0, k=2., x0=50., haz_type='RF', impf_id=2)
self.assertTrue(np.array_equal(imp_fun.paa, np.ones(20)))
self.assertEqual(imp_fun.mdd[10], 0.5)
self.assertEqual(imp_fun.mdd[-1], 1.0)
self.assertTrue(np.array_equal(imp_fun.intensity, np.arange(0, 100, 5)))
self.assertEqual(imp_fun.haz_type, 'RF')
self.assertEqual(imp_fun.id, 2)

# Execute Tests
if __name__ == "__main__":
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestInterpolation)
unittest.TextTestRunner(verbosity=2).run(TESTS)



2 changes: 1 addition & 1 deletion climada/hazard/test/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1392,7 +1392,7 @@ def test_clear_pool(self):
def dummy_step_impf(haz):
from climada.entity import ImpactFunc
intensity = (0, 1, haz.intensity.max())
impf = ImpactFunc.from_step_impf(intensity)
impf = ImpactFunc.from_step_impf(intensity, haz_type=haz.tag.haz_type)
return impf

class TestImpactFuncs(unittest.TestCase):
Expand Down