Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.

"""Module containing the Esdl to asset mapper class."""
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.esdl_asset_object import EsdlAssetObject
from omotes_simulator_core.simulation.mappers.mappers import EsdlMapperAbstract

Expand All @@ -29,7 +36,9 @@ 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.
Expand All @@ -41,13 +50,27 @@ 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_constraint():
profile = esdl_asset.get_constraint_max_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:
resampled_profile = pd.DataFrame()

contr_producer = ControllerProducer(
name=esdl_asset.esdl_asset.name,
identifier=esdl_asset.esdl_asset.id,
temperature_in=temperature_in,
temperature_out=temperature_out,
power=power,
marginal_costs=marginal_costs,
profile=resampled_profile, # Empty DataFrame is added if there is no profile.
priority=strategy_priority,
)
return contr_producer
Original file line number Diff line number Diff line change
Expand Up @@ -116,24 +116,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.
Expand All @@ -149,7 +149,7 @@ def set_supply(self, factor: float = 1, priority: int = 0) -> dict:
continue
# Discharging (e.g., heat from component/system to the network) is negative.
producers[source.id] = {
PROPERTY_HEAT_DEMAND: -1 * source.power * factor,
PROPERTY_HEAT_DEMAND: -1 * source.get_max_power(time) * factor,
PROPERTY_TEMPERATURE_OUT: source.temperature_out,
PROPERTY_TEMPERATURE_IN: source.temperature_in,
PROPERTY_SET_PRESSURE: False,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Module containing the classes for the controller."""

import datetime

import pandas as pd

from omotes_simulator_core.entities.assets.controller.asset_controller_abstract import (
AssetControllerAbstract,
)
Expand All @@ -30,6 +34,7 @@ def __init__(
temperature_out: float,
power: float,
marginal_costs: float,
profile: pd.DataFrame,
priority: None | int = 1,
):
"""Constructor for the source.
Expand All @@ -48,3 +53,20 @@ 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

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
25 changes: 24 additions & 1 deletion src/omotes_simulator_core/entities/assets/esdl_asset_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,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])
Expand All @@ -101,6 +101,15 @@ def get_profile(self) -> pd.DataFrame:
)
raise ValueError(f"No profile found for asset: {self.esdl_asset.name}")

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
else:
return pd.DataFrame()
return get_data_from_profile(profile)

def get_sampling_method(self) -> ProfileSamplingMethod:
"""Get the interpolation method of the asset."""
# TODO: Get sampling method from ESDL properties if available
Expand Down Expand Up @@ -195,6 +204,20 @@ 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 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."""
if self.esdl_asset.constraint.items:
return True
else:
return False

def get_esdl_type(self) -> str:
"""Returns the ESDL type of the asset as a string."""
return StringEsdlAssetMapper().to_entity(type(self.esdl_asset))
Expand Down
23 changes: 12 additions & 11 deletions src/omotes_simulator_core/entities/network_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,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]
)
Expand All @@ -108,8 +108,7 @@ def update_setpoints(self, time: datetime.datetime) -> dict:
time,
)
factor = (total_supply + total_discharge_storage) / total_demand
# Define setpoints
producer_setpoints = self._set_producers_to_max()
producer_setpoints = self._set_producers_to_max(time)
storage_setpoints = self._set_all_storages_discharge_to_max()
consumer_setpoints = self._set_consumer_to_demand(time, factor=factor)
else:
Expand All @@ -122,19 +121,19 @@ def update_setpoints(self, time: datetime.datetime) -> dict:
surplus_supply = total_supply - total_demand
if surplus_supply <= total_charge_storage:
storage_setpoints = self._set_storages_charge_power(surplus_supply)
producer_setpoints = self._set_producers_to_max()
producer_setpoints = self._set_producers_to_max(time)
elif surplus_supply > total_charge_storage:
# need to cap the power of the source based on priority
storage_setpoints = self._set_storages_charge_power(total_charge_storage)
producer_setpoints = self._set_producers_based_on_priority(
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
# producer.
deficit_supply = total_demand - total_supply
storage_setpoints = self._set_storages_discharge_power(deficit_supply)
producer_setpoints = self._set_producers_to_max()
producer_setpoints = self._set_producers_to_max(time)

# Update the asset setpoints with the setpoints of the producers, consumers,
# and storages.
Expand Down Expand Up @@ -206,10 +205,10 @@ def update_setpoints(self, time: datetime.datetime) -> dict:

return asset_setpoints

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:
Expand Down Expand Up @@ -254,7 +253,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, 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
Expand All @@ -265,12 +266,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:
Expand Down
2 changes: 1 addition & 1 deletion src/omotes_simulator_core/infrastructure/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,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
Expand Down
54 changes: 54 additions & 0 deletions testdata/test1_prod_profile.esdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?xml version='1.0' encoding='UTF-8'?>
<esdl:EnergySystem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:esdl="http://www.tno.nl/esdl" id="fdbbf5ee-6e86-4c82-9926-4b59de482378_with_return_network" description="" esdlVersion="v2207" name="Untitled EnergySystem with return network" version="10">
<energySystemInformation xsi:type="esdl:EnergySystemInformation" id="c615f17e-c077-48c4-8a78-6ae05f8a908f">
<quantityAndUnits xsi:type="esdl:QuantityAndUnits" id="f61a1799-bf04-416a-b15e-93097722ada7">
<quantityAndUnit xsi:type="esdl:QuantityAndUnitType" physicalQuantity="POWER" id="e9405fc8-5e57-4df5-8584-4babee7cdf1b" multiplier="MEGA" unit="WATT" description="Power in MW"/>
<quantityAndUnit xsi:type="esdl:QuantityAndUnitType" physicalQuantity="ENERGY" id="12c481c0-f81e-49b6-9767-90457684d24a" multiplier="KILO" unit="WATTHOUR" description="Energy in kWh"/>
</quantityAndUnits>
<carriers xsi:type="esdl:Carriers" id="c27258b1-f4f6-4e09-a77a-ce466dbd82d2">
<carrier xsi:type="esdl:HeatCommodity" supplyTemperature="80.0" name="HeatSupply" id="0bd9cb08-2f69-4e97-8ac8-bd87b07e466a"/>
<carrier xsi:type="esdl:HeatCommodity" returnTemperature="40.0" name="HeatReturn" id="0bd9cb08-2f69-4e97-8ac8-bd87b07e466a_ret"/>
</carriers>
</energySystemInformation>
<instance xsi:type="esdl:Instance" id="a357cbbe-f277-42b1-8456-cbbadc8ceb2e" name="Untitled Instance">
<area xsi:type="esdl:Area" name="Untitled Area" id="e4002c22-abd5-43f6-81a8-e6b5f960bfa5">
<asset xsi:type="esdl:HeatingDemand" id="48f3e425-2143-4dcd-9101-c7e22559e82b" name="HeatingDemand_48f3">
<port xsi:type="esdl:InPort" id="af0904f7-ba1f-4e79-9040-71e08041601b" name="In" connectedTo="3f2dc09a-0cee-44bd-a337-cea55461a334" carrier="0bd9cb08-2f69-4e97-8ac8-bd87b07e466a"/>
<port xsi:type="esdl:OutPort" id="e890f65f-80e7-46fa-8c52-5385324bf686" name="Out" carrier="0bd9cb08-2f69-4e97-8ac8-bd87b07e466a_ret" connectedTo="422cb921-23d2-4410-9072-aaa5796a0620">
<profile xsi:type="esdl:InfluxDBProfile" endDate="2019-12-31T22:00:00.000000+0000" id="b77e41bc-a5ca-4823-b467-09872f2b6772" port="443" host="profiles.warmingup.info" filters="" startDate="2018-12-31T23:00:00.000000+0000" database="energy_profiles" measurement="WarmingUp default profiles" field="demand4_MW">
<profileQuantityAndUnit xsi:type="esdl:QuantityAndUnitReference" reference="e9405fc8-5e57-4df5-8584-4babee7cdf1b"/>
</profile>
</port>
<geometry xsi:type="esdl:Point" CRS="WGS84" lon="4.63726043701172" lat="52.158769628869045"/>
</asset>
<asset xsi:type="esdl:GenericProducer" power="5000000.0" id="cf3d4b5e-437f-4c1b-a7f9-7fd7e8a269b4" name="GenericProducer_cf3d">
<port xsi:type="esdl:InPort" id="9c258b9d-3149-4720-8931-f4bef1080ec1" name="In" connectedTo="935fb733-9f76-4a8d-8899-1ad8689a4b12" carrier="0bd9cb08-2f69-4e97-8ac8-bd87b07e466a_ret"/>
<port xsi:type="esdl:OutPort" id="2d818e3d-8a39-4cec-afa0-f6dbbfd50696" name="Out" carrier="0bd9cb08-2f69-4e97-8ac8-bd87b07e466a" connectedTo="a9793a5e-df4f-4795-8079-015dfaf57f82"/>
<geometry xsi:type="esdl:Point" CRS="WGS84" lon="4.558639526367188" lat="52.148869383489114"/>
<constraint xsi:type="esdl:ProfileConstraint" id="c78d01bc-d2ba-4ef3-934d-3876a98ceadd" name="test_prof" description="" attributeReference="power">
<maximum xsi:type="esdl:InfluxDBProfile" multiplier="0.1" measurement="WarmingUp default profiles" field="demand1_MW" host="https://profiles.warmingup.info" database="energy_profiles" filters="" startDate="2018-12-31T23:00:00.000000+0000" endDate="2019-12-31T22:00:00.000000+0000" id="7c1057e0-9493-47d0-9f79-2e17ca4f2a0d">
<profileQuantityAndUnit xsi:type="esdl:QuantityAndUnitReference" reference="e9405fc8-5e57-4df5-8584-4babee7cdf1b"/>
</maximum>
</constraint>
</asset>
<asset xsi:type="esdl:Pipe" id="Pipe1" length="6267.0" name="Pipe1" innerDiameter="0.1" related="Pipe1_ret">
<port xsi:type="esdl:InPort" id="a9793a5e-df4f-4795-8079-015dfaf57f82" name="In" connectedTo="2d818e3d-8a39-4cec-afa0-f6dbbfd50696" carrier="0bd9cb08-2f69-4e97-8ac8-bd87b07e466a"/>
<port xsi:type="esdl:OutPort" id="3f2dc09a-0cee-44bd-a337-cea55461a334" name="Out" connectedTo="af0904f7-ba1f-4e79-9040-71e08041601b" carrier="0bd9cb08-2f69-4e97-8ac8-bd87b07e466a"/>
<geometry xsi:type="esdl:Line" CRS="WGS84">
<point xsi:type="esdl:Point" lon="4.558639526367188" lat="52.148869383489114"/>
<point xsi:type="esdl:Point" lon="4.594688415527345" lat="52.16740421514521"/>
<point xsi:type="esdl:Point" lon="4.63726043701172" lat="52.158769628869045"/>
</geometry>
</asset>
<asset xsi:type="esdl:Pipe" id="Pipe1_ret" length="6267.0" name="Pipe1_ret" innerDiameter="0.1" related="Pipe1">
<port xsi:type="esdl:InPort" id="422cb921-23d2-4410-9072-aaa5796a0620" name="In_ret" connectedTo="e890f65f-80e7-46fa-8c52-5385324bf686" carrier="0bd9cb08-2f69-4e97-8ac8-bd87b07e466a_ret"/>
<port xsi:type="esdl:OutPort" id="935fb733-9f76-4a8d-8899-1ad8689a4b12" name="Out_ret" connectedTo="9c258b9d-3149-4720-8931-f4bef1080ec1" carrier="0bd9cb08-2f69-4e97-8ac8-bd87b07e466a_ret"/>
<geometry xsi:type="esdl:Line">
<point xsi:type="esdl:Point" CRS="WGS84" lon="4.636858896813017" lat="52.15885962895904"/>
<point xsi:type="esdl:Point" CRS="WGS84" lon="4.5942969754153795" lat="52.16749421523521"/>
<point xsi:type="esdl:Point" CRS="WGS84" lon="4.558225705568235" lat="52.14895938357911"/>
</geometry>
</asset>
</area>
</instance>
</esdl:EnergySystem>
Loading