From d82e81f341aa9952c547c7c559da4253a1a779eb Mon Sep 17 00:00:00 2001 From: Santiago Patterson Date: Fri, 16 Jan 2026 14:15:40 +0100 Subject: [PATCH 01/11] Added the capability of loading profiles onto producers, using their port. --- .../controller_producer_mapper.py | 24 ++++++++++++++++++- .../assets/controller/controller_producer.py | 7 ++++++ .../entities/assets/esdl_asset_object.py | 7 ++++++ .../infrastructure/app.py | 1 + 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py b/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py index b1a27629..32108aa7 100644 --- a/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py +++ b/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py @@ -14,9 +14,16 @@ # along with this program. If not, see . """Module containing the Esdl to asset mapper class.""" +import pandas as pd + +from typing import Optional + from omotes_simulator_core.entities.assets.controller.asset_controller_abstract import ( AssetControllerAbstract, ) +from omotes_simulator_core.entities.assets.controller.profile_interpolation import ( + ProfileInterpolator, +) from omotes_simulator_core.entities.assets.controller.controller_producer import ControllerProducer from omotes_simulator_core.entities.assets.esdl_asset_object import EsdlAssetObject from omotes_simulator_core.simulation.mappers.mappers import EsdlMapperAbstract @@ -29,7 +36,8 @@ def to_esdl(self, entity: AssetControllerAbstract) -> EsdlAssetObject: """Map an Entity to a EsdlAsset.""" raise NotImplementedError("EsdlAssetControllerProducerMapper.to_esdl()") - def to_entity(self, esdl_asset: EsdlAssetObject) -> ControllerProducer: + def to_entity(self, esdl_asset: EsdlAssetObject, timestep: Optional[int] = None + ) -> ControllerProducer: """Method to map an esdl asset to a producer entity class. :param EsdlAssetObject model: Object to be converted to an asset entity. @@ -41,6 +49,19 @@ def to_entity(self, esdl_asset: EsdlAssetObject) -> ControllerProducer: temperature_in = esdl_asset.get_temperature("In", "Return") temperature_out = esdl_asset.get_temperature("Out", "Supply") strategy_priority = esdl_asset.get_strategy_priority() + + if esdl_asset.has_profile(): + profile = esdl_asset.get_profile() + self.profile_interpolator = ProfileInterpolator( + profile=profile, + sampling_method=esdl_asset.get_sampling_method(), + interpolation_method=esdl_asset.get_interpolation_method(), + timestep=timestep, + ) + resampled_profile = self.profile_interpolator.get_resampled_profile() + else: + profile = pd.DataFrame() + contr_producer = ControllerProducer( name=esdl_asset.esdl_asset.name, identifier=esdl_asset.esdl_asset.id, @@ -48,6 +69,7 @@ def to_entity(self, esdl_asset: EsdlAssetObject) -> ControllerProducer: temperature_out=temperature_out, power=power, marginal_costs=marginal_costs, + profile=resampled_profile if not profile.empty else profile, priority=strategy_priority, ) return contr_producer diff --git a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py index 573a7da8..e725ec4b 100644 --- a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py +++ b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py @@ -14,6 +14,8 @@ # along with this program. If not, see . """Module containing the classes for the controller.""" +import pandas as pd + from omotes_simulator_core.entities.assets.controller.asset_controller_abstract import ( AssetControllerAbstract, ) @@ -30,6 +32,7 @@ def __init__( temperature_out: float, power: float, marginal_costs: float, + profile: pd.DataFrame, priority: None | int = 1, ): """Constructor for the source. @@ -48,3 +51,7 @@ def __init__( self.power: float = power self.marginal_costs: float = marginal_costs self.priority: None | int = priority + self.profile: pd.DataFrame = \ + profile.set_index("date") if not profile.empty else profile + + # TODO: add function get_max_power that depends on whether there is a profile or not. diff --git a/src/omotes_simulator_core/entities/assets/esdl_asset_object.py b/src/omotes_simulator_core/entities/assets/esdl_asset_object.py index 13ea7fea..0cba0824 100644 --- a/src/omotes_simulator_core/entities/assets/esdl_asset_object.py +++ b/src/omotes_simulator_core/entities/assets/esdl_asset_object.py @@ -194,6 +194,13 @@ def is_heat_transfer_asset(self) -> bool: self.esdl_asset, esdl.HeatExchange ) + def has_profile(self) -> bool: + """Checks if an asset has a profile assigned to it""" + for esdl_port in self.esdl_asset.port: + if esdl_port.profile: + return True + return False + def get_return_temperature(esdl_port: esdl.Port) -> float: """Get the temperature of the port.""" diff --git a/src/omotes_simulator_core/infrastructure/app.py b/src/omotes_simulator_core/infrastructure/app.py index a7f9d91e..8384542d 100644 --- a/src/omotes_simulator_core/infrastructure/app.py +++ b/src/omotes_simulator_core/infrastructure/app.py @@ -63,6 +63,7 @@ def run(file_path: str | None = None) -> pd.DataFrame: level=logging.INFO, format="%(asctime)s [%(levelname)s]:%(name)s - %(message)s" ) t1 = datetime.now() + #result = run(r".\testdata\test1_prod_profile.esdl") result = run(r".\testdata\test1.esdl") t2 = datetime.now() From bc4e4d8d657660e04accb4c4ad800d78f68ce1f4 Mon Sep 17 00:00:00 2001 From: Santiago Patterson Date: Fri, 16 Jan 2026 17:30:25 +0100 Subject: [PATCH 02/11] Profile implemented into the producer and it now controls the maximum power it outputs. --- .../assets/controller/controller_network.py | 12 ++--- .../assets/controller/controller_producer.py | 18 ++++++- .../entities/network_controller.py | 19 +++---- .../infrastructure/app.py | 3 +- .../simulation/networksimulation.py | 7 +++ testdata/test1_prod_profile.esdl | 53 +++++++++++++++++++ .../controller/test_controller_network.py | 4 +- 7 files changed, 97 insertions(+), 19 deletions(-) create mode 100644 testdata/test1_prod_profile.esdl diff --git a/src/omotes_simulator_core/entities/assets/controller/controller_network.py b/src/omotes_simulator_core/entities/assets/controller/controller_network.py index baa645b3..1011b11a 100644 --- a/src/omotes_simulator_core/entities/assets/controller/controller_network.py +++ b/src/omotes_simulator_core/entities/assets/controller/controller_network.py @@ -115,24 +115,24 @@ def get_total_charge_storage(self) -> float: * self.factor_to_first_network ) - def get_total_supply(self) -> float: + def get_total_supply(self, time: datetime.datetime) -> float: """Method to get the total heat supply of the network. :return float: Total heat supply of all producers. """ return ( - float(sum([producer.power for producer in self.producers])) + float(sum([producer.get_max_power(time) for producer in self.producers])) * self.factor_to_first_network ) - def set_supply_to_max(self, priority: int = 0) -> dict: + def set_supply_to_max(self, time: datetime.datetime, priority: int = 0) -> dict: """Method to set the producers to the max power. :return dict: Dict with key= asset-id and value=setpoints for the producers. """ - return self.set_supply(factor=1, priority=priority) + return self.set_supply(time, factor=1, priority=priority) - def set_supply(self, factor: float = 1, priority: int = 0) -> dict: + def set_supply(self, time: datetime.datetime, factor: float = 1, priority: int = 0) -> dict: """Method to set the producers with the given priority to max power times the factor. :param float factor: Factor to multiply the max power with. @@ -147,7 +147,7 @@ def set_supply(self, factor: float = 1, priority: int = 0) -> dict: elif source.priority != priority: continue producers[source.id] = { - PROPERTY_HEAT_DEMAND: source.power * factor, + PROPERTY_HEAT_DEMAND: source.get_max_power(time) * factor, PROPERTY_TEMPERATURE_OUT: source.temperature_out, PROPERTY_TEMPERATURE_IN: source.temperature_in, PROPERTY_SET_PRESSURE: False, diff --git a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py index e725ec4b..b7f83c6f 100644 --- a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py +++ b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py @@ -14,6 +14,7 @@ # along with this program. If not, see . """Module containing the classes for the controller.""" +import datetime import pandas as pd from omotes_simulator_core.entities.assets.controller.asset_controller_abstract import ( @@ -53,5 +54,18 @@ def __init__( self.priority: None | int = priority self.profile: pd.DataFrame = \ profile.set_index("date") if not profile.empty else profile - - # TODO: add function get_max_power that depends on whether there is a profile or not. + + def get_max_power(self, time: datetime.datetime) -> float: + """Gets the maximum producer power at the given timestep. If + there is a profile, it will look it up, otherwise it returns the + maximum power defined in the esdl parameter.""" + + if self.profile.empty: + max_power = self.power + else: + try: + max_power = float(self.profile.loc[time, "values"]) + except KeyError: + max_power = self.power + + return max_power diff --git a/src/omotes_simulator_core/entities/network_controller.py b/src/omotes_simulator_core/entities/network_controller.py index 52f8c2ae..4ef35c78 100644 --- a/src/omotes_simulator_core/entities/network_controller.py +++ b/src/omotes_simulator_core/entities/network_controller.py @@ -83,7 +83,7 @@ def update_setpoints(self, time: datetime.datetime) -> dict: """ self.update_networks_factor() total_demand = sum([network.get_total_heat_demand(time) for network in self.networks]) - total_supply = sum([network.get_total_supply() for network in self.networks]) + total_supply = sum([network.get_total_supply(time) for network in self.networks]) total_charge_storage = sum( [network.get_total_charge_storage() for network in self.networks] ) @@ -97,7 +97,7 @@ def update_setpoints(self, time: datetime.datetime) -> dict: f"Consumers are capped to the available power." ) factor = (total_supply + total_discharge_storage) / total_demand - producers = self._set_producers_to_max() + producers = self._set_producers_to_max(time) producers.update(self._set_all_storages_discharge_to_max()) producers.update(self._set_consumer_to_demand(time, factor=factor)) else: @@ -107,11 +107,12 @@ def update_setpoints(self, time: datetime.datetime) -> dict: surplus_supply = total_supply - total_demand if surplus_supply <= total_charge_storage: storages = self._set_storages_charge_power(surplus_supply) - producers = self._set_producers_to_max() + producers = self._set_producers_to_max(time) else: # need to cap the power of the source based on priority storages = self._set_storages_charge_power(total_charge_storage) producers = self._set_producers_based_on_priority( + time, total_demand + total_charge_storage ) else: @@ -119,7 +120,7 @@ def update_setpoints(self, time: datetime.datetime) -> dict: # producer. deficit_supply = total_demand - total_supply storages = self._set_storages_discharge_power(deficit_supply) - producers = self._set_producers_to_max() + producers = self._set_producers_to_max(time) producers.update(consumers) producers.update(storages) # Getting the settings for the heat transfer assets @@ -153,10 +154,10 @@ def update_setpoints(self, time: datetime.datetime) -> dict: producers[pressure_set_asset][PROPERTY_SET_PRESSURE] = True return producers - def _set_producers_to_max(self) -> dict: + def _set_producers_to_max(self, time: datetime.datetime) -> dict: result = {} for network in self.networks: - result.update(network.set_supply_to_max()) + result.update(network.set_supply_to_max(time)) return result def _set_all_storages_discharge_to_max(self) -> dict: @@ -199,7 +200,7 @@ def _set_storages_discharge_power(self, power: float) -> dict: results.update(network.set_storage_discharge_power(factor=factor)) return results - def _set_producers_based_on_priority(self, required_supply: float) -> dict: + def _set_producers_based_on_priority(self, time: datetime.datetime, required_supply: float) -> dict: """Method to set the producers based on the priority of the source.""" producers = {} priority = 0 @@ -210,12 +211,12 @@ def _set_producers_based_on_priority(self, required_supply: float) -> dict: if required_supply > 0: # set the producers with the priority to the max for network in self.networks: - producers.update(network.set_supply_to_max(priority)) + producers.update(network.set_supply_to_max(time, priority)) else: # set the producers with the priority with a factor. factor = 1 + required_supply / max_supply_priority for network in self.networks: - producers.update(network.set_supply(factor=factor, priority=priority)) + producers.update(network.set_supply(time, factor=factor, priority=priority)) if len(producers) < sum([len(network.producers) for network in self.networks]): # not al producers are set need to set the remaining to zero. for network in self.networks: diff --git a/src/omotes_simulator_core/infrastructure/app.py b/src/omotes_simulator_core/infrastructure/app.py index 8384542d..57253d7f 100644 --- a/src/omotes_simulator_core/infrastructure/app.py +++ b/src/omotes_simulator_core/infrastructure/app.py @@ -64,9 +64,10 @@ def run(file_path: str | None = None) -> pd.DataFrame: ) t1 = datetime.now() #result = run(r".\testdata\test1_prod_profile.esdl") - result = run(r".\testdata\test1.esdl") + result = run(r".\testdata\test1_prod_profile.esdl") t2 = datetime.now() logger.info(f"Results dataframe shape=({result.shape})") logger.info(f"Execution time: {t2 - t1}") logger.debug(result.head()) + producer_id = "cf3d4b5e-437f-4c1b-a7f9-7fd7e8a269b4" diff --git a/src/omotes_simulator_core/simulation/networksimulation.py b/src/omotes_simulator_core/simulation/networksimulation.py index 01a40691..246c5caa 100644 --- a/src/omotes_simulator_core/simulation/networksimulation.py +++ b/src/omotes_simulator_core/simulation/networksimulation.py @@ -62,6 +62,13 @@ def run( tzinfo=timezone.utc ) + #### REMOVE AFTER TESTING ######## + producer = self.controller.networks[0].producers[0] + print(f'Current time: {time}') + print(f'Current max power producer: {producer.get_max_power(time)}') + print(f'Current producer profile: {producer.profile.loc[time, "values"]}') + ################################## + # Link controller to network self.controller.update_network_state(heat_network=self.network) diff --git a/testdata/test1_prod_profile.esdl b/testdata/test1_prod_profile.esdl new file mode 100644 index 00000000..633e4b31 --- /dev/null +++ b/testdata/test1_prod_profile.esdl @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unit_test/entities/controller/test_controller_network.py b/unit_test/entities/controller/test_controller_network.py index b79e0aad..401b343d 100644 --- a/unit_test/entities/controller/test_controller_network.py +++ b/unit_test/entities/controller/test_controller_network.py @@ -124,12 +124,14 @@ def test_get_total_supply(self): # arrange producer1 = Mock() producer1.power = 10 + producer1.get_max_power = Mock(return_value=10) producer2 = Mock() producer2.power = 20 + producer1.get_max_power = Mock(return_value=20) self.controller_network.factor_to_first_network = 2.0 self.controller_network.producers = [producer1, producer2] # act - res = self.controller_network.get_total_supply() + res = self.controller_network.get_total_supply(datetime.datetime.now()) # assert self.assertEqual(res, 60) From fd2bca111bd15ed2b5935cc8f792e21633801295 Mon Sep 17 00:00:00 2001 From: Santiago Patterson Date: Mon, 26 Jan 2026 14:15:42 +0100 Subject: [PATCH 03/11] Moved the producer profiles to the constraint instead of the out port. --- .../controller_producer_mapper.py | 4 ++-- .../assets/controller/controller_producer.py | 4 ++-- .../entities/assets/esdl_asset_object.py | 15 +++++++++++- .../infrastructure/app.py | 24 +++++++++++++++++-- .../simulation/networksimulation.py | 7 ------ 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py b/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py index 32108aa7..ae9885b4 100644 --- a/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py +++ b/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py @@ -50,8 +50,8 @@ def to_entity(self, esdl_asset: EsdlAssetObject, timestep: Optional[int] = None temperature_out = esdl_asset.get_temperature("Out", "Supply") strategy_priority = esdl_asset.get_strategy_priority() - if esdl_asset.has_profile(): - profile = esdl_asset.get_profile() + if esdl_asset.has_constraint(): + profile = esdl_asset.get_constraint_profile() self.profile_interpolator = ProfileInterpolator( profile=profile, sampling_method=esdl_asset.get_sampling_method(), diff --git a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py index b7f83c6f..6c98870c 100644 --- a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py +++ b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py @@ -33,7 +33,7 @@ def __init__( temperature_out: float, power: float, marginal_costs: float, - profile: pd.DataFrame, + profile: pd.DataFrame, # TODO: find where this is called and replace by the constraint. priority: None | int = 1, ): """Constructor for the source. @@ -65,7 +65,7 @@ def get_max_power(self, time: datetime.datetime) -> float: else: try: max_power = float(self.profile.loc[time, "values"]) - except KeyError: + except KeyError: #TODO: Test to make sure this works when there is no profile in the selected date. max_power = self.power return max_power diff --git a/src/omotes_simulator_core/entities/assets/esdl_asset_object.py b/src/omotes_simulator_core/entities/assets/esdl_asset_object.py index 0cba0824..d2bdde6d 100644 --- a/src/omotes_simulator_core/entities/assets/esdl_asset_object.py +++ b/src/omotes_simulator_core/entities/assets/esdl_asset_object.py @@ -90,7 +90,7 @@ def get_property(self, esdl_property_name: str, default_value: Any) -> Any: return getattr(self.esdl_asset, esdl_property_name, default_value) def get_profile(self) -> pd.DataFrame: - """Get the profile of the asset.""" + """Get the profile of the asset's ports.""" for esdl_port in self.esdl_asset.port: if esdl_port.profile: return get_data_from_profile(esdl_port.profile[0]) @@ -99,6 +99,11 @@ def get_profile(self) -> pd.DataFrame: extra={"esdl_object_id": self.get_id()}, ) raise ValueError(f"No profile found for asset: {self.esdl_asset.name}") + + def get_constraint_profile(self) -> pd.DataFrame: + """Get the profile from the asset's constraint.""" + profile = self.esdl_asset.constraint[0].maximum + return get_data_from_profile(profile) def get_sampling_method(self) -> ProfileSamplingMethod: """Get the interpolation method of the asset.""" @@ -200,6 +205,14 @@ def has_profile(self) -> bool: if esdl_port.profile: return True return False + + def has_constraint(self) -> bool: + """Checks if an asset has a constraint assigned to it""" + if self.esdl_asset.constraint.items: + return True + else: + return False + def get_return_temperature(esdl_port: esdl.Port) -> float: diff --git a/src/omotes_simulator_core/infrastructure/app.py b/src/omotes_simulator_core/infrastructure/app.py index 57253d7f..a0679bb3 100644 --- a/src/omotes_simulator_core/infrastructure/app.py +++ b/src/omotes_simulator_core/infrastructure/app.py @@ -28,6 +28,8 @@ from omotes_simulator_core.infrastructure.simulation_manager import SimulationManager from omotes_simulator_core.infrastructure.utils import pyesdl_from_file +import matplotlib.pyplot as plt + logger = logging.getLogger(__name__) @@ -43,7 +45,7 @@ def run(file_path: str | None = None) -> pd.DataFrame: name="test run", timestep=3600, start=datetime.strptime("2019-01-01T00:00:00", "%Y-%m-%dT%H:%M:%S"), - stop=datetime.strptime("2019-01-01T01:00:00", "%Y-%m-%dT%H:%M:%S"), + stop=datetime.strptime("2019-01-08T01:00:00", "%Y-%m-%dT%H:%M:%S"), ) esdl_file_path = sys.argv[1] if file_path is None else file_path @@ -63,7 +65,7 @@ def run(file_path: str | None = None) -> pd.DataFrame: level=logging.INFO, format="%(asctime)s [%(levelname)s]:%(name)s - %(message)s" ) t1 = datetime.now() - #result = run(r".\testdata\test1_prod_profile.esdl") + #result = run(r".\testdata\test1.esdl") result = run(r".\testdata\test1_prod_profile.esdl") t2 = datetime.now() @@ -71,3 +73,21 @@ def run(file_path: str | None = None) -> pd.DataFrame: logger.info(f"Execution time: {t2 - t1}") logger.debug(result.head()) producer_id = "cf3d4b5e-437f-4c1b-a7f9-7fd7e8a269b4" + + # Producer in and out ports: "9c258b9d-3149-4720-8931-f4bef1080ec1", "2d818e3d-8a39-4cec-afa0-f6dbbfd50696", carrier in: "0bd9cb08-2f69-4e97-8ac8-bd87b07e466a_ret", carrier out: "0bd9cb08-2f69-4e97-8ac8-bd87b07e466a". + prod_massfl_in = result["9c258b9d-3149-4720-8931-f4bef1080ec1", "mass_flow"].values + prod_massfl_out = result["2d818e3d-8a39-4cec-afa0-f6dbbfd50696", "mass_flow"].values + prod_temp_in = result["9c258b9d-3149-4720-8931-f4bef1080ec1", "temperature"].values + prod_temp_out = result["2d818e3d-8a39-4cec-afa0-f6dbbfd50696", "temperature"].values + # Consumer in and out ports: "af0904f7-ba1f-4e79-9040-71e08041601b", "e890f65f-80e7-46fa-8c52-5385324bf686", carrier in: "0bd9cb08-2f69-4e97-8ac8-bd87b07e466a", carrier out: "0bd9cb08-2f69-4e97-8ac8-bd87b07e466a_ret". + cons_massfl_in = result["af0904f7-ba1f-4e79-9040-71e08041601b", "mass_flow"].values + cons_massfl_out = result["e890f65f-80e7-46fa-8c52-5385324bf686", "mass_flow"].values + cons_temp_in = result["af0904f7-ba1f-4e79-9040-71e08041601b", "temperature"].values + cons_temp_out = result["e890f65f-80e7-46fa-8c52-5385324bf686", "temperature"].values + + prod_power = 4180 * prod_massfl_in * (prod_temp_out - prod_temp_in) + cons_power = 4180 * cons_massfl_in * (cons_temp_in - cons_temp_out) + + plt.figure() + plt.plot(prod_power) + plt.show() \ No newline at end of file diff --git a/src/omotes_simulator_core/simulation/networksimulation.py b/src/omotes_simulator_core/simulation/networksimulation.py index 246c5caa..01a40691 100644 --- a/src/omotes_simulator_core/simulation/networksimulation.py +++ b/src/omotes_simulator_core/simulation/networksimulation.py @@ -62,13 +62,6 @@ def run( tzinfo=timezone.utc ) - #### REMOVE AFTER TESTING ######## - producer = self.controller.networks[0].producers[0] - print(f'Current time: {time}') - print(f'Current max power producer: {producer.get_max_power(time)}') - print(f'Current producer profile: {producer.profile.loc[time, "values"]}') - ################################## - # Link controller to network self.controller.update_network_state(heat_network=self.network) From f3594ac5a70e9d9be08551ae8f65b1724c025596 Mon Sep 17 00:00:00 2001 From: Santiago Patterson Date: Tue, 27 Jan 2026 10:16:50 +0100 Subject: [PATCH 04/11] Fixed tests --- .../assets/controller/controller_producer.py | 2 +- .../controller/test_controller_network.py | 19 +++++++++++++++---- .../controller/test_controller_new_class.py | 4 ++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py index 6c98870c..ac9e1399 100644 --- a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py +++ b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py @@ -33,7 +33,7 @@ def __init__( temperature_out: float, power: float, marginal_costs: float, - profile: pd.DataFrame, # TODO: find where this is called and replace by the constraint. + profile: pd.DataFrame = pd.DataFrame(), priority: None | int = 1, ): """Constructor for the source. diff --git a/unit_test/entities/controller/test_controller_network.py b/unit_test/entities/controller/test_controller_network.py index 401b343d..1b7c2964 100644 --- a/unit_test/entities/controller/test_controller_network.py +++ b/unit_test/entities/controller/test_controller_network.py @@ -127,7 +127,7 @@ def test_get_total_supply(self): producer1.get_max_power = Mock(return_value=10) producer2 = Mock() producer2.power = 20 - producer1.get_max_power = Mock(return_value=20) + producer2.get_max_power = Mock(return_value=20) self.controller_network.factor_to_first_network = 2.0 self.controller_network.producers = [producer1, producer2] # act @@ -143,13 +143,15 @@ def test_set_supply_to_max_priority(self): producer1.temperature_out = 50 producer1.temperature_in = 40 producer1.priority = 1 + producer1.get_max_power = Mock(return_value=10) producer2 = Mock() producer2.id = "producer2" producer2.power = 20 producer2.priority = 2 + producer2.get_max_power = Mock(return_value=20) self.controller_network.producers = [producer1, producer2] # act - res = self.controller_network.set_supply_to_max(priority=1) + res = self.controller_network.set_supply_to_max(datetime.datetime.now(), priority=1) # assert self.assertEqual( res, @@ -171,15 +173,17 @@ def test_set_supply_to_max_priority_zero(self): producer1.temperature_out = 50 producer1.temperature_in = 40 producer1.priority = 1 + producer1.get_max_power = Mock(return_value=10) producer2 = Mock() producer2.id = "producer2" producer2.power = 20 producer2.priority = 2 producer2.temperature_out = 80 producer2.temperature_in = 100 + producer2.get_max_power = Mock(return_value=20) self.controller_network.producers = [producer1, producer2] # act - res = self.controller_network.set_supply_to_max(priority=0) + res = self.controller_network.set_supply_to_max(datetime.datetime.now(), priority=0) # assert self.assertEqual( res, @@ -207,13 +211,15 @@ def test_set_supply(self): producer1.temperature_out = 50 producer1.temperature_in = 40 producer1.priority = 1 + producer1.get_max_power = Mock(return_value=10) producer2 = Mock() producer2.id = "producer2" producer2.power = 20 producer2.priority = 2 + producer2.get_max_power = Mock(return_value=20) self.controller_network.producers = [producer1, producer2] # act - res = self.controller_network.set_supply(priority=1, factor=0.5) + res = self.controller_network.set_supply(datetime.datetime.now(), priority=1, factor=0.5) # assert self.assertEqual( res, @@ -310,3 +316,8 @@ def test_get_total_supply_priority(self): # assert pass + +if __name__ == "__main__": + test = TestControllerNetwork() + test.setUp() + test.test_set_supply_to_max_priority() diff --git a/unit_test/entities/controller/test_controller_new_class.py b/unit_test/entities/controller/test_controller_new_class.py index a1144ba9..103baa84 100644 --- a/unit_test/entities/controller/test_controller_new_class.py +++ b/unit_test/entities/controller/test_controller_new_class.py @@ -299,7 +299,7 @@ def test_set_producers_to_max(self): return_value={"id3": {"prop1": 9, "prop2": 15}} ) # act - result = self.controller._set_producers_to_max() + result = self.controller._set_producers_to_max(datetime.datetime.now()) # assert self.assertEqual( result, @@ -440,7 +440,7 @@ def test_set_producers_based_on_priority(self): storages_in=[], ) # act - result = self.controller._set_producers_based_on_priority(120) + result = self.controller._set_producers_based_on_priority(datetime.datetime.now(), 120) # assert self.assertEqual(result["producer1"][PROPERTY_HEAT_DEMAND], 50) From cb98ef1046a8d2eb5ff2e494bee0e4a8dd85b02e Mon Sep 17 00:00:00 2001 From: Santiago Patterson Date: Tue, 27 Jan 2026 10:22:26 +0100 Subject: [PATCH 05/11] Removed test file. --- testdata/test1_prod_profile.esdl | 53 -------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 testdata/test1_prod_profile.esdl diff --git a/testdata/test1_prod_profile.esdl b/testdata/test1_prod_profile.esdl deleted file mode 100644 index 633e4b31..00000000 --- a/testdata/test1_prod_profile.esdl +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From aa62e3afaa554289bf6885ef757c218595fc6aaf Mon Sep 17 00:00:00 2001 From: Santiago Patterson Date: Tue, 27 Jan 2026 11:11:08 +0100 Subject: [PATCH 06/11] Linting and formatting. --- .../controller_producer_mapper.py | 9 ++++--- .../assets/controller/controller_producer.py | 24 ++++++++++-------- .../entities/assets/esdl_asset_object.py | 9 +++---- .../entities/network_controller.py | 7 +++--- .../infrastructure/app.py | 25 ++----------------- .../controller/test_controller_network.py | 3 ++- .../entities/test_air_to_water_heat_pump.py | 1 + 7 files changed, 32 insertions(+), 46 deletions(-) diff --git a/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py b/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py index ae9885b4..e0376d01 100644 --- a/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py +++ b/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py @@ -14,17 +14,17 @@ # along with this program. If not, see . """Module containing the Esdl to asset mapper class.""" -import pandas as pd - from typing import Optional +import pandas as pd + from omotes_simulator_core.entities.assets.controller.asset_controller_abstract import ( AssetControllerAbstract, ) +from omotes_simulator_core.entities.assets.controller.controller_producer import ControllerProducer from omotes_simulator_core.entities.assets.controller.profile_interpolation import ( ProfileInterpolator, ) -from omotes_simulator_core.entities.assets.controller.controller_producer import ControllerProducer from omotes_simulator_core.entities.assets.esdl_asset_object import EsdlAssetObject from omotes_simulator_core.simulation.mappers.mappers import EsdlMapperAbstract @@ -36,7 +36,8 @@ def to_esdl(self, entity: AssetControllerAbstract) -> EsdlAssetObject: """Map an Entity to a EsdlAsset.""" raise NotImplementedError("EsdlAssetControllerProducerMapper.to_esdl()") - def to_entity(self, esdl_asset: EsdlAssetObject, timestep: Optional[int] = None + def to_entity( + self, esdl_asset: EsdlAssetObject, timestep: Optional[int] = None ) -> ControllerProducer: """Method to map an esdl asset to a producer entity class. diff --git a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py index ac9e1399..b02ded36 100644 --- a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py +++ b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py @@ -15,7 +15,9 @@ """Module containing the classes for the controller.""" import datetime + import pandas as pd +from typing import Optional from omotes_simulator_core.entities.assets.controller.asset_controller_abstract import ( AssetControllerAbstract, @@ -33,7 +35,7 @@ def __init__( temperature_out: float, power: float, marginal_costs: float, - profile: pd.DataFrame = pd.DataFrame(), + profile: Optional[pd.DataFrame] = None, priority: None | int = 1, ): """Constructor for the source. @@ -52,20 +54,22 @@ def __init__( self.power: float = power self.marginal_costs: float = marginal_costs self.priority: None | int = priority - self.profile: pd.DataFrame = \ - profile.set_index("date") if not profile.empty else profile - + self.profile: pd.DataFrame = ( + pd.DataFrame() if profile is None or profile.empty else profile.set_index("date") + ) + def get_max_power(self, time: datetime.datetime) -> float: - """Gets the maximum producer power at the given timestep. If - there is a profile, it will look it up, otherwise it returns the - maximum power defined in the esdl parameter.""" - + """Gets the maximum producer power at the given timestep. + + If there is a profile, it will look it up, otherwise it returns the + maximum power defined in the esdl parameter. + """ if self.profile.empty: max_power = self.power else: try: max_power = float(self.profile.loc[time, "values"]) - except KeyError: #TODO: Test to make sure this works when there is no profile in the selected date. + except KeyError: max_power = self.power - + return max_power diff --git a/src/omotes_simulator_core/entities/assets/esdl_asset_object.py b/src/omotes_simulator_core/entities/assets/esdl_asset_object.py index d2bdde6d..70e78130 100644 --- a/src/omotes_simulator_core/entities/assets/esdl_asset_object.py +++ b/src/omotes_simulator_core/entities/assets/esdl_asset_object.py @@ -99,7 +99,7 @@ def get_profile(self) -> pd.DataFrame: extra={"esdl_object_id": self.get_id()}, ) raise ValueError(f"No profile found for asset: {self.esdl_asset.name}") - + def get_constraint_profile(self) -> pd.DataFrame: """Get the profile from the asset's constraint.""" profile = self.esdl_asset.constraint[0].maximum @@ -200,21 +200,20 @@ def is_heat_transfer_asset(self) -> bool: ) def has_profile(self) -> bool: - """Checks if an asset has a profile assigned to it""" + """Checks if an asset has a profile assigned to any of its ports.""" for esdl_port in self.esdl_asset.port: if esdl_port.profile: return True return False - + def has_constraint(self) -> bool: - """Checks if an asset has a constraint assigned to it""" + """Checks if an asset has a constraint assigned to it.""" if self.esdl_asset.constraint.items: return True else: return False - def get_return_temperature(esdl_port: esdl.Port) -> float: """Get the temperature of the port.""" return float(esdl_port.carrier.returnTemperature) + 273.15 diff --git a/src/omotes_simulator_core/entities/network_controller.py b/src/omotes_simulator_core/entities/network_controller.py index 4ef35c78..f9b74728 100644 --- a/src/omotes_simulator_core/entities/network_controller.py +++ b/src/omotes_simulator_core/entities/network_controller.py @@ -112,8 +112,7 @@ def update_setpoints(self, time: datetime.datetime) -> dict: # need to cap the power of the source based on priority storages = self._set_storages_charge_power(total_charge_storage) producers = self._set_producers_based_on_priority( - time, - total_demand + total_charge_storage + time, total_demand + total_charge_storage ) else: # there is a deficit of supply we can discharge the storage, storage becomes @@ -200,7 +199,9 @@ def _set_storages_discharge_power(self, power: float) -> dict: results.update(network.set_storage_discharge_power(factor=factor)) return results - def _set_producers_based_on_priority(self, time: datetime.datetime, required_supply: float) -> dict: + def _set_producers_based_on_priority( + self, time: datetime.datetime, required_supply: float + ) -> dict: """Method to set the producers based on the priority of the source.""" producers = {} priority = 0 diff --git a/src/omotes_simulator_core/infrastructure/app.py b/src/omotes_simulator_core/infrastructure/app.py index a0679bb3..83d9f756 100644 --- a/src/omotes_simulator_core/infrastructure/app.py +++ b/src/omotes_simulator_core/infrastructure/app.py @@ -28,8 +28,6 @@ from omotes_simulator_core.infrastructure.simulation_manager import SimulationManager from omotes_simulator_core.infrastructure.utils import pyesdl_from_file -import matplotlib.pyplot as plt - logger = logging.getLogger(__name__) @@ -65,29 +63,10 @@ def run(file_path: str | None = None) -> pd.DataFrame: level=logging.INFO, format="%(asctime)s [%(levelname)s]:%(name)s - %(message)s" ) t1 = datetime.now() - #result = run(r".\testdata\test1.esdl") - result = run(r".\testdata\test1_prod_profile.esdl") + # result = run(r".\testdata\test1.esdl") + result = run(r".\testdata\test1.esdl") t2 = datetime.now() logger.info(f"Results dataframe shape=({result.shape})") logger.info(f"Execution time: {t2 - t1}") logger.debug(result.head()) - producer_id = "cf3d4b5e-437f-4c1b-a7f9-7fd7e8a269b4" - - # Producer in and out ports: "9c258b9d-3149-4720-8931-f4bef1080ec1", "2d818e3d-8a39-4cec-afa0-f6dbbfd50696", carrier in: "0bd9cb08-2f69-4e97-8ac8-bd87b07e466a_ret", carrier out: "0bd9cb08-2f69-4e97-8ac8-bd87b07e466a". - prod_massfl_in = result["9c258b9d-3149-4720-8931-f4bef1080ec1", "mass_flow"].values - prod_massfl_out = result["2d818e3d-8a39-4cec-afa0-f6dbbfd50696", "mass_flow"].values - prod_temp_in = result["9c258b9d-3149-4720-8931-f4bef1080ec1", "temperature"].values - prod_temp_out = result["2d818e3d-8a39-4cec-afa0-f6dbbfd50696", "temperature"].values - # Consumer in and out ports: "af0904f7-ba1f-4e79-9040-71e08041601b", "e890f65f-80e7-46fa-8c52-5385324bf686", carrier in: "0bd9cb08-2f69-4e97-8ac8-bd87b07e466a", carrier out: "0bd9cb08-2f69-4e97-8ac8-bd87b07e466a_ret". - cons_massfl_in = result["af0904f7-ba1f-4e79-9040-71e08041601b", "mass_flow"].values - cons_massfl_out = result["e890f65f-80e7-46fa-8c52-5385324bf686", "mass_flow"].values - cons_temp_in = result["af0904f7-ba1f-4e79-9040-71e08041601b", "temperature"].values - cons_temp_out = result["e890f65f-80e7-46fa-8c52-5385324bf686", "temperature"].values - - prod_power = 4180 * prod_massfl_in * (prod_temp_out - prod_temp_in) - cons_power = 4180 * cons_massfl_in * (cons_temp_in - cons_temp_out) - - plt.figure() - plt.plot(prod_power) - plt.show() \ No newline at end of file diff --git a/unit_test/entities/controller/test_controller_network.py b/unit_test/entities/controller/test_controller_network.py index 1b7c2964..79e2d558 100644 --- a/unit_test/entities/controller/test_controller_network.py +++ b/unit_test/entities/controller/test_controller_network.py @@ -316,7 +316,8 @@ def test_get_total_supply_priority(self): # assert pass - + + if __name__ == "__main__": test = TestControllerNetwork() test.setUp() diff --git a/unit_test/entities/test_air_to_water_heat_pump.py b/unit_test/entities/test_air_to_water_heat_pump.py index 70301a44..9eacb5fe 100644 --- a/unit_test/entities/test_air_to_water_heat_pump.py +++ b/unit_test/entities/test_air_to_water_heat_pump.py @@ -281,6 +281,7 @@ def get_mass_flow_rate(_, i: int): def test_get_electric_consumption(self): """Test getting the electric power consumed by the heatpump.""" + # Arrange def get_internal_energy(_, i: int): if i == 0: From 4820cabe6d1833c564894f6f7c90cc55709c6e75 Mon Sep 17 00:00:00 2001 From: Santiago Patterson Date: Tue, 27 Jan 2026 11:20:10 +0100 Subject: [PATCH 07/11] Linting and formatting. --- .../entities/assets/controller/controller_producer.py | 2 +- unit_test/entities/test_air_to_water_heat_pump.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py index b02ded36..adf4859c 100644 --- a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py +++ b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py @@ -15,9 +15,9 @@ """Module containing the classes for the controller.""" import datetime +from typing import Optional import pandas as pd -from typing import Optional from omotes_simulator_core.entities.assets.controller.asset_controller_abstract import ( AssetControllerAbstract, diff --git a/unit_test/entities/test_air_to_water_heat_pump.py b/unit_test/entities/test_air_to_water_heat_pump.py index 9eacb5fe..70301a44 100644 --- a/unit_test/entities/test_air_to_water_heat_pump.py +++ b/unit_test/entities/test_air_to_water_heat_pump.py @@ -281,7 +281,6 @@ def get_mass_flow_rate(_, i: int): def test_get_electric_consumption(self): """Test getting the electric power consumed by the heatpump.""" - # Arrange def get_internal_energy(_, i: int): if i == 0: From 4821ff5f4c71086bcab67bde9f1d041b0d5dc98e Mon Sep 17 00:00:00 2001 From: Santiago Patterson Date: Wed, 28 Jan 2026 17:22:20 +0100 Subject: [PATCH 08/11] Implemented comments from PR. --- .../controller_producer_mapper.py | 6 +- .../assets/controller/controller_producer.py | 6 +- .../entities/assets/esdl_asset_object.py | 8 +- testdata/test1_prod_profile.esdl | 54 ++++++++++++++ .../test_controller_producer_mapper.py | 28 +++++-- .../controller/test_controller_consumer.py | 5 ++ .../controller/test_controller_network.py | 5 -- .../controller/test_controller_new_class.py | 11 +++ .../controller/test_controller_producer.py | 74 +++++++++++-------- 9 files changed, 146 insertions(+), 51 deletions(-) create mode 100644 testdata/test1_prod_profile.esdl diff --git a/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py b/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py index e0376d01..a8beca2d 100644 --- a/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py +++ b/src/omotes_simulator_core/adapter/transforms/controller_mappers/controller_producer_mapper.py @@ -52,7 +52,7 @@ def to_entity( strategy_priority = esdl_asset.get_strategy_priority() if esdl_asset.has_constraint(): - profile = esdl_asset.get_constraint_profile() + profile = esdl_asset.get_constraint_max_profile() self.profile_interpolator = ProfileInterpolator( profile=profile, sampling_method=esdl_asset.get_sampling_method(), @@ -61,7 +61,7 @@ def to_entity( ) resampled_profile = self.profile_interpolator.get_resampled_profile() else: - profile = pd.DataFrame() + resampled_profile = pd.DataFrame() contr_producer = ControllerProducer( name=esdl_asset.esdl_asset.name, @@ -70,7 +70,7 @@ def to_entity( temperature_out=temperature_out, power=power, marginal_costs=marginal_costs, - profile=resampled_profile if not profile.empty else profile, + profile=resampled_profile, # Empty DataFrame is added if there is no profile. priority=strategy_priority, ) return contr_producer diff --git a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py index adf4859c..4f87f287 100644 --- a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py +++ b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py @@ -35,7 +35,7 @@ def __init__( temperature_out: float, power: float, marginal_costs: float, - profile: Optional[pd.DataFrame] = None, + profile: pd.DataFrame, priority: None | int = 1, ): """Constructor for the source. @@ -54,9 +54,7 @@ def __init__( self.power: float = power self.marginal_costs: float = marginal_costs self.priority: None | int = priority - self.profile: pd.DataFrame = ( - pd.DataFrame() if profile is None or profile.empty else profile.set_index("date") - ) + self.profile: pd.DataFrame = profile.set_index("date") if not profile.empty else profile def get_max_power(self, time: datetime.datetime) -> float: """Gets the maximum producer power at the given timestep. diff --git a/src/omotes_simulator_core/entities/assets/esdl_asset_object.py b/src/omotes_simulator_core/entities/assets/esdl_asset_object.py index 70e78130..0eaacc5e 100644 --- a/src/omotes_simulator_core/entities/assets/esdl_asset_object.py +++ b/src/omotes_simulator_core/entities/assets/esdl_asset_object.py @@ -100,9 +100,11 @@ def get_profile(self) -> pd.DataFrame: ) raise ValueError(f"No profile found for asset: {self.esdl_asset.name}") - def get_constraint_profile(self) -> pd.DataFrame: - """Get the profile from the asset's constraint.""" - profile = self.esdl_asset.constraint[0].maximum + def get_constraint_max_profile(self) -> pd.DataFrame: + """Get the profile from the asset's maximum constraint.""" + for constraint in self.esdl_asset.constraint: + if constraint.maximum: + profile = constraint.maximum return get_data_from_profile(profile) def get_sampling_method(self) -> ProfileSamplingMethod: diff --git a/testdata/test1_prod_profile.esdl b/testdata/test1_prod_profile.esdl new file mode 100644 index 00000000..025692ee --- /dev/null +++ b/testdata/test1_prod_profile.esdl @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py b/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py index 883deb03..46e8c66c 100644 --- a/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py +++ b/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py @@ -17,6 +17,7 @@ import unittest from pathlib import Path +from datetime import datetime from omotes_simulator_core.adapter.transforms.controller_mappers import ControllerProducerMapper from omotes_simulator_core.entities.esdl_object import EsdlObject @@ -26,8 +27,9 @@ class TestControllerProducerMapper(unittest.TestCase): """Test ControllerProducerMapper.""" - def setUp(self) -> None: - """Set up test case.""" + def test_to_entity_method(self): + """Test settings of controller for producer.""" + # Arrange # Load the test esdl file esdl_file_path = ( Path(__file__).parent / ".." / ".." / ".." / ".." / "testdata" / "test_ates.esdl" @@ -35,10 +37,6 @@ def setUp(self) -> None: self.esdl_object = EsdlObject(pyesdl_from_file(esdl_file_path)) # Create a ControllerProducerMapper object self.mapper = ControllerProducerMapper() - - def test_to_entity_method(self): - """Test settings of controller for producer.""" - # Arrange producer_assets = self.esdl_object.get_all_assets_of_type("producer") # Act @@ -49,3 +47,21 @@ def test_to_entity_method(self): self.assertEqual(controller_producer.temperature_out, 353.15) self.assertEqual(controller_producer.power, 1e8) self.assertEqual(controller_producer.marginal_costs, 0) + + def test_get_max_constraint(self) -> None: + """Test to check if the maximum constraint is grabbed correctly. + """ + # Arrange + esdl_file_path_constraint = ( + Path(__file__).parent / ".." / ".." / ".." / ".." / "testdata" / "test1_prod_profile.esdl" + ) + self.esdl_object = EsdlObject(pyesdl_from_file(esdl_file_path_constraint)) + self.mapper = ControllerProducerMapper() + producer_assets = self.esdl_object.get_all_assets_of_type("producer") + + # Act + constraint_profile = producer_assets[0].get_constraint_max_profile() + + # Assert + self.assertEqual(producer_assets[0].has_constraint(), True) + self.assertEqual(constraint_profile.iloc[0].values[1], 143277.6298) diff --git a/unit_test/entities/controller/test_controller_consumer.py b/unit_test/entities/controller/test_controller_consumer.py index ee0ec13c..d79252eb 100644 --- a/unit_test/entities/controller/test_controller_consumer.py +++ b/unit_test/entities/controller/test_controller_consumer.py @@ -87,3 +87,8 @@ def test_date_not_in_profile_consumer(self): demand = self.consumer.get_heat_demand(datetime(2021, 3, 2, 0, 0)) # Assert self.assertEqual(demand, 0) + +if __name__ == "__main__": + test = ConsumerControllerTest() + test.setUp() + test.test_controller_consumer_get_heat_demand() \ No newline at end of file diff --git a/unit_test/entities/controller/test_controller_network.py b/unit_test/entities/controller/test_controller_network.py index 79e2d558..d46bcc40 100644 --- a/unit_test/entities/controller/test_controller_network.py +++ b/unit_test/entities/controller/test_controller_network.py @@ -317,8 +317,3 @@ def test_get_total_supply_priority(self): # assert pass - -if __name__ == "__main__": - test = TestControllerNetwork() - test.setUp() - test.test_set_supply_to_max_priority() diff --git a/unit_test/entities/controller/test_controller_new_class.py b/unit_test/entities/controller/test_controller_new_class.py index 103baa84..64591007 100644 --- a/unit_test/entities/controller/test_controller_new_class.py +++ b/unit_test/entities/controller/test_controller_new_class.py @@ -15,6 +15,7 @@ """Test controller class.""" import datetime import unittest +import pandas as pd from unittest.mock import Mock from omotes_simulator_core.entities.assets.asset_defaults import ( @@ -104,6 +105,7 @@ def setup_update_set_points(self): power=50, marginal_costs=1, priority=2, + profile=pd.DataFrame(), ) producer2 = ControllerProducer( name="producer2", @@ -113,6 +115,7 @@ def setup_update_set_points(self): power=40, marginal_costs=1, priority=3, + profile=pd.DataFrame(), ) consumer1 = Mock(spec=ControllerConsumer) consumer1.id = "consumer1" @@ -389,6 +392,7 @@ def test_set_producers_based_on_priority(self): power=50, marginal_costs=1, priority=2, + profile=pd.DataFrame(), ) producer2 = ControllerProducer( name="producer2", @@ -398,6 +402,7 @@ def test_set_producers_based_on_priority(self): power=40, marginal_costs=1, priority=3, + profile=pd.DataFrame(), ) producer3 = ControllerProducer( name="producer3", @@ -407,6 +412,7 @@ def test_set_producers_based_on_priority(self): power=40, marginal_costs=1, priority=1, + profile=pd.DataFrame(), ) producer4 = ControllerProducer( name="producer4", @@ -416,6 +422,7 @@ def test_set_producers_based_on_priority(self): power=20, marginal_costs=1, priority=3, + profile=pd.DataFrame(), ) self.controller.networks[0] = ControllerNetwork( heat_transfer_assets_prim_in=[], @@ -458,6 +465,7 @@ def test_get_total_supply_priority(self): power=50, marginal_costs=1, priority=2, + profile=pd.DataFrame(), ) producer2 = ControllerProducer( name="producer2", @@ -467,6 +475,7 @@ def test_get_total_supply_priority(self): power=40, marginal_costs=1, priority=3, + profile=pd.DataFrame(), ) producer3 = ControllerProducer( name="producer3", @@ -476,6 +485,7 @@ def test_get_total_supply_priority(self): power=40, marginal_costs=1, priority=1, + profile=pd.DataFrame(), ) producer4 = ControllerProducer( name="producer4", @@ -485,6 +495,7 @@ def test_get_total_supply_priority(self): power=20, marginal_costs=1, priority=3, + profile=pd.DataFrame(), ) self.controller.networks[0] = ControllerNetwork( heat_transfer_assets_prim_in=[], diff --git a/unit_test/entities/controller/test_controller_producer.py b/unit_test/entities/controller/test_controller_producer.py index d0ee406d..cb49d0ba 100644 --- a/unit_test/entities/controller/test_controller_producer.py +++ b/unit_test/entities/controller/test_controller_producer.py @@ -14,6 +14,9 @@ # along with this program. If not, see . """Test controller producer class.""" import unittest +import pandas as pd + +from datetime import datetime from omotes_simulator_core.entities.assets.asset_defaults import ( DEFAULT_TEMPERATURE, @@ -25,10 +28,16 @@ class ControllerProducerTest(unittest.TestCase): """Testcase for ControllerProducer class.""" - def test_controller_producer_init(self) -> None: - """Init test for ControllerProducer.""" - # Arrange - producer = ControllerProducer( + def setUp(self): + """Set up the test case.""" + self.values = [100, 200] + self.profile = pd.DataFrame( + { + "date": [datetime(2021, 1, 1, 0, 0, 0), datetime(2021, 1, 1, 1, 0, 0)], + "values": self.values, + } + ) + self.producer = ControllerProducer( "producer", "id", temperature_out=DEFAULT_TEMPERATURE + DEFAULT_TEMPERATURE_DIFFERENCE, @@ -36,19 +45,22 @@ def test_controller_producer_init(self) -> None: power=1000, marginal_costs=0.1, priority=1, + profile=self.profile, ) - # Act + + def test_controller_producer_init(self) -> None: + """Init test for ControllerProducer.""" # Assert - self.assertEqual(producer.name, "producer") - self.assertEqual(producer.id, "id") - self.assertEqual(producer.temperature_in, DEFAULT_TEMPERATURE) + self.assertEqual(self.producer.name, "producer") + self.assertEqual(self.producer.id, "id") + self.assertEqual(self.producer.temperature_in, DEFAULT_TEMPERATURE) self.assertEqual( - producer.temperature_out, DEFAULT_TEMPERATURE + DEFAULT_TEMPERATURE_DIFFERENCE + self.producer.temperature_out, DEFAULT_TEMPERATURE + DEFAULT_TEMPERATURE_DIFFERENCE ) - self.assertEqual(producer.power, 1000) - self.assertEqual(producer.marginal_costs, 0.1) - self.assertEqual(producer.priority, 1) + self.assertEqual(self.producer.power, 1000) + self.assertEqual(self.producer.marginal_costs, 0.1) + self.assertEqual(self.producer.priority, 1) def test_controller_producer_none_priority(self) -> None: """Test to ensure a None priority does not break the CotrollerProducer. @@ -57,25 +69,27 @@ def test_controller_producer_none_priority(self) -> None: producer with no priority assigned to it. """ # Arrange - producer = ControllerProducer( - "producer", - "id", - temperature_out=DEFAULT_TEMPERATURE + DEFAULT_TEMPERATURE_DIFFERENCE, - temperature_in=DEFAULT_TEMPERATURE, - power=1000, - marginal_costs=0.1, - priority=None, - ) + self.producer.priority = None + + # Assert + self.assertEqual(self.producer.priority, None) + + def test_get_max_power_profile(self) -> None: + """Test to check if get_max_power returns the profile value or the power esdl value if + no profile is present. + """ + + # Arrange + time = datetime(2021, 1, 1, 0, 0, 0) # Act + self.producer.profile = pd.DataFrame() + max_power_1 = self.producer.get_max_power(time) + self.producer.profile = self.profile.set_index("date") + max_power_2 = self.producer.get_max_power(time) # Assert - self.assertEqual(producer.name, "producer") - self.assertEqual(producer.id, "id") - self.assertEqual(producer.temperature_in, DEFAULT_TEMPERATURE) - self.assertEqual( - producer.temperature_out, DEFAULT_TEMPERATURE + DEFAULT_TEMPERATURE_DIFFERENCE - ) - self.assertEqual(producer.power, 1000) - self.assertEqual(producer.marginal_costs, 0.1) - self.assertEqual(producer.priority, None) + self.assertEqual(max_power_1, self.producer.power) + self.assertEqual(max_power_2, self.values[0]) + + \ No newline at end of file From 41d69aed47953828bcad945c0ba6a784ccbefe41 Mon Sep 17 00:00:00 2001 From: Santiago Patterson Date: Fri, 30 Jan 2026 16:43:34 +0100 Subject: [PATCH 09/11] Implemented PR comments. --- .../assets/controller/controller_producer.py | 1 - .../entities/assets/esdl_asset_object.py | 2 ++ src/omotes_simulator_core/infrastructure/app.py | 1 - .../test_controller_producer_mapper.py | 14 +++++++++----- .../controller/test_controller_consumer.py | 5 ----- .../entities/controller/test_controller_network.py | 1 - .../controller/test_controller_new_class.py | 3 ++- .../controller/test_controller_producer.py | 12 +++++------- unit_test/entities/test_air_to_water_heat_pump.py | 1 + 9 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py index 4f87f287..4a8c2511 100644 --- a/src/omotes_simulator_core/entities/assets/controller/controller_producer.py +++ b/src/omotes_simulator_core/entities/assets/controller/controller_producer.py @@ -15,7 +15,6 @@ """Module containing the classes for the controller.""" import datetime -from typing import Optional import pandas as pd diff --git a/src/omotes_simulator_core/entities/assets/esdl_asset_object.py b/src/omotes_simulator_core/entities/assets/esdl_asset_object.py index 0eaacc5e..c6e0f57e 100644 --- a/src/omotes_simulator_core/entities/assets/esdl_asset_object.py +++ b/src/omotes_simulator_core/entities/assets/esdl_asset_object.py @@ -105,6 +105,8 @@ def get_constraint_max_profile(self) -> pd.DataFrame: for constraint in self.esdl_asset.constraint: if constraint.maximum: profile = constraint.maximum + else: + return pd.DataFrame() return get_data_from_profile(profile) def get_sampling_method(self) -> ProfileSamplingMethod: diff --git a/src/omotes_simulator_core/infrastructure/app.py b/src/omotes_simulator_core/infrastructure/app.py index 83d9f756..8ef26cbb 100644 --- a/src/omotes_simulator_core/infrastructure/app.py +++ b/src/omotes_simulator_core/infrastructure/app.py @@ -63,7 +63,6 @@ def run(file_path: str | None = None) -> pd.DataFrame: level=logging.INFO, format="%(asctime)s [%(levelname)s]:%(name)s - %(message)s" ) t1 = datetime.now() - # result = run(r".\testdata\test1.esdl") result = run(r".\testdata\test1.esdl") t2 = datetime.now() diff --git a/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py b/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py index 46e8c66c..dbae3116 100644 --- a/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py +++ b/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py @@ -17,7 +17,6 @@ import unittest from pathlib import Path -from datetime import datetime from omotes_simulator_core.adapter.transforms.controller_mappers import ControllerProducerMapper from omotes_simulator_core.entities.esdl_object import EsdlObject @@ -49,11 +48,16 @@ def test_to_entity_method(self): self.assertEqual(controller_producer.marginal_costs, 0) def test_get_max_constraint(self) -> None: - """Test to check if the maximum constraint is grabbed correctly. - """ + """Test to check if the maximum constraint is grabbed correctly.""" # Arrange esdl_file_path_constraint = ( - Path(__file__).parent / ".." / ".." / ".." / ".." / "testdata" / "test1_prod_profile.esdl" + Path(__file__).parent + / ".." + / ".." + / ".." + / ".." + / "testdata" + / "test1_prod_profile.esdl" ) self.esdl_object = EsdlObject(pyesdl_from_file(esdl_file_path_constraint)) self.mapper = ControllerProducerMapper() @@ -64,4 +68,4 @@ def test_get_max_constraint(self) -> None: # Assert self.assertEqual(producer_assets[0].has_constraint(), True) - self.assertEqual(constraint_profile.iloc[0].values[1], 143277.6298) + self.assertEqual(constraint_profile.iloc[0].values[1], 143277.6298) diff --git a/unit_test/entities/controller/test_controller_consumer.py b/unit_test/entities/controller/test_controller_consumer.py index d79252eb..ee0ec13c 100644 --- a/unit_test/entities/controller/test_controller_consumer.py +++ b/unit_test/entities/controller/test_controller_consumer.py @@ -87,8 +87,3 @@ def test_date_not_in_profile_consumer(self): demand = self.consumer.get_heat_demand(datetime(2021, 3, 2, 0, 0)) # Assert self.assertEqual(demand, 0) - -if __name__ == "__main__": - test = ConsumerControllerTest() - test.setUp() - test.test_controller_consumer_get_heat_demand() \ No newline at end of file diff --git a/unit_test/entities/controller/test_controller_network.py b/unit_test/entities/controller/test_controller_network.py index d46bcc40..7ec058ef 100644 --- a/unit_test/entities/controller/test_controller_network.py +++ b/unit_test/entities/controller/test_controller_network.py @@ -316,4 +316,3 @@ def test_get_total_supply_priority(self): # assert pass - diff --git a/unit_test/entities/controller/test_controller_new_class.py b/unit_test/entities/controller/test_controller_new_class.py index 64591007..3b5be63f 100644 --- a/unit_test/entities/controller/test_controller_new_class.py +++ b/unit_test/entities/controller/test_controller_new_class.py @@ -15,9 +15,10 @@ """Test controller class.""" import datetime import unittest -import pandas as pd from unittest.mock import Mock +import pandas as pd + from omotes_simulator_core.entities.assets.asset_defaults import ( PRIMARY, PROPERTY_HEAT_DEMAND, diff --git a/unit_test/entities/controller/test_controller_producer.py b/unit_test/entities/controller/test_controller_producer.py index cb49d0ba..b1c394b6 100644 --- a/unit_test/entities/controller/test_controller_producer.py +++ b/unit_test/entities/controller/test_controller_producer.py @@ -14,10 +14,10 @@ # along with this program. If not, see . """Test controller producer class.""" import unittest -import pandas as pd - from datetime import datetime +import pandas as pd + from omotes_simulator_core.entities.assets.asset_defaults import ( DEFAULT_TEMPERATURE, DEFAULT_TEMPERATURE_DIFFERENCE, @@ -73,23 +73,21 @@ def test_controller_producer_none_priority(self) -> None: # Assert self.assertEqual(self.producer.priority, None) - + def test_get_max_power_profile(self) -> None: """Test to check if get_max_power returns the profile value or the power esdl value if no profile is present. """ - + # Arrange time = datetime(2021, 1, 1, 0, 0, 0) # Act self.producer.profile = pd.DataFrame() - max_power_1 = self.producer.get_max_power(time) + max_power_1 = self.producer.get_max_power(time) # No constraint profile present. self.producer.profile = self.profile.set_index("date") max_power_2 = self.producer.get_max_power(time) # Assert self.assertEqual(max_power_1, self.producer.power) self.assertEqual(max_power_2, self.values[0]) - - \ No newline at end of file diff --git a/unit_test/entities/test_air_to_water_heat_pump.py b/unit_test/entities/test_air_to_water_heat_pump.py index 70301a44..9eacb5fe 100644 --- a/unit_test/entities/test_air_to_water_heat_pump.py +++ b/unit_test/entities/test_air_to_water_heat_pump.py @@ -281,6 +281,7 @@ def get_mass_flow_rate(_, i: int): def test_get_electric_consumption(self): """Test getting the electric power consumed by the heatpump.""" + # Arrange def get_internal_energy(_, i: int): if i == 0: From aee634daf727fd59931f5a516f8bef8f0a54b923 Mon Sep 17 00:00:00 2001 From: Santiago Patterson Date: Fri, 30 Jan 2026 16:44:37 +0100 Subject: [PATCH 10/11] Formatting. --- unit_test/entities/controller/test_controller_producer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unit_test/entities/controller/test_controller_producer.py b/unit_test/entities/controller/test_controller_producer.py index b1c394b6..00f473ef 100644 --- a/unit_test/entities/controller/test_controller_producer.py +++ b/unit_test/entities/controller/test_controller_producer.py @@ -84,7 +84,7 @@ def test_get_max_power_profile(self) -> None: # Act self.producer.profile = pd.DataFrame() - max_power_1 = self.producer.get_max_power(time) # No constraint profile present. + max_power_1 = self.producer.get_max_power(time) # No constraint profile present. self.producer.profile = self.profile.set_index("date") max_power_2 = self.producer.get_max_power(time) From 6cf0a1ecab33f6617cbd79c04ad4723c6721891d Mon Sep 17 00:00:00 2001 From: Santiago Patterson Date: Fri, 30 Jan 2026 17:03:26 +0100 Subject: [PATCH 11/11] Linting and formatting. --- .../controller_mappers/test_controller_producer_mapper.py | 2 +- unit_test/entities/controller/test_controller_producer.py | 6 +----- unit_test/entities/test_air_to_water_heat_pump.py | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py b/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py index dbae3116..c74f655d 100644 --- a/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py +++ b/unit_test/adapters/transforms/controller_mappers/test_controller_producer_mapper.py @@ -47,7 +47,7 @@ def test_to_entity_method(self): self.assertEqual(controller_producer.power, 1e8) self.assertEqual(controller_producer.marginal_costs, 0) - def test_get_max_constraint(self) -> None: + def test_get_max_constraint(self): """Test to check if the maximum constraint is grabbed correctly.""" # Arrange esdl_file_path_constraint = ( diff --git a/unit_test/entities/controller/test_controller_producer.py b/unit_test/entities/controller/test_controller_producer.py index 00f473ef..c842b6a6 100644 --- a/unit_test/entities/controller/test_controller_producer.py +++ b/unit_test/entities/controller/test_controller_producer.py @@ -50,7 +50,6 @@ def setUp(self): def test_controller_producer_init(self) -> None: """Init test for ControllerProducer.""" - # Assert self.assertEqual(self.producer.name, "producer") self.assertEqual(self.producer.id, "id") @@ -75,10 +74,7 @@ def test_controller_producer_none_priority(self) -> None: self.assertEqual(self.producer.priority, None) def test_get_max_power_profile(self) -> None: - """Test to check if get_max_power returns the profile value or the power esdl value if - no profile is present. - """ - + """Test to check if get_max_power returns the profile value or the power esdl value.""" # Arrange time = datetime(2021, 1, 1, 0, 0, 0) diff --git a/unit_test/entities/test_air_to_water_heat_pump.py b/unit_test/entities/test_air_to_water_heat_pump.py index 9eacb5fe..70301a44 100644 --- a/unit_test/entities/test_air_to_water_heat_pump.py +++ b/unit_test/entities/test_air_to_water_heat_pump.py @@ -281,7 +281,6 @@ def get_mass_flow_rate(_, i: int): def test_get_electric_consumption(self): """Test getting the electric power consumed by the heatpump.""" - # Arrange def get_internal_energy(_, i: int): if i == 0: