Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

193 set priority based on marginal costs #198

Merged
merged 13 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 12 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 @@ -86,6 +86,7 @@ def to_entity(self, esdl_asset: EsdlAssetObject) -> ControllerProducer:
power = result[0]
else:
raise ValueError("No power found for asset: " + esdl_asset.esdl_asset.name)
marginal_costs = esdl_asset.get_marginal_costs()
temperature_supply = esdl_asset.get_supply_temperature("Out")
temperature_return = esdl_asset.get_return_temperature("In")
contr_producer = ControllerProducer(
Expand All @@ -94,6 +95,7 @@ def to_entity(self, esdl_asset: EsdlAssetObject) -> ControllerProducer:
temperature_supply=temperature_supply,
temperature_return=temperature_return,
power=power,
marginal_costs=marginal_costs,
vanmeerkerk marked this conversation as resolved.
Show resolved Hide resolved
)
return contr_producer

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def __init__(
temperature_supply: float,
temperature_return: float,
power: float,
marginal_costs: float,
vanmeerkerk marked this conversation as resolved.
Show resolved Hide resolved
priority: int = 1,
):
"""Constructor for the source.
Expand All @@ -38,10 +39,12 @@ def __init__(
:param float temperature_supply: Supply temperature of the source.
:param float temperature_return: Return temperature of the source.
:param float power: Power of the source.
:param float marginal_costs: Marginal costs of the source.
:param int priority: Priority of the source.
"""
super().__init__(name, identifier)
self.temperature_return: float = temperature_return
vanmeerkerk marked this conversation as resolved.
Show resolved Hide resolved
self.temperature_supply: float = temperature_supply
self.power: float = power
self.marginal_costs: float = marginal_costs
self.priority: int = priority
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def set_setpoints(self, setpoints: Dict) -> None:
raise ValueError(
f"The setpoints {necessary_setpoints.difference(setpoints_set)} are missing."
)
self.thermal_power_allocation = setpoints[PROPERTY_HEAT_DEMAND]
self.thermal_power_allocation = -setpoints[PROPERTY_HEAT_DEMAND]
self.temperature_return_target = setpoints[PROPERTY_TEMPERATURE_RETURN]
self.temperature_supply = setpoints[PROPERTY_TEMPERATURE_SUPPLY]
adjusted_mass_flowrate = heat_demand_and_temperature_to_mass_flow(
Expand Down
16 changes: 16 additions & 0 deletions src/omotes_simulator_core/entities/assets/esdl_asset_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ def get_port_type(self, port_type: str) -> Type[esdl.Port]:
else:
raise ValueError(f"Port type not recognized: {port_type}")

def get_marginal_costs(self) -> float:
"""Get the marginal costs of the asset."""
if self.esdl_asset.costInformation is None:
logger.warning(
f"No cost information found for asset, Marginal costs set to 0 for: "
f"{self.esdl_asset.name}"
)
return 0
if self.esdl_asset.costInformation.marginalCosts is None:
logger.warning(
f"No marginal costs found for asset, Marginal costs set to 0 for: "
f"{self.esdl_asset.name}"
)
return 0
return float(self.esdl_asset.costInformation.marginalCosts.value)


def get_return_temperature(esdl_port: esdl.Port) -> float:
"""Get the temperature of the port."""
Expand Down
81 changes: 64 additions & 17 deletions src/omotes_simulator_core/entities/network_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import datetime
import logging
from typing import List

from omotes_simulator_core.entities.network_controller_abstract import NetworkControllerAbstract
from omotes_simulator_core.entities.assets.asset_defaults import (
PROPERTY_TEMPERATURE_SUPPLY,
Expand All @@ -40,10 +41,32 @@ def __init__(
consumers: List[ControllerConsumer],
storages: List[ControllerStorage],
) -> None:
"""Constructor for controller for a heat network."""
"""Constructor for controller for a heat network.

The priority of the producers is set based on the marginal costs. The lowest marginal
costs has the highest priority.

:param List[ControllerProducer] producers: List of producers in the network.
:param List[ControllerConsumer] consumers: List of consumers in the network.
:param List[ControllerStorage] storages: List of storages in the network.
"""
self.producers = producers
self.consumers = consumers
self.storages = storages
self._set_priority_from_marginal_costs()

vanmeerkerk marked this conversation as resolved.
Show resolved Hide resolved
def _set_priority_from_marginal_costs(self) -> None:
"""This method sets the priority of the producers based on the marginal costs.

The priority of the producers is set based on the marginal costs. The producer with the
lowest marginal costs has the highest priority (lowest value).
"""
# Created a sorted list of unique marginal costs.
unique_sorted_values = sorted(set([producer.marginal_costs for producer in self.producers]))

# set the priority based on the index of the marginal cost in the sorted list.
for producer in self.producers:
producer.priority = unique_sorted_values.index(producer.marginal_costs) + 1

def update_setpoints(self, time: datetime.datetime) -> dict:
"""Method to get the controller inputs for the network.
Expand All @@ -52,9 +75,13 @@ def update_setpoints(self, time: datetime.datetime) -> dict:
:return: dict with the key the asset id and the heat demand for that asset.
"""
# TODO add also the possibility to return mass flow rate instead of heat demand.
if (self.get_total_supply() + (
-1 * self.get_total_discharge_storage())) <= self.get_total_demand(time):
logger.warning(f"Total supply + storage is lower than total demand at time: {time}")
if (
vanmeerkerk marked this conversation as resolved.
Show resolved Hide resolved
self.get_total_supply() + (-1 * self.get_total_discharge_storage())
) <= self.get_total_demand(time):
logger.warning(
f"Total supply + storage is lower than total demand at time: {time}"
f"Consumers are capped to the available power."
)
producers = self._set_producers_to_max()
storages = self._set_all_storages_discharge_to_max()
consumers = self._set_consumer_capped(time)
Expand Down Expand Up @@ -138,6 +165,21 @@ def _set_producers_to_max(self) -> dict:
producers[self.producers[0].id][PROPERTY_SET_PRESSURE] = True
return producers

def _get_basic_producers(self) -> dict:
"""Method to get the basic dict with setting for the producers.

:return dict: Dict with key= asset-id and value=setpoints for the producers.
"""
producers = {}
for source in self.producers:
producers[source.id] = {
vanmeerkerk marked this conversation as resolved.
Show resolved Hide resolved
PROPERTY_HEAT_DEMAND: 0.0,
PROPERTY_TEMPERATURE_RETURN: source.temperature_return,
PROPERTY_TEMPERATURE_SUPPLY: source.temperature_supply,
PROPERTY_SET_PRESSURE: False,
}
return producers

def _set_all_storages_discharge_to_max(self) -> dict:
"""Method to set all the storages to the max discharge power.

Expand Down Expand Up @@ -218,30 +260,35 @@ def _set_producers_based_on_priority(self, time: datetime.datetime) -> dict:
:return: dict with the key the asset id and the value a dict with the set points for the
producers.
"""
producers = self._set_producers_to_max()
producers = self._get_basic_producers()
total_demand = self.get_total_demand(time) + self.get_total_charge_storage()
if total_demand > self.get_total_supply():
raise ValueError(
"Total demand is higher than total supply. "
"Cannot set producers based on priority."
logger.warning(
"Total demand is higher than total supply. Cannot set producers based on priority."
)

priority = 0
set_pressure = True
while total_demand > 0:
priority += 1
total_supply = self.get_total_supply_priority(priority)
priority_producers = [
producer for producer in self.producers if producer.priority == priority
]
if total_supply > total_demand:
factor = total_demand / total_supply
for source in self.producers:
if source.priority == priority:
producers[source.id][PROPERTY_HEAT_DEMAND] = source.power * factor
if set_pressure:
producers[source.id][PROPERTY_SET_PRESSURE] = True
set_pressure = False
for source in priority_producers:
producers[source.id][PROPERTY_HEAT_DEMAND] = source.power * factor
if set_pressure:
producers[source.id][PROPERTY_SET_PRESSURE] = True
set_pressure = False
total_demand = 0
else:
for source in self.producers:
if source.priority == priority:
producers[source.id][PROPERTY_HEAT_DEMAND] = source.power
for source in priority_producers:
producers[source.id][PROPERTY_HEAT_DEMAND] = source.power
total_demand -= total_supply
if set_pressure:
vanmeerkerk marked this conversation as resolved.
Show resolved Hide resolved
# set pressure has not been set and it needs to be set. This is set for the first
# producer
producers[self.producers[0].id][PROPERTY_SET_PRESSURE] = True
return producers
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def get_thermal_equations(self, connection_point: int) -> EquationObject:
"mass_flow_rate", connection_point=connection_point, use_relative_indexing=True
)
]
> 0
> 1e-5
):
return self.get_internal_energy_equation()
else:
Expand Down
Loading
Loading