From ea1f91a1522c4fd89f6908e195b6b7ba8cd561c1 Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 11:39:07 +0100 Subject: [PATCH 01/19] Create hazardForecast base class --- climada/hazard/hazard_forecast.py | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 climada/hazard/hazard_forecast.py diff --git a/climada/hazard/hazard_forecast.py b/climada/hazard/hazard_forecast.py new file mode 100644 index 0000000000..269f4bcdbd --- /dev/null +++ b/climada/hazard/hazard_forecast.py @@ -0,0 +1,40 @@ +""" +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 . + +--- + +Define Forecast variant of Hazard. +""" + +import logging + +import numpy as np + +from climada.engine.forecast import Forecast +from climada.hazard.hazard import Hazard + +LOGGER = logging.getLogger(__name__) + + +class HazardForecast(Forecast, Hazard): + + def __init__( + self, + lead_time: np.ndarray | None = None, + member: np.ndarray | None = None, + **kwargs, + ): + super().__init__(lead_time=lead_time, member=member, **kwargs) From 4e253821486497cb38becd46018a8429bc576875 Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 11:40:04 +0100 Subject: [PATCH 02/19] Add from_hazard method in hazardForecast --- climada/hazard/hazard_forecast.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/climada/hazard/hazard_forecast.py b/climada/hazard/hazard_forecast.py index 269f4bcdbd..fbac93a387 100644 --- a/climada/hazard/hazard_forecast.py +++ b/climada/hazard/hazard_forecast.py @@ -38,3 +38,21 @@ def __init__( **kwargs, ): super().__init__(lead_time=lead_time, member=member, **kwargs) + + def from_hazard(self, hazard: Hazard): + return cls( + haz_type=hazard.haz_type, + pool=hazard.pool, + units=hazard.units, + centroids=hazard.centroids, + event_id=hazard.event_id, + frequency=hazard.frequency, + frequency_unit=hazard.frequency_unit, + event_name=hazard.event_name, + date=hazard.date, + orig=hazard.orig, + intensity=hazard.intensity, + fraction=hazard.fraction, + lead_time=self.lead_time, + member=self.member, + ) From 03a9cf714cced6b127c8bf6f47829d5b944ed0f7 Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 11:42:26 +0100 Subject: [PATCH 03/19] Remove duplicate file --- climada/hazard/forecast.py | 38 ++++++++++++++++++++ climada/hazard/hazard_forecast.py | 58 ------------------------------- 2 files changed, 38 insertions(+), 58 deletions(-) delete mode 100644 climada/hazard/hazard_forecast.py diff --git a/climada/hazard/forecast.py b/climada/hazard/forecast.py index 61095539ba..fbac93a387 100644 --- a/climada/hazard/forecast.py +++ b/climada/hazard/forecast.py @@ -18,3 +18,41 @@ Define Forecast variant of Hazard. """ + +import logging + +import numpy as np + +from climada.engine.forecast import Forecast +from climada.hazard.hazard import Hazard + +LOGGER = logging.getLogger(__name__) + + +class HazardForecast(Forecast, Hazard): + + def __init__( + self, + lead_time: np.ndarray | None = None, + member: np.ndarray | None = None, + **kwargs, + ): + super().__init__(lead_time=lead_time, member=member, **kwargs) + + def from_hazard(self, hazard: Hazard): + return cls( + haz_type=hazard.haz_type, + pool=hazard.pool, + units=hazard.units, + centroids=hazard.centroids, + event_id=hazard.event_id, + frequency=hazard.frequency, + frequency_unit=hazard.frequency_unit, + event_name=hazard.event_name, + date=hazard.date, + orig=hazard.orig, + intensity=hazard.intensity, + fraction=hazard.fraction, + lead_time=self.lead_time, + member=self.member, + ) diff --git a/climada/hazard/hazard_forecast.py b/climada/hazard/hazard_forecast.py deleted file mode 100644 index fbac93a387..0000000000 --- a/climada/hazard/hazard_forecast.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -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 . - ---- - -Define Forecast variant of Hazard. -""" - -import logging - -import numpy as np - -from climada.engine.forecast import Forecast -from climada.hazard.hazard import Hazard - -LOGGER = logging.getLogger(__name__) - - -class HazardForecast(Forecast, Hazard): - - def __init__( - self, - lead_time: np.ndarray | None = None, - member: np.ndarray | None = None, - **kwargs, - ): - super().__init__(lead_time=lead_time, member=member, **kwargs) - - def from_hazard(self, hazard: Hazard): - return cls( - haz_type=hazard.haz_type, - pool=hazard.pool, - units=hazard.units, - centroids=hazard.centroids, - event_id=hazard.event_id, - frequency=hazard.frequency, - frequency_unit=hazard.frequency_unit, - event_name=hazard.event_name, - date=hazard.date, - orig=hazard.orig, - intensity=hazard.intensity, - fraction=hazard.fraction, - lead_time=self.lead_time, - member=self.member, - ) From 5adaccb697d17d3359da0afa6bf23350a0b5260d Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 12:06:28 +0100 Subject: [PATCH 04/19] Give explicit name to kwards --- climada/hazard/forecast.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/climada/hazard/forecast.py b/climada/hazard/forecast.py index fbac93a387..94497e4318 100644 --- a/climada/hazard/forecast.py +++ b/climada/hazard/forecast.py @@ -35,12 +35,14 @@ def __init__( self, lead_time: np.ndarray | None = None, member: np.ndarray | None = None, - **kwargs, + **hazard_kwargs, ): - super().__init__(lead_time=lead_time, member=member, **kwargs) + super().__init__(lead_time=lead_time, member=member, **hazard_kwargs) def from_hazard(self, hazard: Hazard): return cls( + lead_time=self.lead_time, + member=self.member, haz_type=hazard.haz_type, pool=hazard.pool, units=hazard.units, @@ -53,6 +55,4 @@ def from_hazard(self, hazard: Hazard): orig=hazard.orig, intensity=hazard.intensity, fraction=hazard.fraction, - lead_time=self.lead_time, - member=self.member, ) From 55883d8bfbba012432ec3d8b11486dd6cfa9de42 Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 12:23:28 +0100 Subject: [PATCH 05/19] Split hazard_kwargs and dummy hazard in hazard base test --- climada/hazard/test/test_base.py | 43 ++++++++++++++++---------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/climada/hazard/test/test_base.py b/climada/hazard/test/test_base.py index b4cbafd0e3..6e97bb8769 100644 --- a/climada/hazard/test/test_base.py +++ b/climada/hazard/test/test_base.py @@ -46,27 +46,28 @@ """ -def dummy_hazard(): - fraction = sparse.csr_matrix( - [[0.02, 0.03, 0.04], [0.01, 0.01, 0.01], [0.3, 0.1, 0.0], [0.3, 0.2, 0.0]] - ) - intensity = sparse.csr_matrix( - [[0.2, 0.3, 0.4], [0.1, 0.1, 0.01], [4.3, 2.1, 1.0], [5.3, 0.2, 0.0]] - ) - - return Hazard( - "TC", - intensity=intensity, - fraction=fraction, - centroids=Centroids(lat=np.array([1, 3, 5]), lon=np.array([2, 4, 6])), - event_id=np.array([1, 2, 3, 4]), - event_name=["ev1", "ev2", "ev3", "ev4"], - date=np.array([1, 2, 3, 4]), - orig=np.array([True, False, False, True]), - frequency=np.array([0.1, 0.5, 0.5, 0.2]), - frequency_unit="1/week", - units="m/s", - ) +def hazard_kwargs(): + return { + haz_type: "TC", + intensity: sparse.csr_matrix( + [[0.2, 0.3, 0.4], [0.1, 0.1, 0.01], [4.3, 2.1, 1.0], [5.3, 0.2, 0.0]] + ), + fraction: sparse.csr_matrix( + [[0.02, 0.03, 0.04], [0.01, 0.01, 0.01], [0.3, 0.1, 0.0], [0.3, 0.2, 0.0]] + ), + centroids: Centroids(lat=np.array([1, 3, 5]), lon=np.array([2, 4, 6])), + event_id: np.array([1, 2, 3, 4]), + event_name: ["ev1", "ev2", "ev3", "ev4"], + date: np.array([1, 2, 3, 4]), + orig: np.array([True, False, False, True]), + frequency: np.array([0.1, 0.5, 0.5, 0.2]), + frequency_unit: "1/week", + units: "m/s", + } + + +def dummy_hazard(haz_kwargs): + return Hazard(**haz_kwargs) class TestLoader(unittest.TestCase): From e94be48674819aeee70df22b1b21df4ce1a5c63e Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 12:26:29 +0100 Subject: [PATCH 06/19] Draft test init hazardForecast --- climada/hazard/test/test_forecast.py | 44 +++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/climada/hazard/test/test_forecast.py b/climada/hazard/test/test_forecast.py index f895711f83..95cc60bad3 100644 --- a/climada/hazard/test/test_forecast.py +++ b/climada/hazard/test/test_forecast.py @@ -19,13 +19,55 @@ Tests for Hazard Forecast. """ +import numpy as np +import numpy.testing as npt import pytest -from climada.hazard import Hazard +from climada.hazard import Hazard, HazardForecast +from climada.hazard.test.test_base import hazard_kwargs # --- Examples for fixtures and test organization --- # +@pytest.fixture +def haz_kwargs(): + return hazard_kwargs() + + +@pytest.fixture +def dummy_hazard(haz_kwargs): + return Hazard(haz_kwargs()) + + +def test_init_hazard_forecast(): + haz_fc = HazardForecast( + lead_time=np.array( + ["2024-01-01T00:00:00", "2024-01-01T00:01:00"], dtype="datetime64[s]" + ), + member=np.array([0, 1]), + **hazard_kwargs, + ) + assert isinstance(haz_fc, HazardForecast) + assert np.assert_array_equal( + haz_fc.lead_time, + np.array(["2024-01-01T00:00:00", "2024-01-01T00:01:00"], dtype="datetime64[s]"), + ) + assert haz_fc.lead_time.dtype == "datetime64[s]" + assert haz_fc.member == np.array([0, 1]) + assert haz_fc.haz_type == haz_kwargs["haz_type"] + assert haz_fc.pool == haz_kwargs["pool"] + assert haz_fc.units == haz_kwargs["units"] + assert haz_fc.centroids == haz_kwargs["centroids"] + assert haz_fc.event_id == haz_kwargs["event_id"] + assert haz_fc.frequency == haz_kwargs["frequency"] + assert haz_fc.frequency_unit == haz_kwargs["frequency_unit"] + assert haz_fc.event_name == haz_kwargs["event_name"] + assert haz_fc.date == haz_kwargs["date"] + assert haz_fc.orig == haz_kwargs["orig"] + assert haz_fc.intensity == haz_kwargs["intensity"] + assert haz_fc.fraction == haz_kwargs["fraction"] + + @pytest.fixture def hazard(): return Hazard() From 38b2fc5f22e46e64e411c1a6281daafe94f0de69 Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 12:43:57 +0100 Subject: [PATCH 07/19] Correct error in hazard_kwargs test base --- climada/hazard/test/test_base.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/climada/hazard/test/test_base.py b/climada/hazard/test/test_base.py index 6e97bb8769..f059a167ab 100644 --- a/climada/hazard/test/test_base.py +++ b/climada/hazard/test/test_base.py @@ -48,21 +48,21 @@ def hazard_kwargs(): return { - haz_type: "TC", - intensity: sparse.csr_matrix( + "haz_type": "TC", + "intensity": sparse.csr_matrix( [[0.2, 0.3, 0.4], [0.1, 0.1, 0.01], [4.3, 2.1, 1.0], [5.3, 0.2, 0.0]] ), - fraction: sparse.csr_matrix( + "fraction": sparse.csr_matrix( [[0.02, 0.03, 0.04], [0.01, 0.01, 0.01], [0.3, 0.1, 0.0], [0.3, 0.2, 0.0]] ), - centroids: Centroids(lat=np.array([1, 3, 5]), lon=np.array([2, 4, 6])), - event_id: np.array([1, 2, 3, 4]), - event_name: ["ev1", "ev2", "ev3", "ev4"], - date: np.array([1, 2, 3, 4]), - orig: np.array([True, False, False, True]), - frequency: np.array([0.1, 0.5, 0.5, 0.2]), - frequency_unit: "1/week", - units: "m/s", + "centroids": Centroids(lat=np.array([1, 3, 5]), lon=np.array([2, 4, 6])), + "event_id": np.array([1, 2, 3, 4]), + "event_name": ["ev1", "ev2", "ev3", "ev4"], + "date": np.array([1, 2, 3, 4]), + "orig": np.array([True, False, False, True]), + "frequency": np.array([0.1, 0.5, 0.5, 0.2]), + "frequency_unit": "1/week", + "units": "m/s", } From 3a2a024b316a0e701b46fe3fa252c42f32724bdd Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 12:44:41 +0100 Subject: [PATCH 08/19] Correct import --- climada/hazard/forecast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/climada/hazard/forecast.py b/climada/hazard/forecast.py index 94497e4318..7bcdb6ceaa 100644 --- a/climada/hazard/forecast.py +++ b/climada/hazard/forecast.py @@ -24,7 +24,7 @@ import numpy as np from climada.engine.forecast import Forecast -from climada.hazard.hazard import Hazard +from climada.hazard.base import Hazard LOGGER = logging.getLogger(__name__) From 488f5ea176e0f200cd2cce689e484c5346a28585 Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 12:46:13 +0100 Subject: [PATCH 09/19] Correct use of fixtures --- climada/hazard/test/test_forecast.py | 41 +++++++++++++++------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/climada/hazard/test/test_forecast.py b/climada/hazard/test/test_forecast.py index 95cc60bad3..0a548594a5 100644 --- a/climada/hazard/test/test_forecast.py +++ b/climada/hazard/test/test_forecast.py @@ -23,29 +23,32 @@ import numpy.testing as npt import pytest -from climada.hazard import Hazard, HazardForecast +from climada.hazard.base import Hazard +from climada.hazard.forecast import HazardForecast from climada.hazard.test.test_base import hazard_kwargs # --- Examples for fixtures and test organization --- # @pytest.fixture -def haz_kwargs(): - return hazard_kwargs() +def hazard_kwargs_fixture(): + from climada.hazard.test.test_base import hazard_kwargs as get_hazard_kwargs + + return get_hazard_kwargs() @pytest.fixture -def dummy_hazard(haz_kwargs): - return Hazard(haz_kwargs()) +def dummy_hazard(hazard_kwargs_fixture): + return Hazard(**hazard_kwargs_fixture) -def test_init_hazard_forecast(): +def test_init_hazard_forecast(hazard_kwargs_fixture): haz_fc = HazardForecast( lead_time=np.array( ["2024-01-01T00:00:00", "2024-01-01T00:01:00"], dtype="datetime64[s]" ), member=np.array([0, 1]), - **hazard_kwargs, + **hazard_kwargs_fixture, ) assert isinstance(haz_fc, HazardForecast) assert np.assert_array_equal( @@ -54,18 +57,18 @@ def test_init_hazard_forecast(): ) assert haz_fc.lead_time.dtype == "datetime64[s]" assert haz_fc.member == np.array([0, 1]) - assert haz_fc.haz_type == haz_kwargs["haz_type"] - assert haz_fc.pool == haz_kwargs["pool"] - assert haz_fc.units == haz_kwargs["units"] - assert haz_fc.centroids == haz_kwargs["centroids"] - assert haz_fc.event_id == haz_kwargs["event_id"] - assert haz_fc.frequency == haz_kwargs["frequency"] - assert haz_fc.frequency_unit == haz_kwargs["frequency_unit"] - assert haz_fc.event_name == haz_kwargs["event_name"] - assert haz_fc.date == haz_kwargs["date"] - assert haz_fc.orig == haz_kwargs["orig"] - assert haz_fc.intensity == haz_kwargs["intensity"] - assert haz_fc.fraction == haz_kwargs["fraction"] + assert haz_fc.haz_type == hazard_kwargs_fixture["haz_type"] + assert haz_fc.pool == hazard_kwargs_fixture["pool"] + assert haz_fc.units == hazard_kwargs_fixture["units"] + assert haz_fc.centroids == hazard_kwargs_fixture["centroids"] + assert haz_fc.event_id == hazard_kwargs_fixture["event_id"] + assert haz_fc.frequency == hazard_kwargs_fixture["frequency"] + assert haz_fc.frequency_unit == hazard_kwargs_fixture["frequency_unit"] + assert haz_fc.event_name == hazard_kwargs_fixture["event_name"] + assert haz_fc.date == hazard_kwargs_fixture["date"] + assert haz_fc.orig == hazard_kwargs_fixture["orig"] + assert haz_fc.intensity == hazard_kwargs_fixture["intensity"] + assert haz_fc.fraction == hazard_kwargs_fixture["fraction"] @pytest.fixture From 4534794db4f60c3b8d9e1f0d27a0e01a96cbc44f Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 13:06:42 +0100 Subject: [PATCH 10/19] Correct assert for arrays --- climada/hazard/test/test_forecast.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/climada/hazard/test/test_forecast.py b/climada/hazard/test/test_forecast.py index 0a548594a5..bbd8840328 100644 --- a/climada/hazard/test/test_forecast.py +++ b/climada/hazard/test/test_forecast.py @@ -51,24 +51,28 @@ def test_init_hazard_forecast(hazard_kwargs_fixture): **hazard_kwargs_fixture, ) assert isinstance(haz_fc, HazardForecast) - assert np.assert_array_equal( + npt.assert_array_equal( haz_fc.lead_time, np.array(["2024-01-01T00:00:00", "2024-01-01T00:01:00"], dtype="datetime64[s]"), ) assert haz_fc.lead_time.dtype == "datetime64[s]" - assert haz_fc.member == np.array([0, 1]) + npt.assert_array_equal(haz_fc.member, np.array([0, 1])) assert haz_fc.haz_type == hazard_kwargs_fixture["haz_type"] assert haz_fc.pool == hazard_kwargs_fixture["pool"] assert haz_fc.units == hazard_kwargs_fixture["units"] assert haz_fc.centroids == hazard_kwargs_fixture["centroids"] - assert haz_fc.event_id == hazard_kwargs_fixture["event_id"] - assert haz_fc.frequency == hazard_kwargs_fixture["frequency"] + npt.assert_array_equal(haz_fc.event_id, hazard_kwargs_fixture["event_id"]) + npt.assert_array_equal(haz_fc.frequency, hazard_kwargs_fixture["frequency"]) assert haz_fc.frequency_unit == hazard_kwargs_fixture["frequency_unit"] - assert haz_fc.event_name == hazard_kwargs_fixture["event_name"] - assert haz_fc.date == hazard_kwargs_fixture["date"] - assert haz_fc.orig == hazard_kwargs_fixture["orig"] - assert haz_fc.intensity == hazard_kwargs_fixture["intensity"] - assert haz_fc.fraction == hazard_kwargs_fixture["fraction"] + npt.assert_array_equal(haz_fc.event_name, hazard_kwargs_fixture["event_name"]) + npt.assert_array_equal(haz_fc.date, hazard_kwargs_fixture["date"]) + npt.assert_array_equal(haz_fc.orig, hazard_kwargs_fixture["orig"]) + npt.assert_array_equal( + haz_fc.intensity.todense(), hazard_kwargs_fixture["intensity"].todense() + ) + npt.assert_array_equal( + haz_fc.fraction.todense(), hazard_kwargs_fixture["fraction"].todense() + ) @pytest.fixture From 2d15a81569b908731b3685ee7f492f7c51922108 Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 13:07:03 +0100 Subject: [PATCH 11/19] Add pool kwargs in hazard_kwargs --- climada/hazard/test/test_base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/climada/hazard/test/test_base.py b/climada/hazard/test/test_base.py index f059a167ab..b5394f79f6 100644 --- a/climada/hazard/test/test_base.py +++ b/climada/hazard/test/test_base.py @@ -49,6 +49,7 @@ def hazard_kwargs(): return { "haz_type": "TC", + "pool": None, "intensity": sparse.csr_matrix( [[0.2, 0.3, 0.4], [0.1, 0.1, 0.01], [4.3, 2.1, 1.0], [5.3, 0.2, 0.0]] ), From 09fd55bf1a9c6ab3e01303ad1b72ac21411fbcfe Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 13:07:50 +0100 Subject: [PATCH 12/19] add from hazard as class method --- climada/hazard/forecast.py | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/climada/hazard/forecast.py b/climada/hazard/forecast.py index 7bcdb6ceaa..1cd3cb87e0 100644 --- a/climada/hazard/forecast.py +++ b/climada/hazard/forecast.py @@ -23,8 +23,8 @@ import numpy as np -from climada.engine.forecast import Forecast from climada.hazard.base import Hazard +from climada.util.forecast import Forecast LOGGER = logging.getLogger(__name__) @@ -39,20 +39,21 @@ def __init__( ): super().__init__(lead_time=lead_time, member=member, **hazard_kwargs) - def from_hazard(self, hazard: Hazard): - return cls( - lead_time=self.lead_time, - member=self.member, - haz_type=hazard.haz_type, - pool=hazard.pool, - units=hazard.units, - centroids=hazard.centroids, - event_id=hazard.event_id, - frequency=hazard.frequency, - frequency_unit=hazard.frequency_unit, - event_name=hazard.event_name, - date=hazard.date, - orig=hazard.orig, - intensity=hazard.intensity, - fraction=hazard.fraction, - ) + @classmethod + def from_hazard(self, hazard: Hazard): + return cls( + lead_time=self.lead_time, + member=self.member, + haz_type=hazard.haz_type, + pool=hazard.pool, + units=hazard.units, + centroids=hazard.centroids, + event_id=hazard.event_id, + frequency=hazard.frequency, + frequency_unit=hazard.frequency_unit, + event_name=hazard.event_name, + date=hazard.date, + orig=hazard.orig, + intensity=hazard.intensity, + fraction=hazard.fraction, + ) From 86c9d934c0dbf3465d27c614894c3d226f5b9578 Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 13:11:16 +0100 Subject: [PATCH 13/19] Add docstrings to hazardForecast base class --- climada/hazard/forecast.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/climada/hazard/forecast.py b/climada/hazard/forecast.py index 1cd3cb87e0..8b192db0fd 100644 --- a/climada/hazard/forecast.py +++ b/climada/hazard/forecast.py @@ -37,10 +37,37 @@ def __init__( member: np.ndarray | None = None, **hazard_kwargs, ): + """ + Initialize a HazardForecast object. + + Parameters + ---------- + lead_time : np.ndarray of np.datetime64 or None, optional + Forecast lead times. Default is empty array. + member : np.ndarray or None, optional + Ensemble member identifiers as integers. Default is empty array. + **hazard_kwargs + keyword arguments to pass to Hazard.__init__. See Hazard.__init__ + docstring for details. + """ super().__init__(lead_time=lead_time, member=member, **hazard_kwargs) @classmethod def from_hazard(self, hazard: Hazard): + """ + Create a HazardForecast object from a Hazard object. + + Parameters + ---------- + hazard : Hazard + Hazard object to convert into a HazardForecast. + + Returns + ------- + HazardForecast + A HazardForecast object with the same attributes as the input hazard, + but with lead_time and member attributes set from instance of HazardForecast. + """ return cls( lead_time=self.lead_time, member=self.member, From 33d0ad907c6efd226b26752062db59b9812424ef Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 14:10:09 +0100 Subject: [PATCH 14/19] Rename fixture --- climada/hazard/test/test_forecast.py | 66 +++++++++++++++++++--------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/climada/hazard/test/test_forecast.py b/climada/hazard/test/test_forecast.py index bbd8840328..764c5c0433 100644 --- a/climada/hazard/test/test_forecast.py +++ b/climada/hazard/test/test_forecast.py @@ -31,24 +31,22 @@ @pytest.fixture -def hazard_kwargs_fixture(): - from climada.hazard.test.test_base import hazard_kwargs as get_hazard_kwargs - - return get_hazard_kwargs() +def haz_kwargs(): + return hazard_kwargs() @pytest.fixture -def dummy_hazard(hazard_kwargs_fixture): - return Hazard(**hazard_kwargs_fixture) +def dummy_hazard(haz_kwargs): + return Hazard(**haz_kwargs) -def test_init_hazard_forecast(hazard_kwargs_fixture): +def test_init_hazard_forecast(haz_kwargs): haz_fc = HazardForecast( lead_time=np.array( ["2024-01-01T00:00:00", "2024-01-01T00:01:00"], dtype="datetime64[s]" ), member=np.array([0, 1]), - **hazard_kwargs_fixture, + **haz_kwargs, ) assert isinstance(haz_fc, HazardForecast) npt.assert_array_equal( @@ -57,21 +55,49 @@ def test_init_hazard_forecast(hazard_kwargs_fixture): ) assert haz_fc.lead_time.dtype == "datetime64[s]" npt.assert_array_equal(haz_fc.member, np.array([0, 1])) - assert haz_fc.haz_type == hazard_kwargs_fixture["haz_type"] - assert haz_fc.pool == hazard_kwargs_fixture["pool"] - assert haz_fc.units == hazard_kwargs_fixture["units"] - assert haz_fc.centroids == hazard_kwargs_fixture["centroids"] - npt.assert_array_equal(haz_fc.event_id, hazard_kwargs_fixture["event_id"]) - npt.assert_array_equal(haz_fc.frequency, hazard_kwargs_fixture["frequency"]) - assert haz_fc.frequency_unit == hazard_kwargs_fixture["frequency_unit"] - npt.assert_array_equal(haz_fc.event_name, hazard_kwargs_fixture["event_name"]) - npt.assert_array_equal(haz_fc.date, hazard_kwargs_fixture["date"]) - npt.assert_array_equal(haz_fc.orig, hazard_kwargs_fixture["orig"]) + assert haz_fc.haz_type == haz_kwargs["haz_type"] + assert haz_fc.pool == haz_kwargs["pool"] + assert haz_fc.units == haz_kwargs["units"] + assert haz_fc.centroids == haz_kwargs["centroids"] + npt.assert_array_equal(haz_fc.event_id, haz_kwargs["event_id"]) + npt.assert_array_equal(haz_fc.frequency, haz_kwargs["frequency"]) + assert haz_fc.frequency_unit == haz_kwargs["frequency_unit"] + npt.assert_array_equal(haz_fc.event_name, haz_kwargs["event_name"]) + npt.assert_array_equal(haz_fc.date, haz_kwargs["date"]) + npt.assert_array_equal(haz_fc.orig, haz_kwargs["orig"]) + npt.assert_array_equal( + haz_fc.intensity.todense(), haz_kwargs["intensity"].todense() + ) + npt.assert_array_equal(haz_fc.fraction.todense(), haz_kwargs["fraction"].todense()) + + +def test_from_hazard(dummy_hazard): + lead_time = np.array( + ["2024-01-01T00:00:00", "2024-01-01T00:01:00"], dtype="datetime64[s]" + ) + member = np.array([0, 1]) + haz_fc_from_haz = HazardForecast.from_hazard( + dummy_hazard, lead_time=lead_time, member=member + ) + + assert isinstance(haz_fc_from_haz, HazardForecast) + npt.assert_array_equal(haz_fc_from_haz.lead_time, lead_time) + npt.assert_array_equal(haz_fc_from_haz.member, member) + assert haz_fc_from_haz.haz_type == dummy_hazard.haz_type + assert haz_fc_from_haz.pool == dummy_hazard.pool + assert haz_fc_from_haz.units == dummy_hazard.units + assert haz_fc_from_haz.centroids == dummy_hazard.centroids + npt.assert_array_equal(haz_fc_from_haz.event_id, dummy_hazard.event_id) + npt.assert_array_equal(haz_fc_from_haz.frequency, dummy_hazard.frequency) + assert haz_fc_from_haz.frequency_unit == dummy_hazard.frequency_unit + npt.assert_array_equal(haz_fc_from_haz.event_name, dummy_hazard.event_name) + npt.assert_array_equal(haz_fc_from_haz.date, dummy_hazard.date) + npt.assert_array_equal(haz_fc_from_haz.orig, dummy_hazard.orig) npt.assert_array_equal( - haz_fc.intensity.todense(), hazard_kwargs_fixture["intensity"].todense() + haz_fc_from_haz.intensity.todense(), dummy_hazard.intensity.todense() ) npt.assert_array_equal( - haz_fc.fraction.todense(), hazard_kwargs_fixture["fraction"].todense() + haz_fc_from_haz.fraction.todense(), dummy_hazard.fraction.todense() ) From dfdecd97a0cf1a83156f9c95c2cecfe15414d98e Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 14:10:29 +0100 Subject: [PATCH 15/19] Fix haz_kwargs in test hazards base --- climada/hazard/test/test_base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/climada/hazard/test/test_base.py b/climada/hazard/test/test_base.py index b5394f79f6..ea607893dd 100644 --- a/climada/hazard/test/test_base.py +++ b/climada/hazard/test/test_base.py @@ -67,8 +67,8 @@ def hazard_kwargs(): } -def dummy_hazard(haz_kwargs): - return Hazard(**haz_kwargs) +def dummy_hazard(): + return Hazard(**hazard_kwargs()) class TestLoader(unittest.TestCase): From 58806695f997fc28ed2dd3c9abe6554661863543 Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 14:11:00 +0100 Subject: [PATCH 16/19] Correct class atr error in from_hazard --- climada/hazard/forecast.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/climada/hazard/forecast.py b/climada/hazard/forecast.py index 8b192db0fd..7fb6e12bca 100644 --- a/climada/hazard/forecast.py +++ b/climada/hazard/forecast.py @@ -53,7 +53,7 @@ def __init__( super().__init__(lead_time=lead_time, member=member, **hazard_kwargs) @classmethod - def from_hazard(self, hazard: Hazard): + def from_hazard(cls, hazard: Hazard, lead_time: np.ndarray, member: np.ndarray): """ Create a HazardForecast object from a Hazard object. @@ -69,8 +69,8 @@ def from_hazard(self, hazard: Hazard): but with lead_time and member attributes set from instance of HazardForecast. """ return cls( - lead_time=self.lead_time, - member=self.member, + lead_time=lead_time, + member=member, haz_type=hazard.haz_type, pool=hazard.pool, units=hazard.units, From 71339afd727fcde62993220fed63e36ab4d2b49b Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 14:29:12 +0100 Subject: [PATCH 17/19] add member leadtime and forecast as fixtures --- climada/hazard/test/test_forecast.py | 37 +++++++++++++++++----------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/climada/hazard/test/test_forecast.py b/climada/hazard/test/test_forecast.py index 764c5c0433..c2a9a64909 100644 --- a/climada/hazard/test/test_forecast.py +++ b/climada/hazard/test/test_forecast.py @@ -21,6 +21,7 @@ import numpy as np import numpy.testing as npt +import pandas as pd import pytest from climada.hazard.base import Hazard @@ -40,21 +41,33 @@ def dummy_hazard(haz_kwargs): return Hazard(**haz_kwargs) -def test_init_hazard_forecast(haz_kwargs): - haz_fc = HazardForecast( - lead_time=np.array( - ["2024-01-01T00:00:00", "2024-01-01T00:01:00"], dtype="datetime64[s]" - ), - member=np.array([0, 1]), +@pytest.fixture +def lead_time(): + return pd.date_range("2000-01-01", "2000-01-02", periods=6).to_numpy() + + +@pytest.fixture +def member(): + return np.arange(6) + + +@pytest.fixture +def haz_fc(lead_time, member, haz_kwargs): + return HazardForecast( + lead_time=lead_time, + member=member, **haz_kwargs, ) + + +def test_init_hazard_forecast(haz_fc, member, lead_time, haz_kwargs): assert isinstance(haz_fc, HazardForecast) npt.assert_array_equal( haz_fc.lead_time, - np.array(["2024-01-01T00:00:00", "2024-01-01T00:01:00"], dtype="datetime64[s]"), + lead_time, ) - assert haz_fc.lead_time.dtype == "datetime64[s]" - npt.assert_array_equal(haz_fc.member, np.array([0, 1])) + assert haz_fc.lead_time.dtype == lead_time.dtype + npt.assert_array_equal(haz_fc.member, member) assert haz_fc.haz_type == haz_kwargs["haz_type"] assert haz_fc.pool == haz_kwargs["pool"] assert haz_fc.units == haz_kwargs["units"] @@ -71,11 +84,7 @@ def test_init_hazard_forecast(haz_kwargs): npt.assert_array_equal(haz_fc.fraction.todense(), haz_kwargs["fraction"].todense()) -def test_from_hazard(dummy_hazard): - lead_time = np.array( - ["2024-01-01T00:00:00", "2024-01-01T00:01:00"], dtype="datetime64[s]" - ) - member = np.array([0, 1]) +def test_from_hazard(lead_time, member, dummy_hazard): haz_fc_from_haz = HazardForecast.from_hazard( dummy_hazard, lead_time=lead_time, member=member ) From bb51bf32756735b9e92757349f2402af4c659bbd Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 14:39:54 +0100 Subject: [PATCH 18/19] Add HazardForecast.select test --- climada/hazard/test/test_forecast.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/climada/hazard/test/test_forecast.py b/climada/hazard/test/test_forecast.py index c2a9a64909..e3f0dde17f 100644 --- a/climada/hazard/test/test_forecast.py +++ b/climada/hazard/test/test_forecast.py @@ -110,6 +110,15 @@ def test_from_hazard(lead_time, member, dummy_hazard): ) +def test_hazard_forecast_select(haz_fc, lead_time, member): + """Check if Hazard.select works on the derived class""" + haz_fc_select = haz_fc.select(event_id=[4, 1]) + # NOTE: Events keep their original order + npt.assert_array_equal(haz_fc_select.event_id, haz_fc.event_id[np.array([3, 0])]) + npt.assert_array_equal(haz_fc_select.member, member[np.array([3, 0])]) + npt.assert_array_equal(haz_fc_select.lead_time, lead_time[np.array([3, 0])]) + + @pytest.fixture def hazard(): return Hazard() From ee3a1bd7b693eca5f5653cfdbd4f3761113d9f35 Mon Sep 17 00:00:00 2001 From: luseverin Date: Mon, 8 Dec 2025 14:54:21 +0100 Subject: [PATCH 19/19] Readd test hazardForecast.select removed during merge --- climada/hazard/test/test_forecast.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/climada/hazard/test/test_forecast.py b/climada/hazard/test/test_forecast.py index cd4acda21a..e47ca00351 100644 --- a/climada/hazard/test/test_forecast.py +++ b/climada/hazard/test/test_forecast.py @@ -88,3 +88,12 @@ def test_from_hazard(lead_time, member, hazard, haz_kwargs): npt.assert_array_equal(haz_fc_from_haz.lead_time, lead_time) npt.assert_array_equal(haz_fc_from_haz.member, member) assert_hazard_kwargs(haz_fc_from_haz, **haz_kwargs) + + +def test_hazard_forecast_select(haz_fc, lead_time, member): + """Check if Hazard.select works on the derived class""" + haz_fc_select = haz_fc.select(event_id=[4, 1]) + # NOTE: Events keep their original order + npt.assert_array_equal(haz_fc_select.event_id, haz_fc.event_id[np.array([3, 0])]) + npt.assert_array_equal(haz_fc_select.member, member[np.array([3, 0])]) + npt.assert_array_equal(haz_fc_select.lead_time, lead_time[np.array([3, 0])])