From f849db9d1e495e473dda10587a3ded85f0c83581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrik=20Sch=C3=B6nfeldt?= Date: Tue, 9 Jul 2024 15:35:55 +0200 Subject: [PATCH] Fix loss calculation for non-hourly time steps This one first slipped through due to too many irrelevant changes in the LP files. I now added a unit test that is independent of lp files. Note that the formula is more complicated but it just changes a value in the lp file, so it will not significantly increase computational time. --- .../solph/components/_generic_storage.py | 4 +- tests/lp_files/nonequidistant_timeindex.lp | 10 ++-- tests/test_components/test_storage.py | 50 +++++++++++++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 tests/test_components/test_storage.py diff --git a/src/oemof/solph/components/_generic_storage.py b/src/oemof/solph/components/_generic_storage.py index 0d03beee7..ab8141789 100644 --- a/src/oemof/solph/components/_generic_storage.py +++ b/src/oemof/solph/components/_generic_storage.py @@ -360,7 +360,7 @@ class GenericStorageBlock(ScalarBlock): Storage losses :attr:`om.Storage.losses[n, t]` .. math:: E_{loss}(t) = &E(t-1) \cdot - \beta(t)^{\tau(t)/(t_u)} \\ + 1 - (1 - \beta(t))^{\tau(t)/(t_u)} \\ &- \gamma(t)\cdot E_{nom} \cdot {\tau(t)/(t_u)}\\ &- \delta(t) \cdot {\tau(t)/(t_u)} @@ -515,7 +515,7 @@ def _storage_content_bound_rule(block, n, t): def _storage_losses_rule(block, n, t): expr = block.storage_content[n, t] * ( - n.loss_rate[t] ** m.timeincrement[t] + 1 - (1 - n.loss_rate[t]) ** m.timeincrement[t] ) expr += ( n.fixed_losses_relative[t] diff --git a/tests/lp_files/nonequidistant_timeindex.lp b/tests/lp_files/nonequidistant_timeindex.lp index c450ce7b2..702f1efab 100644 --- a/tests/lp_files/nonequidistant_timeindex.lp +++ b/tests/lp_files/nonequidistant_timeindex.lp @@ -165,27 +165,27 @@ c_e_GenericStorageBlock_losses(storage_2)_: c_e_GenericStorageBlock_losses(storage_3)_: -1 GenericStorageBlock_storage_losses(storage_3) -+0.010000000000000002 GenericStorageBlock_storage_content(storage_3) ++0.19 GenericStorageBlock_storage_content(storage_3) = 0 c_e_GenericStorageBlock_losses(storage_4)_: -1 GenericStorageBlock_storage_losses(storage_4) -+0.010000000000000002 GenericStorageBlock_storage_content(storage_4) ++0.19 GenericStorageBlock_storage_content(storage_4) = 0 c_e_GenericStorageBlock_losses(storage_5)_: -1 GenericStorageBlock_storage_losses(storage_5) -+0.31622776601683794 GenericStorageBlock_storage_content(storage_5) ++0.05131670194948623 GenericStorageBlock_storage_content(storage_5) = 0 c_e_GenericStorageBlock_losses(storage_6)_: -1 GenericStorageBlock_storage_losses(storage_6) -+0.31622776601683794 GenericStorageBlock_storage_content(storage_6) ++0.05131670194948623 GenericStorageBlock_storage_content(storage_6) = 0 c_e_GenericStorageBlock_losses(storage_7)_: -1 GenericStorageBlock_storage_losses(storage_7) -+0.31622776601683794 GenericStorageBlock_storage_content(storage_7) ++0.05131670194948623 GenericStorageBlock_storage_content(storage_7) = 0 c_e_GenericStorageBlock_balance(storage_0)_: diff --git a/tests/test_components/test_storage.py b/tests/test_components/test_storage.py new file mode 100644 index 000000000..cfcc904e1 --- /dev/null +++ b/tests/test_components/test_storage.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- + +import pytest +import numpy as np + +from oemof import solph + + +def test_relative_losses(): + cases = [ + {"number": 500, "interval": 2}, + {"number": 1000, "interval": 1}, + {"number": 2000, "interval": 0.5}, + ] + + for case in cases: + es = solph.EnergySystem( + timeindex=solph.create_time_index( + year=2023, number=case["number"], interval=case["interval"] + ), + infer_last_interval=True, + ) + + bus = solph.Bus("slack_bus", balanced=False) + es.add(bus) + + storage = solph.components.GenericStorage( + "storage", + inputs={bus: solph.Flow(variable_costs=1)}, + outputs={bus: solph.Flow(variable_costs=1)}, + nominal_storage_capacity=10, + initial_storage_level=1, + loss_rate=0.004125876075, # half life of one week + ) + es.add(storage) + + model = solph.Model(es) + model.solve("cbc") + + result = solph.processing.results(model)[(storage, None)]["sequences"][ + "storage_content" + ] + case["result"] = np.array(result) + + for i in range(500): + assert ( + cases[0]["result"][i] + == pytest.approx(cases[1]["result"][2 * i]) + == pytest.approx(cases[2]["result"][4 * i]) + )