From 1220e6fe36405c78075df8f41d034d56c2c9636d Mon Sep 17 00:00:00 2001 From: tolga-akan Date: Mon, 9 Feb 2026 14:06:04 +0100 Subject: [PATCH 1/7] heat transfer coefficient is read from discharge efficiency --- src/mesido/esdl/esdl_heat_model.py | 13 ++++++++++++- .../component_library/milp/heat/heat_buffer.py | 2 +- ...t_case_small_network_with_ates_buffer_geo.esdl | 2 +- tests/test_max_size_and_optional_assets.py | 15 +++++++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/mesido/esdl/esdl_heat_model.py b/src/mesido/esdl/esdl_heat_model.py index 40b1f35a7..02cdb08aa 100644 --- a/src/mesido/esdl/esdl_heat_model.py +++ b/src/mesido/esdl/esdl_heat_model.py @@ -428,12 +428,23 @@ def convert_heat_buffer(self, asset: Asset) -> Tuple[Type[HeatBuffer], MODIFIERS else 10.0e6 ) + # Todo: Currently esdl does not have "heatTransferCoefficient" attribute for + # heat_buffer asset. Temporarily we are using "dischargeEfficiency" attribute + # of HeatStorage asset attribute in ESDL to define heat_transfer_coeff. + # Once, "heatTransferCoefficient" attribute is added into esdl, + # we can update this part. + heat_transfer_coeff = ( + asset.attributes.get("heatTransferCoefficient") + or asset.attributes.get("dischargeEfficiency") + or 1.0 + ) + q_nominal = self._get_connected_q_nominal(asset) modifiers = dict( height=r, radius=r, - heat_transfer_coeff=1.0, + heat_transfer_coeff=heat_transfer_coeff, min_fraction_tank_volume=min_fraction_tank_volume, Stored_heat=dict(min=min_heat, max=max_heat), Heat_buffer=dict(min=-hfr_discharge_max, max=hfr_charge_max), diff --git a/src/mesido/pycml/component_library/milp/heat/heat_buffer.py b/src/mesido/pycml/component_library/milp/heat/heat_buffer.py index d9b700876..69e69d675 100644 --- a/src/mesido/pycml/component_library/milp/heat/heat_buffer.py +++ b/src/mesido/pycml/component_library/milp/heat/heat_buffer.py @@ -32,7 +32,7 @@ def __init__(self, name, **modifiers): self.component_type = "heat_buffer" - self.heat_transfer_coeff = 1.0 + self.heat_transfer_coeff = nan self.height = 5.0 self.radius = 10.0 self.volume = math.pi * self.radius**2 * self.height diff --git a/tests/models/test_case_small_network_with_ates_buffer_geo/model/test_case_small_network_with_ates_buffer_geo.esdl b/tests/models/test_case_small_network_with_ates_buffer_geo/model/test_case_small_network_with_ates_buffer_geo.esdl index 10bb4d086..a41a0822f 100644 --- a/tests/models/test_case_small_network_with_ates_buffer_geo/model/test_case_small_network_with_ates_buffer_geo.esdl +++ b/tests/models/test_case_small_network_with_ates_buffer_geo/model/test_case_small_network_with_ates_buffer_geo.esdl @@ -401,7 +401,7 @@ - + diff --git a/tests/test_max_size_and_optional_assets.py b/tests/test_max_size_and_optional_assets.py index 01a263696..444150aef 100644 --- a/tests/test_max_size_and_optional_assets.py +++ b/tests/test_max_size_and_optional_assets.py @@ -30,6 +30,7 @@ def test_max_size_and_aggr_count(self): assets should not be placed by the optmizer because of heat losses. Checks: + - Check that heat_transfer_coeff can be read from esdl - Check that source 1 is utilized and also placed - Check that source 2 is utilized and placed - Check that the geothermal source is not placed @@ -60,6 +61,20 @@ def test_max_size_and_aggr_count(self): results = solution.extract_results() parameters = solution.parameters(0) + # Test that heat_transfer_coeff can be read from esdl + esdl_asset = solution.esdl_assets[solution.esdl_asset_name_to_id_map["HeatStorage_74c1"]] + try: + heat_transfer_coeff = esdl_asset.attributes["heatTransferCoefficient"] + except KeyError: + # Currently, "heatTransferCoefficient" attribute is not available in esdl. + # We are temporariliy using "dischargeEfficiency" attribute in esdl + # to define heat_transfer_coeff + heat_transfer_coeff = esdl_asset.attributes["dischargeEfficiency"] + + np.testing.assert_allclose( + parameters["HeatStorage_74c1.heat_transfer_coeff"], heat_transfer_coeff + ) + # Producer 1 and geothermal source should not produce due to higher cost # Producer 2 should produce heat_1 = results["HeatProducer_1.Heat_source"] From 2035f7faaffd3a438c150981d8462e7346b80484 Mon Sep 17 00:00:00 2001 From: tolga-akan Date: Mon, 9 Feb 2026 14:09:16 +0100 Subject: [PATCH 2/7] change log is updated --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d604ff29..569b08aae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ -# [Unreleased-main] - 2026-02-05 +# [Unreleased-main] - 2026-02-09 ## Added -- xxx +- heat_transfer_coeff variable of HeatStorage is read from dischargeEfficiency attribute in esdl ## Changed - xxx From 5f0835372aec19f36d84e70891c277df431cc902 Mon Sep 17 00:00:00 2001 From: tolga-akan Date: Fri, 20 Feb 2026 22:00:42 +0100 Subject: [PATCH 3/7] "dischargeEfficiency" represents the percentage of stored heat that is lost per day --- heat_producer) | 0 src/mesido/esdl/esdl_heat_model.py | 18 +++++++-------- .../milp/heat/heat_buffer.py | 10 +++++---- ...se_small_network_with_ates_buffer_geo.esdl | 2 +- tests/test_max_size_and_optional_assets.py | 22 +++++++++---------- tests/test_warmingup_unit_cases.py | 4 ++-- 6 files changed, 28 insertions(+), 28 deletions(-) delete mode 100644 heat_producer) diff --git a/heat_producer) b/heat_producer) deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/mesido/esdl/esdl_heat_model.py b/src/mesido/esdl/esdl_heat_model.py index 02cdb08aa..1c8e007e9 100644 --- a/src/mesido/esdl/esdl_heat_model.py +++ b/src/mesido/esdl/esdl_heat_model.py @@ -428,15 +428,13 @@ def convert_heat_buffer(self, asset: Asset) -> Tuple[Type[HeatBuffer], MODIFIERS else 10.0e6 ) - # Todo: Currently esdl does not have "heatTransferCoefficient" attribute for - # heat_buffer asset. Temporarily we are using "dischargeEfficiency" attribute - # of HeatStorage asset attribute in ESDL to define heat_transfer_coeff. - # Once, "heatTransferCoefficient" attribute is added into esdl, - # we can update this part. - heat_transfer_coeff = ( - asset.attributes.get("heatTransferCoefficient") - or asset.attributes.get("dischargeEfficiency") - or 1.0 + # The asset attribute "dischargeEfficiency" represents the percentage of stored heat + # that is lost per day. If this attribute is not provided, a default heat loss rate + # of 1% per day is used. The value is converted to a per‑second loss factor. + heat_loss_efficiency = ( + asset.attributes.get("dischargeEfficiency") / (24.0 * 3600.0) + if asset.attributes.get("dischargeEfficiency") + else 0.01 / (24.0 * 3600.0) ) q_nominal = self._get_connected_q_nominal(asset) @@ -444,7 +442,7 @@ def convert_heat_buffer(self, asset: Asset) -> Tuple[Type[HeatBuffer], MODIFIERS modifiers = dict( height=r, radius=r, - heat_transfer_coeff=heat_transfer_coeff, + heat_loss_efficiency=heat_loss_efficiency, min_fraction_tank_volume=min_fraction_tank_volume, Stored_heat=dict(min=min_heat, max=max_heat), Heat_buffer=dict(min=-hfr_discharge_max, max=hfr_charge_max), diff --git a/src/mesido/pycml/component_library/milp/heat/heat_buffer.py b/src/mesido/pycml/component_library/milp/heat/heat_buffer.py index 69e69d675..95afc4a26 100644 --- a/src/mesido/pycml/component_library/milp/heat/heat_buffer.py +++ b/src/mesido/pycml/component_library/milp/heat/heat_buffer.py @@ -32,11 +32,10 @@ def __init__(self, name, **modifiers): self.component_type = "heat_buffer" - self.heat_transfer_coeff = nan self.height = 5.0 self.radius = 10.0 self.volume = math.pi * self.radius**2 * self.height - self.heat_loss_coeff = 2 * self.heat_transfer_coeff / (self.radius * self.rho * self.cp) + self.heat_loss_efficiency = nan # The hot/cold tank can have a lower bound on its volume. # Meaning that they might always be, for e.g., 5% full. self.min_fraction_tank_volume = 0.05 @@ -78,7 +77,9 @@ def __init__(self, name, **modifiers): # 10.0, we aim for a state vector entry of ~0.1 (instead of 1.0) self._heat_loss_error_to_state_factor = 10.0 self._nominal_heat_loss = ( - self._nominal_stored_heat * self.heat_loss_coeff * self._heat_loss_error_to_state_factor + self._nominal_stored_heat + * self.heat_loss_efficiency + * self._heat_loss_error_to_state_factor ) self.add_variable(Variable, "Heat_loss", min=0.0, nominal=self._nominal_heat_loss) @@ -91,7 +92,8 @@ def __init__(self, name, **modifiers): / self._heat_loss_eq_nominal_buf ) self.add_equation( - (self.Heat_loss - self.Stored_heat * self.heat_loss_coeff) / self._nominal_heat_loss + (self.Heat_loss - self.Stored_heat * self.heat_loss_efficiency) + / self._nominal_heat_loss ) self.add_equation((self.Heat_flow - self.Heat_buffer) / self.Heat_nominal) diff --git a/tests/models/test_case_small_network_with_ates_buffer_geo/model/test_case_small_network_with_ates_buffer_geo.esdl b/tests/models/test_case_small_network_with_ates_buffer_geo/model/test_case_small_network_with_ates_buffer_geo.esdl index a41a0822f..75a5f566b 100644 --- a/tests/models/test_case_small_network_with_ates_buffer_geo/model/test_case_small_network_with_ates_buffer_geo.esdl +++ b/tests/models/test_case_small_network_with_ates_buffer_geo/model/test_case_small_network_with_ates_buffer_geo.esdl @@ -401,7 +401,7 @@ - + diff --git a/tests/test_max_size_and_optional_assets.py b/tests/test_max_size_and_optional_assets.py index 444150aef..4cff6919e 100644 --- a/tests/test_max_size_and_optional_assets.py +++ b/tests/test_max_size_and_optional_assets.py @@ -30,7 +30,8 @@ def test_max_size_and_aggr_count(self): assets should not be placed by the optmizer because of heat losses. Checks: - - Check that heat_transfer_coeff can be read from esdl + - Check that heat_loss_efficiency of buffer is read from esdl and used to calculate + the heat loss of buffer - Check that source 1 is utilized and also placed - Check that source 2 is utilized and placed - Check that the geothermal source is not placed @@ -61,18 +62,17 @@ def test_max_size_and_aggr_count(self): results = solution.extract_results() parameters = solution.parameters(0) - # Test that heat_transfer_coeff can be read from esdl + # Test that heat_loss_efficiency can be read from esdl + # and incorporated into heat loss calculation of buffer esdl_asset = solution.esdl_assets[solution.esdl_asset_name_to_id_map["HeatStorage_74c1"]] - try: - heat_transfer_coeff = esdl_asset.attributes["heatTransferCoefficient"] - except KeyError: - # Currently, "heatTransferCoefficient" attribute is not available in esdl. - # We are temporariliy using "dischargeEfficiency" attribute in esdl - # to define heat_transfer_coeff - heat_transfer_coeff = esdl_asset.attributes["dischargeEfficiency"] - np.testing.assert_allclose( - parameters["HeatStorage_74c1.heat_transfer_coeff"], heat_transfer_coeff + parameters["HeatStorage_74c1.heat_loss_efficiency"], + esdl_asset.attributes["dischargeEfficiency"] / (24.0 * 3600.0), + ) + np.testing.assert_allclose( + results["HeatStorage_74c1.Heat_loss"], + results["HeatStorage_74c1.Stored_heat"] + * parameters["HeatStorage_74c1.heat_loss_efficiency"], ) # Producer 1 and geothermal source should not produce due to higher cost diff --git a/tests/test_warmingup_unit_cases.py b/tests/test_warmingup_unit_cases.py index 3a53f1665..8e4eb48a6 100644 --- a/tests/test_warmingup_unit_cases.py +++ b/tests/test_warmingup_unit_cases.py @@ -171,9 +171,9 @@ def test_3a(self): results[f"{buffer}.Heat_buffer"], ) # buffer should have positive heat loss - assert parameters[f"{buffer}.heat_loss_coeff"] > 0.0 + assert parameters[f"{buffer}.heat_loss_efficiency"] > 0.0 np.testing.assert_allclose( - results[f"{buffer}.Stored_heat"] * parameters[f"{buffer}.heat_loss_coeff"], + results[f"{buffer}.Stored_heat"] * parameters[f"{buffer}.heat_loss_efficiency"], results[f"{buffer}.Heat_loss"], ) np.testing.assert_allclose( From 87da386e628d4d29dd01c580dfc27d7fb65b4781 Mon Sep 17 00:00:00 2001 From: tolga-akan Date: Fri, 6 Mar 2026 18:44:44 +0100 Subject: [PATCH 4/7] final checks done --- CHANGELOG.md | 2 +- src/mesido/esdl/esdl_heat_model.py | 4 ++-- tests/test_max_size_and_optional_assets.py | 8 +++----- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c794ab624..31ae62320 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Added - Parsing of ensemble profiles when using input profiles from csv. - Maximum profile constraint for PV asset is considered in PV sizing -- heat_transfer_coeff variable of HeatStorage is read from dischargeEfficiency attribute in esdl +- heat_loss_efficiency of HeatStorage is read from dischargeEfficiency attribute in esdl ## Changed diff --git a/src/mesido/esdl/esdl_heat_model.py b/src/mesido/esdl/esdl_heat_model.py index 1c8e007e9..ff60adc07 100644 --- a/src/mesido/esdl/esdl_heat_model.py +++ b/src/mesido/esdl/esdl_heat_model.py @@ -428,8 +428,8 @@ def convert_heat_buffer(self, asset: Asset) -> Tuple[Type[HeatBuffer], MODIFIERS else 10.0e6 ) - # The asset attribute "dischargeEfficiency" represents the percentage of stored heat - # that is lost per day. If this attribute is not provided, a default heat loss rate + # The asset attribute "dischargeEfficiency" represents the fraction of stored heat + # that is lost per day. If this attribute is not provided in esdl, a default heat loss rate # of 1% per day is used. The value is converted to a per‑second loss factor. heat_loss_efficiency = ( asset.attributes.get("dischargeEfficiency") / (24.0 * 3600.0) diff --git a/tests/test_max_size_and_optional_assets.py b/tests/test_max_size_and_optional_assets.py index 4cff6919e..f2aa7f332 100644 --- a/tests/test_max_size_and_optional_assets.py +++ b/tests/test_max_size_and_optional_assets.py @@ -27,11 +27,10 @@ def test_max_size_and_aggr_count(self): source will be placed due to the minimization of the cost (which cost is this?, I assume operational cost) and installation cost. The placement behaviour is further tested in a second case by adding an optional ates and buffer. However, these 2 additional optional - assets should not be placed by the optmizer because of heat losses. + assets should not be placed by the optImizer because of heat losses. Checks: - - Check that heat_loss_efficiency of buffer is read from esdl and used to calculate - the heat loss of buffer + - Check the heat loss of buffer calculation - Check that source 1 is utilized and also placed - Check that source 2 is utilized and placed - Check that the geothermal source is not placed @@ -62,8 +61,7 @@ def test_max_size_and_aggr_count(self): results = solution.extract_results() parameters = solution.parameters(0) - # Test that heat_loss_efficiency can be read from esdl - # and incorporated into heat loss calculation of buffer + # Test that if heat_loss_efficiency is incorporated into heat loss calculation of buffer esdl_asset = solution.esdl_assets[solution.esdl_asset_name_to_id_map["HeatStorage_74c1"]] np.testing.assert_allclose( parameters["HeatStorage_74c1.heat_loss_efficiency"], From 67b47396e0f6ddad6aec58b216a7239fb7cccd81 Mon Sep 17 00:00:00 2001 From: tolga-akan Date: Fri, 6 Mar 2026 18:54:38 +0100 Subject: [PATCH 5/7] typo --- tests/test_max_size_and_optional_assets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_max_size_and_optional_assets.py b/tests/test_max_size_and_optional_assets.py index f2aa7f332..d07e532c9 100644 --- a/tests/test_max_size_and_optional_assets.py +++ b/tests/test_max_size_and_optional_assets.py @@ -27,10 +27,10 @@ def test_max_size_and_aggr_count(self): source will be placed due to the minimization of the cost (which cost is this?, I assume operational cost) and installation cost. The placement behaviour is further tested in a second case by adding an optional ates and buffer. However, these 2 additional optional - assets should not be placed by the optImizer because of heat losses. + assets should not be placed by the optimizer because of heat losses. Checks: - - Check the heat loss of buffer calculation + - Check the buffer heat loss calculation - Check that source 1 is utilized and also placed - Check that source 2 is utilized and placed - Check that the geothermal source is not placed From 0a67d4decbe503d296c1fc5b4d7a708ee3912603 Mon Sep 17 00:00:00 2001 From: tolga-akan Date: Wed, 11 Mar 2026 12:00:55 +0100 Subject: [PATCH 6/7] PR comments update --- CHANGELOG.md | 2 +- src/mesido/esdl/esdl_heat_model.py | 4 +- .../milp/heat/heat_buffer.py | 11 ++- tests/models/unit_cases/case_3a/model/3a.esdl | 92 +++++++++---------- tests/test_max_size_and_optional_assets.py | 13 --- tests/test_warmingup_unit_cases.py | 12 ++- 6 files changed, 67 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 254ecab89..984001236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - Maximum profile constraint for PV asset is considered in PV sizing - Cater for input via esdl constraints to specify the upper limit for OPTIONAL assets in DTK - Initial implementation of adaptable pipe DN lower limit per pipe -- heat_loss_efficiency of HeatStorage is read from dischargeEfficiency attribute in esdl +- DischargeEfficiency is parsed from HeatStorage assets in ESDL to a heat loss coefficient ## Changed diff --git a/src/mesido/esdl/esdl_heat_model.py b/src/mesido/esdl/esdl_heat_model.py index ca8a2edae..ae29fb1cf 100644 --- a/src/mesido/esdl/esdl_heat_model.py +++ b/src/mesido/esdl/esdl_heat_model.py @@ -554,7 +554,7 @@ def convert_heat_buffer(self, asset: Asset) -> Tuple[Type[HeatBuffer], MODIFIERS # The asset attribute "dischargeEfficiency" represents the fraction of stored heat # that is lost per day. If this attribute is not provided in esdl, a default heat loss rate # of 1% per day is used. The value is converted to a per‑second loss factor. - heat_loss_efficiency = ( + heat_loss_coefficient = ( asset.attributes.get("dischargeEfficiency") / (24.0 * 3600.0) if asset.attributes.get("dischargeEfficiency") else 0.01 / (24.0 * 3600.0) @@ -565,7 +565,7 @@ def convert_heat_buffer(self, asset: Asset) -> Tuple[Type[HeatBuffer], MODIFIERS modifiers = dict( height=r, radius=r, - heat_loss_efficiency=heat_loss_efficiency, + heat_loss_coefficient=heat_loss_coefficient, min_fraction_tank_volume=min_fraction_tank_volume, Stored_heat=dict(min=min_heat, max=max_heat), Heat_buffer=dict(min=-hfr_discharge_max, max=hfr_charge_max), diff --git a/src/mesido/pycml/component_library/milp/heat/heat_buffer.py b/src/mesido/pycml/component_library/milp/heat/heat_buffer.py index 95afc4a26..68a1731ea 100644 --- a/src/mesido/pycml/component_library/milp/heat/heat_buffer.py +++ b/src/mesido/pycml/component_library/milp/heat/heat_buffer.py @@ -35,7 +35,12 @@ def __init__(self, name, **modifiers): self.height = 5.0 self.radius = 10.0 self.volume = math.pi * self.radius**2 * self.height - self.heat_loss_efficiency = nan + self.heat_loss_coefficient = nan + # self.heat_transfer_coeff = 1.0 + # self.heat_loss_coefficient = ( + # 2 * self.heat_transfer_coeff / (self.radius * self.rho * self.cp) + # ) + # The hot/cold tank can have a lower bound on its volume. # Meaning that they might always be, for e.g., 5% full. self.min_fraction_tank_volume = 0.05 @@ -78,7 +83,7 @@ def __init__(self, name, **modifiers): self._heat_loss_error_to_state_factor = 10.0 self._nominal_heat_loss = ( self._nominal_stored_heat - * self.heat_loss_efficiency + * self.heat_loss_coefficient * self._heat_loss_error_to_state_factor ) @@ -92,7 +97,7 @@ def __init__(self, name, **modifiers): / self._heat_loss_eq_nominal_buf ) self.add_equation( - (self.Heat_loss - self.Stored_heat * self.heat_loss_efficiency) + (self.Heat_loss - self.Stored_heat * self.heat_loss_coefficient) / self._nominal_heat_loss ) diff --git a/tests/models/unit_cases/case_3a/model/3a.esdl b/tests/models/unit_cases/case_3a/model/3a.esdl index 0cf389fe3..8e4010842 100644 --- a/tests/models/unit_cases/case_3a/model/3a.esdl +++ b/tests/models/unit_cases/case_3a/model/3a.esdl @@ -1,8 +1,8 @@ - + - + @@ -15,8 +15,8 @@ - - + + @@ -31,16 +31,16 @@ - - + + - + - - + + @@ -53,13 +53,13 @@ - + - - + + @@ -72,13 +72,13 @@ - + - - + + @@ -91,13 +91,13 @@ - + - - + + @@ -112,13 +112,13 @@ - - + + - - + + @@ -133,12 +133,12 @@ - + - + @@ -154,29 +154,29 @@ - + - + - + - + - + - - + + @@ -189,7 +189,7 @@ - + @@ -205,10 +205,10 @@ - - + + - + @@ -224,10 +224,10 @@ - - + + - + @@ -243,10 +243,10 @@ - - + + - + @@ -262,10 +262,10 @@ - - + + - + @@ -281,8 +281,8 @@ - - + + diff --git a/tests/test_max_size_and_optional_assets.py b/tests/test_max_size_and_optional_assets.py index d07e532c9..4954f6d0a 100644 --- a/tests/test_max_size_and_optional_assets.py +++ b/tests/test_max_size_and_optional_assets.py @@ -30,7 +30,6 @@ def test_max_size_and_aggr_count(self): assets should not be placed by the optimizer because of heat losses. Checks: - - Check the buffer heat loss calculation - Check that source 1 is utilized and also placed - Check that source 2 is utilized and placed - Check that the geothermal source is not placed @@ -61,18 +60,6 @@ def test_max_size_and_aggr_count(self): results = solution.extract_results() parameters = solution.parameters(0) - # Test that if heat_loss_efficiency is incorporated into heat loss calculation of buffer - esdl_asset = solution.esdl_assets[solution.esdl_asset_name_to_id_map["HeatStorage_74c1"]] - np.testing.assert_allclose( - parameters["HeatStorage_74c1.heat_loss_efficiency"], - esdl_asset.attributes["dischargeEfficiency"] / (24.0 * 3600.0), - ) - np.testing.assert_allclose( - results["HeatStorage_74c1.Heat_loss"], - results["HeatStorage_74c1.Stored_heat"] - * parameters["HeatStorage_74c1.heat_loss_efficiency"], - ) - # Producer 1 and geothermal source should not produce due to higher cost # Producer 2 should produce heat_1 = results["HeatProducer_1.Heat_source"] diff --git a/tests/test_warmingup_unit_cases.py b/tests/test_warmingup_unit_cases.py index 8e4eb48a6..2acc5f6fa 100644 --- a/tests/test_warmingup_unit_cases.py +++ b/tests/test_warmingup_unit_cases.py @@ -128,6 +128,7 @@ def test_3a(self): direction for the pipe connected to the buffer tank) - Check that the Heat_buffer & Heat_flow variable are set correctly - Check that the history for the buffer is set correctly at t=0 + - Check that heat_loss_coefficient is read from esdl and linked to buffer heat loss - Check that the heat loss is positive and as expected - Check that the Stored heat is the sum of (dis)charge and losses @@ -171,9 +172,16 @@ def test_3a(self): results[f"{buffer}.Heat_buffer"], ) # buffer should have positive heat loss - assert parameters[f"{buffer}.heat_loss_efficiency"] > 0.0 + assert parameters[f"{buffer}.heat_loss_coefficient"] > 0.0 + esdl_asset = heat_problem.esdl_assets[ + heat_problem.esdl_asset_name_to_id_map[f"{buffer}"] + ] np.testing.assert_allclose( - results[f"{buffer}.Stored_heat"] * parameters[f"{buffer}.heat_loss_efficiency"], + parameters[f"{buffer}.heat_loss_coefficient"], + esdl_asset.attributes["dischargeEfficiency"] / (24.0 * 3600.0), + ) + np.testing.assert_allclose( + results[f"{buffer}.Stored_heat"] * parameters[f"{buffer}.heat_loss_coefficient"], results[f"{buffer}.Heat_loss"], ) np.testing.assert_allclose( From 5a7200a480a909ae31c110109db7dc84ebe6c173 Mon Sep 17 00:00:00 2001 From: tolga-akan Date: Thu, 12 Mar 2026 13:21:31 +0100 Subject: [PATCH 7/7] comment update --- src/mesido/esdl/esdl_heat_model.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mesido/esdl/esdl_heat_model.py b/src/mesido/esdl/esdl_heat_model.py index ae29fb1cf..d81109658 100644 --- a/src/mesido/esdl/esdl_heat_model.py +++ b/src/mesido/esdl/esdl_heat_model.py @@ -552,8 +552,7 @@ def convert_heat_buffer(self, asset: Asset) -> Tuple[Type[HeatBuffer], MODIFIERS ) # The asset attribute "dischargeEfficiency" represents the fraction of stored heat - # that is lost per day. If this attribute is not provided in esdl, a default heat loss rate - # of 1% per day is used. The value is converted to a per‑second loss factor. + # that is lost per day. heat_loss_coefficient = ( asset.attributes.get("dischargeEfficiency") / (24.0 * 3600.0) if asset.attributes.get("dischargeEfficiency")