From 3db580347368b7b5eac3ed08d7a1a98b66e4f706 Mon Sep 17 00:00:00 2001 From: Valentin Gebhart Date: Mon, 8 Dec 2025 10:43:02 +0100 Subject: [PATCH 1/6] define base class --- climada/util/forecast.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/climada/util/forecast.py b/climada/util/forecast.py index 84225c47fe..053bab8201 100644 --- a/climada/util/forecast.py +++ b/climada/util/forecast.py @@ -19,6 +19,19 @@ Define Forecast base class. """ +import numpy as np + class Forecast: - pass + def __init__(self, lead_time=None, member=None, *args, **kwargs): + if lead_time is None: + self.lead_time = np.array([]) + else: + self.lead_time = np.array(lead_time) + + if member is None: + self.member = np.array([]) + else: + self.member = member + + super().__init__(*args, **kwargs) From 40f9488716ae2393d63c68f92873ee8b9da290fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Evelyn=20M=C3=BChlhofer?= Date: Mon, 8 Dec 2025 10:52:40 +0100 Subject: [PATCH 2/6] tests from ai --- climada/util/test/test_forecast.py | 45 ++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/climada/util/test/test_forecast.py b/climada/util/test/test_forecast.py index 196573e583..ca3221e60b 100644 --- a/climada/util/test/test_forecast.py +++ b/climada/util/test/test_forecast.py @@ -18,3 +18,48 @@ Tests for Forecast base class. """ + +import numpy as np +import pytest + +from climada.util.forecast import Forecast + + +class TestForecastInit: + """Test Forecast initialization""" + + def test_init_with_none_values(self): + """Test initialization with None values for lead_time and member""" + forecast = Forecast(lead_time=None, member=None) + + # Check that lead_time is an empty numpy array + assert isinstance(forecast.lead_time, np.ndarray) + assert forecast.lead_time.size == 0 + + # Check that member is an empty numpy array + assert isinstance(forecast.member, np.ndarray) + assert forecast.member.size == 0 + + def test_init_with_empty_objects(self): + """Test initialization with empty objects for lead_time and member""" + forecast = Forecast(lead_time=[], member=[]) + + # Check that lead_time is an empty numpy array + assert isinstance(forecast.lead_time, np.ndarray) + assert forecast.lead_time.size == 0 + + # Check that member is a list (passed directly) + assert isinstance(forecast.member, list) + assert len(forecast.member) == 0 + + def test_init_default(self): + """Test initialization with no arguments (default behavior)""" + forecast = Forecast() + + # Check that lead_time is an empty numpy array + assert isinstance(forecast.lead_time, np.ndarray) + assert forecast.lead_time.size == 0 + + # Check that member is an empty numpy array + assert isinstance(forecast.member, np.ndarray) + assert forecast.member.size == 0 From da0c914e3d274b522b15a5c1dd082eb613df32bb Mon Sep 17 00:00:00 2001 From: Valentin Gebhart Date: Mon, 8 Dec 2025 11:09:49 +0100 Subject: [PATCH 3/6] add init test --- climada/util/forecast.py | 23 +++++++++++++---------- climada/util/test/test_forecast.py | 24 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/climada/util/forecast.py b/climada/util/forecast.py index 053bab8201..e9db862e56 100644 --- a/climada/util/forecast.py +++ b/climada/util/forecast.py @@ -23,15 +23,18 @@ class Forecast: - def __init__(self, lead_time=None, member=None, *args, **kwargs): - if lead_time is None: - self.lead_time = np.array([]) - else: - self.lead_time = np.array(lead_time) - - if member is None: - self.member = np.array([]) - else: - self.member = member + def __init__( + self, + lead_time: np.ndarray | None = None, + member: np.ndarray | None = None, + *args, + **kwargs, + ): + + self.lead_time = ( + np.asarray(lead_time) if lead_time is not None else np.array([]) + ) + + self.member = np.asarray(member) if member is not None else np.array([]) super().__init__(*args, **kwargs) diff --git a/climada/util/test/test_forecast.py b/climada/util/test/test_forecast.py index 196573e583..8286294097 100644 --- a/climada/util/test/test_forecast.py +++ b/climada/util/test/test_forecast.py @@ -18,3 +18,27 @@ Tests for Forecast base class. """ + +import numpy as np +import numpy.testing as npt +import pytest + +from climada.util.forecast import Forecast + + +def test_forecast_init(): + """Test initialization of Forecast class.""" + forecast = Forecast() + npt.assert_array_equal(forecast.lead_time, np.array([])) + npt.assert_array_equal(forecast.member, np.array([])) + + forecast = Forecast(member=np.array([1, 2])) + npt.assert_array_equal(forecast.member, np.array([1, 2]), strict=True) + + forecast = Forecast(lead_time=np.array([1, 2])) + npt.assert_array_equal(forecast.lead_time, np.array([1, 2]), strict=True) + + forecast = Forecast(lead_time=np.array([1, 2]), member=[3, 4]) + npt.assert_array_equal(forecast.lead_time, np.array([1, 2]), strict=True) + npt.assert_array_equal(forecast.member, np.array([3, 4]), strict=True) + assert isinstance(forecast.member, np.ndarray) From c376dbcf303347864c5e993d7a870862f7f68211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Evelyn=20M=C3=BChlhofer?= Date: Mon, 8 Dec 2025 11:25:43 +0100 Subject: [PATCH 4/6] docstrings and datetime tests --- climada/util/forecast.py | 21 +++++++++++++++++++++ climada/util/test/test_forecast.py | 9 +++++++++ 2 files changed, 30 insertions(+) diff --git a/climada/util/forecast.py b/climada/util/forecast.py index e9db862e56..a53c528d98 100644 --- a/climada/util/forecast.py +++ b/climada/util/forecast.py @@ -23,6 +23,18 @@ class Forecast: + """Mixin class for forecast data. + + Attributes + ---------- + lead_time : np.ndarray + Array of forecast lead times, given as datetime64 objects. + Represents the time points for which forecasts are made. + member : np.ndarray + Array of ensemble member identifiers, given as integers. + Represents different forecast ensemble members. + """ + def __init__( self, lead_time: np.ndarray | None = None, @@ -30,6 +42,15 @@ def __init__( *args, **kwargs, ): + """Initialize Forecast. + + Parameters + ---------- + lead_time : np.ndarray or None, optional + Forecast lead times. Default is empty array. + member : np.ndarray or None, optional + Ensemble member identifiers. Default is empty array. + """ self.lead_time = ( np.asarray(lead_time) if lead_time is not None else np.array([]) diff --git a/climada/util/test/test_forecast.py b/climada/util/test/test_forecast.py index 8286294097..4fae8a1375 100644 --- a/climada/util/test/test_forecast.py +++ b/climada/util/test/test_forecast.py @@ -42,3 +42,12 @@ def test_forecast_init(): npt.assert_array_equal(forecast.lead_time, np.array([1, 2]), strict=True) npt.assert_array_equal(forecast.member, np.array([3, 4]), strict=True) assert isinstance(forecast.member, np.ndarray) + + # Test with datetime64 including seconds + lead_times_seconds = np.array( + ["2024-01-01T00:00:00", "2024-01-01T00:01:00", "2024-01-01"], + dtype="datetime64[s]", + ) + forecast = Forecast(lead_time=lead_times_seconds, member=[1, 2, 3]) + npt.assert_array_equal(forecast.lead_time, lead_times_seconds, strict=True) + assert forecast.lead_time.dtype == np.dtype("datetime64[s]") From 379b4ab59db325eab094fe7b922b991d8c5635ea Mon Sep 17 00:00:00 2001 From: Valentin Gebhart Date: Mon, 8 Dec 2025 11:33:34 +0100 Subject: [PATCH 5/6] remove *args from forecast init --- climada/util/forecast.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/climada/util/forecast.py b/climada/util/forecast.py index a53c528d98..03b6c9ea62 100644 --- a/climada/util/forecast.py +++ b/climada/util/forecast.py @@ -39,7 +39,6 @@ def __init__( self, lead_time: np.ndarray | None = None, member: np.ndarray | None = None, - *args, **kwargs, ): """Initialize Forecast. @@ -58,4 +57,4 @@ def __init__( self.member = np.asarray(member) if member is not None else np.array([]) - super().__init__(*args, **kwargs) + super().__init__(**kwargs) From 855bd60ca43bcd47ac387fd027374bccd153ca5f Mon Sep 17 00:00:00 2001 From: Lukas Riedel <34276446+peanutfun@users.noreply.github.com> Date: Mon, 8 Dec 2025 11:38:06 +0100 Subject: [PATCH 6/6] Fix linter issues --- climada/util/forecast.py | 2 -- climada/util/test/test_forecast.py | 1 - 2 files changed, 3 deletions(-) diff --git a/climada/util/forecast.py b/climada/util/forecast.py index 03b6c9ea62..1fbd090db9 100644 --- a/climada/util/forecast.py +++ b/climada/util/forecast.py @@ -54,7 +54,5 @@ def __init__( self.lead_time = ( np.asarray(lead_time) if lead_time is not None else np.array([]) ) - self.member = np.asarray(member) if member is not None else np.array([]) - super().__init__(**kwargs) diff --git a/climada/util/test/test_forecast.py b/climada/util/test/test_forecast.py index 4fae8a1375..f500c4ba88 100644 --- a/climada/util/test/test_forecast.py +++ b/climada/util/test/test_forecast.py @@ -21,7 +21,6 @@ import numpy as np import numpy.testing as npt -import pytest from climada.util.forecast import Forecast