Skip to content

Integrates risk trajectories within climada #1037

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

Draft
wants to merge 25 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3692813
Implements equality methods for impf and impfset
spjuhel Mar 18, 2025
1606896
adds a few tests
spjuhel Mar 18, 2025
a8b45c6
Merge remote-tracking branch 'origin/develop' into feature/eq_methods…
spjuhel Mar 18, 2025
3776228
updates changelog
spjuhel Mar 18, 2025
f6d9feb
Improves dict comparison
spjuhel Mar 24, 2025
222644d
Applies suggestion from Lukas (1)
spjuhel Mar 24, 2025
29ecee4
init commit
spjuhel Apr 2, 2025
d7c0f23
Pushes tutorial notebook
spjuhel Apr 2, 2025
31ef976
Working tutorial
spjuhel Apr 2, 2025
10ca243
fixes a typo from mass edit
spjuhel Apr 2, 2025
389787e
Refactor to a more object-oriented design
spjuhel Apr 15, 2025
4ea685d
fixes some bugs
spjuhel Apr 15, 2025
0d6335b
fixses some linter issues
spjuhel Apr 16, 2025
31ea96d
feat(trajectory): interpolation and impactcalc files, eai_gdf metric
spjuhel Apr 17, 2025
3f13c2e
impact func __eq__
spjuhel Apr 24, 2025
519c9af
feat(test): adds test for ImpactFuncSet
spjuhel Apr 24, 2025
adfc7e2
fix(format): removes trailing whitespace
spjuhel Apr 24, 2025
6856117
Merge branch 'feature/eq_methods_for_impf' into feature/risk_trajectory
spjuhel Apr 24, 2025
9c358ca
fix: npv arg not considered in .all_risk_metrics()
spjuhel Apr 24, 2025
b7bf68b
refactor: enforce kwargs in RiskTrajectory init
spjuhel Apr 24, 2025
b75ab91
refactor: adds consistency between attribute
spjuhel Apr 24, 2025
e12b2c4
refactor(risktraj): lays better foundations for MeasureAppraiser
spjuhel Apr 25, 2025
8666197
minor additions, refactors and fixes
spjuhel May 7, 2025
9711379
format,doc: cleans up code, adds docstrings
spjuhel May 14, 2025
9aec92f
test: adds lots of unittests, and placeholders
spjuhel May 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ Code freeze date: YYYY-MM-DD

### Added

- `climada.entity.impact_funcs.base.ImpactFunc.__eq__` method
- `climada.entity.impact_funcs.impact_func_set.ImpactFuncSet.__eq__` method

### Changed
- `Hazard.local_exceedance_intensity`, `Hazard.local_return_period` and `Impact.local_exceedance_impact`, `Impact.local_return_period`, using the `climada.util.interpolation` module: New default (no binning), binning on decimals, and faster implementation [#1012](https://github.com/CLIMADA-project/climada_python/pull/1012)
### Fixed
Expand Down
23 changes: 18 additions & 5 deletions climada/entity/impact_funcs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@
self.mdd = mdd if mdd is not None else np.array([])
self.paa = paa if paa is not None else np.array([])

def __eq__(self, value: object, /) -> bool:
if isinstance(value, ImpactFunc):
return (
self.haz_type == value.haz_type
and self.id == value.id
and self.name == value.name
and self.intensity_unit == value.intensity_unit
and np.array_equal(self.intensity, value.intensity)
and np.array_equal(self.mdd, value.mdd)
and np.array_equal(self.paa, value.paa)
)
return False

def calc_mdr(self, inten: Union[float, np.ndarray]) -> np.ndarray:
"""Interpolate impact function to a given intensity.

Expand Down Expand Up @@ -177,7 +190,7 @@
mdd: tuple[float, float] = (0, 1),
paa: tuple[float, float] = (1, 1),
impf_id: int = 1,
**kwargs
**kwargs,
):
"""Step function type impact function.

Expand Down Expand Up @@ -218,7 +231,7 @@
intensity=intensity,
mdd=mdd,
paa=paa,
**kwargs
**kwargs,
)

def set_step_impf(self, *args, **kwargs):
Expand All @@ -235,10 +248,10 @@
intensity: tuple[float, float, float],
L: float,
k: float,
x0: float,

Check warning on line 251 in climada/entity/impact_funcs/base.py

View check run for this annotation

Jenkins - WCR / Pylint

invalid-name

LOW: Argument name "x0" doesn't conform to '(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$' pattern
Raw output
Used when the name doesn't match the regular expression associated to its type(constant, variable, class...).
haz_type: str,
impf_id: int = 1,
**kwargs
**kwargs,
):
r"""Sigmoid type impact function hinging on three parameter.

Expand Down Expand Up @@ -287,7 +300,7 @@
intensity=intensity,
paa=paa,
mdd=mdd,
**kwargs
**kwargs,
)

def set_sigmoid_impf(self, *args, **kwargs):
Expand All @@ -308,7 +321,7 @@
exponent: float,
haz_type: str,
impf_id: int = 1,
**kwargs
**kwargs,
):
r"""S-shape polynomial impact function hinging on four parameter.

Expand Down
6 changes: 6 additions & 0 deletions climada/entity/impact_funcs/impact_func_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ def __init__(self, impact_funcs: Optional[Iterable[ImpactFunc]] = None):
for impf in impact_funcs:
self.append(impf)

def __eq__(self, value: object, /) -> bool:
if isinstance(value, ImpactFuncSet):
return self._data == value._data

return False

def clear(self):
"""Reinitialize attributes."""
self._data = dict() # {hazard_type : {id:ImpactFunc}}
Expand Down
75 changes: 73 additions & 2 deletions climada/entity/impact_funcs/test/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,74 @@
from climada.entity.impact_funcs.base import ImpactFunc


class TestEquality(unittest.TestCase):
"""Test equality method"""

def setUp(self):
self.impf1 = ImpactFunc(
haz_type="TC",
id=1,
intensity=np.array([1, 2, 3]),
mdd=np.array([0.1, 0.2, 0.3]),
paa=np.array([0.4, 0.5, 0.6]),
intensity_unit="m/s",
name="Test Impact",
)
self.impf2 = ImpactFunc(
haz_type="TC",
id=1,
intensity=np.array([1, 2, 3]),
mdd=np.array([0.1, 0.2, 0.3]),
paa=np.array([0.4, 0.5, 0.6]),
intensity_unit="m/s",
name="Test Impact",
)
self.impf3 = ImpactFunc(
haz_type="FL",
id=2,
intensity=np.array([4, 5, 6]),
mdd=np.array([0.7, 0.8, 0.9]),
paa=np.array([0.1, 0.2, 0.3]),
intensity_unit="m",
name="Another Impact",
)

def test_reflexivity(self):
self.assertEqual(self.impf1, self.impf1)

def test_symmetry(self):
self.assertEqual(self.impf1, self.impf2)
self.assertEqual(self.impf2, self.impf1)

def test_transitivity(self):
impf4 = ImpactFunc(
haz_type="TC",
id=1,
intensity=np.array([1, 2, 3]),
mdd=np.array([0.1, 0.2, 0.3]),
paa=np.array([0.4, 0.5, 0.6]),
intensity_unit="m/s",
name="Test Impact",
)
self.assertEqual(self.impf1, self.impf2)
self.assertEqual(self.impf2, impf4)
self.assertEqual(self.impf1, impf4)

def test_consistency(self):
self.assertEqual(self.impf1, self.impf2)
self.assertEqual(self.impf1, self.impf2)

def test_comparison_with_none(self):
self.assertNotEqual(self.impf1, None)

def test_different_types(self):
self.assertNotEqual(self.impf1, "Not an ImpactFunc")

def test_inequality(self):
self.assertNotEqual(self.impf1, self.impf3)
self.assertTrue(self.impf1 != self.impf3)


class TestInterpolation(unittest.TestCase):
"""Impact function interpolation test"""

Expand Down Expand Up @@ -139,5 +207,8 @@ def test_aux_vars(impf):

# Execute Tests
if __name__ == "__main__":
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestInterpolation)
unittest.TextTestRunner(verbosity=2).run(TESTS)
equality_tests = unittest.TestLoader().loadTestsFromTestCase(TestEquality)
interpolation_tests = unittest.TestLoader().loadTestsFromTestCase(TestInterpolation)
unittest.TextTestRunner(verbosity=2).run(
unittest.TestSuite([equality_tests, interpolation_tests])
)
50 changes: 50 additions & 0 deletions climada/entity/impact_funcs/test/test_imp_fun_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,55 @@ def test_remove_add_pass(self):
self.assertEqual([1], imp_fun.get_ids("TC"))


class TestEquality(unittest.TestCase):
"""Test equality method for ImpactFuncSet"""

def setUp(self):
intensity = np.array([0, 20])
paa = np.array([0, 1])
mdd = np.array([0, 0.5])

fun_1 = ImpactFunc("TC", 3, intensity, mdd, paa)
fun_2 = ImpactFunc("TC", 3, intensity, mdd, paa)
fun_3 = ImpactFunc("TC", 4, intensity + 1, mdd, paa)

self.impact_set1 = ImpactFuncSet([fun_1])
self.impact_set2 = ImpactFuncSet([fun_2])
self.impact_set3 = ImpactFuncSet([fun_3])
self.impact_set4 = ImpactFuncSet([fun_1, fun_3])

def test_reflexivity(self):
self.assertEqual(self.impact_set1, self.impact_set1)

def test_symmetry(self):
self.assertEqual(self.impact_set1, self.impact_set2)
self.assertEqual(self.impact_set2, self.impact_set1)

def test_transitivity(self):
impact_set5 = ImpactFuncSet([self.impact_set1._data["TC"][3]])
self.assertEqual(self.impact_set1, self.impact_set2)
self.assertEqual(self.impact_set2, impact_set5)
self.assertEqual(self.impact_set1, impact_set5)

def test_consistency(self):
self.assertEqual(self.impact_set1, self.impact_set2)
self.assertEqual(self.impact_set1, self.impact_set2)

def test_comparison_with_none(self):
self.assertNotEqual(self.impact_set1, None)

def test_different_types(self):
self.assertNotEqual(self.impact_set1, "Not an ImpactFuncSet")

def test_field_comparison(self):
self.assertNotEqual(self.impact_set1, self.impact_set3)
self.assertNotEqual(self.impact_set1, self.impact_set4)

def test_inequality(self):
self.assertNotEqual(self.impact_set1, self.impact_set3)
self.assertTrue(self.impact_set1 != self.impact_set3)


class TestChecker(unittest.TestCase):
"""Test loading funcions from the ImpactFuncSet class"""

Expand Down Expand Up @@ -592,6 +641,7 @@ def test_write_read_pass(self):
# Execute Tests
if __name__ == "__main__":
TESTS = unittest.TestLoader().loadTestsFromTestCase(TestContainer)
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestEquality))
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestChecker))
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestExtend))
TESTS.addTests(unittest.TestLoader().loadTestsFromTestCase(TestReaderExcel))
Expand Down
2 changes: 2 additions & 0 deletions climada/test/test_trajectories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# 100% Coverage goal:
## Integration test for risk period with periods and freq defined
18 changes: 18 additions & 0 deletions climada/trajectories/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
This file is part of CLIMADA.

Copyright (C) 2017 ETH Zurich, CLIMADA contributors listed in AUTHORS.

CLIMADA is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free
Software Foundation, version 3.

CLIMADA is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with CLIMADA. If not, see <https://www.gnu.org/licenses/>.

---
"""
Loading
Loading