From cf53a23e53c52d9e45bc40a7b16eabfd0e33537a Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 22 Sep 2025 10:43:50 +0200 Subject: [PATCH 1/9] Fix error message --- flixopt/features.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/features.py b/flixopt/features.py index eb954944b..e24758074 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -616,7 +616,7 @@ def compute_consecutive_hours_in_state( if len(hours_per_timestep) < nr_of_indexes_with_consecutive_ones: raise ValueError( f'When trying to calculate the consecutive duration, the length of the last duration ' - f'({len(nr_of_indexes_with_consecutive_ones)}) is longer than the provided hours_per_timestep ({len(hours_per_timestep)}), ' + f'({nr_of_indexes_with_consecutive_ones}) is longer than the provided hours_per_timestep ({len(hours_per_timestep)}), ' f'as {binary_values=}' ) From 3a9e636a6ef06f5e1d06cd291ac8719e99fd826a Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 22 Sep 2025 10:44:34 +0200 Subject: [PATCH 2/9] Fix Error in ShareAllocationModel that lead to total max to not get applied when total min was not present --- flixopt/features.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/features.py b/flixopt/features.py index e24758074..14122bb4a 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -945,7 +945,7 @@ def __init__( # Parameters self._shares_are_time_series = shares_are_time_series - self._total_max = total_max if total_min is not None else np.inf + self._total_max = total_max if total_max is not None else np.inf self._total_min = total_min if total_min is not None else -np.inf self._max_per_hour = max_per_hour if max_per_hour is not None else np.inf self._min_per_hour = min_per_hour if min_per_hour is not None else -np.inf From 4c999e524efa1001f5477b39f3201e2e8d952ce5 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 22 Sep 2025 10:45:05 +0200 Subject: [PATCH 3/9] Fix: compression level when saviong dataset --- flixopt/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/io.py b/flixopt/io.py index 35d927136..d08c0a1b5 100644 --- a/flixopt/io.py +++ b/flixopt/io.py @@ -234,7 +234,7 @@ def save_dataset_to_netcdf( path, encoding=None if not apply_encoding - else {data_var: {'zlib': True, 'complevel': 5} for data_var in ds.data_vars}, + else {data_var: {'zlib': True, 'complevel': compression} for data_var in ds.data_vars}, ) From 50ee73b2894926def89cb2af3798ae767add2e3c Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 22 Sep 2025 10:45:28 +0200 Subject: [PATCH 4/9] Fix: Getter and setter for COP of HeatPumpWithSource --- flixopt/linear_converters.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/flixopt/linear_converters.py b/flixopt/linear_converters.py index 7f3169c7d..fc75227ee 100644 --- a/flixopt/linear_converters.py +++ b/flixopt/linear_converters.py @@ -572,13 +572,17 @@ def __init__( @property def COP(self): # noqa: N802 - return self.conversion_factors[0][self.Q_th.label] + return self.conversion_factors[0][self.P_el.label] @COP.setter def COP(self, value): # noqa: N802 check_bounds(value, 'COP', self.label_full, 1, 20) - self.conversion_factors[0][self.Q_th.label] = value - self.conversion_factors[1][self.Q_th.label] = value / (value - 1) + # electricity equation: COP * P_el == 1 * Q_th + self.conversion_factors[0][self.P_el.label] = value + self.conversion_factors[0][self.Q_th.label] = 1 + # heat source equation: (COP/(COP-1)) * Q_ab == 1 * Q_th -> Q_ab = Q_th*(COP-1)/COP + self.conversion_factors[1][self.Q_ab.label] = value / (value - 1) + self.conversion_factors[1][self.Q_th.label] = 1 def check_bounds( From a139c24f28a9a3473123bac799f49d5afb8a796b Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 22 Sep 2025 10:47:48 +0200 Subject: [PATCH 5/9] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e70cf08cb..f02d192e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,9 @@ Until here --> - Fix error handling in network visualization if networkx is not installed. - Fix broken links in docs. - Fix missing args in docstrings in `plotting.py`, `solvers.py`, and `core.py`. +- Fix COP getter and setter of `HeatPumpWithSource` returning and setting wrong conversion factors. +- Fix custom compression levels in `io.save_dataset_to_netcdf` +- Fix `total_max` did not work when total min was not used. ### Known Issues From dcabd8095b4f9ced1b9a66068fba96b5174154bd Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 22 Sep 2025 11:45:20 +0200 Subject: [PATCH 6/9] Fix Test --- tests/test_on_hours_computation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_on_hours_computation.py b/tests/test_on_hours_computation.py index 38b340d57..8fc406a85 100644 --- a/tests/test_on_hours_computation.py +++ b/tests/test_on_hours_computation.py @@ -42,7 +42,7 @@ def test_compute_duration(self, binary_values, hours_per_timestep, expected): ) def test_compute_duration_raises_error(self, binary_values, hours_per_timestep): """Test error conditions.""" - with pytest.raises(TypeError): + with pytest.raises(ValueError): ConsecutiveStateModel.compute_consecutive_hours_in_state(binary_values, hours_per_timestep) From c62fecc55e65aa782cb05ebea36be0fd88883db7 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 22 Sep 2025 11:50:23 +0200 Subject: [PATCH 7/9] Add extra check in HeatPumpWithSource --- flixopt/linear_converters.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/flixopt/linear_converters.py b/flixopt/linear_converters.py index fc75227ee..6fdd30cda 100644 --- a/flixopt/linear_converters.py +++ b/flixopt/linear_converters.py @@ -570,6 +570,9 @@ def __init__( self.Q_ab = Q_ab self.Q_th = Q_th + if np.any(np.asarray(self.COP) <= 1): + raise ValueError(f'{self.label_full}.COP must be strictly > 1 for HeatPumpWithSource.') + @property def COP(self): # noqa: N802 return self.conversion_factors[0][self.P_el.label] @@ -577,6 +580,8 @@ def COP(self): # noqa: N802 @COP.setter def COP(self, value): # noqa: N802 check_bounds(value, 'COP', self.label_full, 1, 20) + if np.any(np.asarray(self.COP) <= 1): + raise ValueError(f'{self.label_full}.COP must be strictly > 1 for HeatPumpWithSource.') # electricity equation: COP * P_el == 1 * Q_th self.conversion_factors[0][self.P_el.label] = value self.conversion_factors[0][self.Q_th.label] = 1 From 3c0574286f43634733a5cf13df2bb6f651e89601 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 22 Sep 2025 11:51:09 +0200 Subject: [PATCH 8/9] Add CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f02d192e0..3c4f60820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ Until here --> ## [Unreleased] ### Added +- Extra Check for HeatPumpWithSource.COP to be strictly > 1 to avoid division by zero ### Changed - Greatly improved docstrings and documentation of all public classes From 02bba03da836b60fdc470bff6078029fdff7ee97 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 22 Sep 2025 11:51:58 +0200 Subject: [PATCH 9/9] Fix inline comment --- tests/test_on_hours_computation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_on_hours_computation.py b/tests/test_on_hours_computation.py index 8fc406a85..cd0e637d0 100644 --- a/tests/test_on_hours_computation.py +++ b/tests/test_on_hours_computation.py @@ -67,7 +67,7 @@ class TestComputePreviousOnStates: ([np.array([0.1, 0, 0.3]), None, np.array([0, 0, 0])], np.array([1, 0, 1])), ([np.array([0, 0, 0]), np.array([0, 1, 0])], np.array([0, 1, 0])), ([np.array([0.1, 0, 0]), np.array([0, 0, 0.2])], np.array([1, 0, 1])), - # Case 6: Mix of None, 1D and 2D arrays + # Case 6: Mix of None and 1D arrays ([None, np.array([0, 0, 0]), np.array([0, 1, 0]), np.array([0, 0, 0])], np.array([0, 1, 0])), ([np.array([0, 0, 0]), None, np.array([0, 0, 0]), np.array([0, 0, 0])], np.array([0, 0, 0])), ],