Skip to content
Open
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
202 changes: 166 additions & 36 deletions src/mesido/heat_physics_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,14 +445,22 @@ def _get_min_bound(bound):
if len(temperature_regimes) == 0:
temperature = temperatures["temperature"]
self.__temperature_regime_var_bounds[temp_var_name] = (temperature, temperature)
elif len(temperature_regimes) == 1:
temperature = temperature_regimes[0]
self.__temperature_regime_var_bounds[temp_var_name] = (temperature, temperature)
else:
self.__temperature_regime_var_bounds[temp_var_name] = (
min(temperature_regimes),
max(temperature_regimes),
)
if max(temperature_regimes) >= temperatures["temperature"]:
logger.error(
f"The temperature provided for carrier with name "
f"{temperatures['name']} and id {temperatures['id']} is "
f"smaller than the largest value in the temperature regime "
f"provided for this carrier, please update the esdl."
)
if len(temperature_regimes) == 1:
temperature = temperature_regimes[0]
self.__temperature_regime_var_bounds[temp_var_name] = (temperature, temperature)
else:
self.__temperature_regime_var_bounds[temp_var_name] = (
min(temperature_regimes),
max(temperature_regimes),
)

for temperature_regime in temperature_regimes:
carrier_selected_var = carrier_id_number_mapping + f"_{temperature_regime}"
Expand Down Expand Up @@ -1503,50 +1511,112 @@ def __source_heat_to_discharge_path_constraints(self, ensemble_member):

sup_carrier = parameters[f"{s}.T_supply_id"]
supply_temperatures = self.temperature_regimes(sup_carrier)

big_m = 2.0 * self.bounds()[f"{s}.HeatOut.Heat"][1]
big_m = (
big_m
if big_m != np.inf
else 2.0 * self.bounds()[f"{s}.Heat_source"][1] * parameters[f"{s}.T_supply"] / dt
)

if len(supply_temperatures) == 0:
constraints.append(
(
(heat_out - discharge * cp * rho * parameters[f"{s}.T_supply"])
/ heat_nominal,
0.0,
0.0,
)
)
else:
for supply_temperature in supply_temperatures:
sup_temperature_is_selected = self.state(f"{sup_carrier}_{supply_temperature}")
# Check to see if the out carrier has a temperature profile assigned to it.
temp_out_profile, _, _, _ = self.__get_out_port_carrier_temp_profile(
parameters, s, "heat_source"
)

if temp_out_profile is None:
if len(supply_temperatures) == 0:
constraints.append(
(
(
heat_out
- discharge * cp * rho * supply_temperature
+ (1.0 - sup_temperature_is_selected) * big_m
)
/ constraint_nominal,
(heat_out - discharge * cp * rho * parameters[f"{s}.T_supply"])
/ heat_nominal,
0.0,
0.0,
np.inf,
)
)
constraints.append(
(
else:
for supply_temperature in supply_temperatures:
sup_temperature_is_selected = self.state(
f"{sup_carrier}_{supply_temperature}"
)

constraints.append(
(
heat_out
- discharge * cp * rho * supply_temperature
- (1.0 - sup_temperature_is_selected) * big_m
(
heat_out
- discharge * cp * rho * supply_temperature
+ (1.0 - sup_temperature_is_selected) * big_m
)
/ constraint_nominal,
0.0,
np.inf,
)
)
constraints.append(
(
(
heat_out
- discharge * cp * rho * supply_temperature
- (1.0 - sup_temperature_is_selected) * big_m
)
/ constraint_nominal,
-np.inf,
0.0,
)
/ constraint_nominal,
-np.inf,
0.0,
)

return constraints

def __source_heat_to_discharge_variable_temp_constraints(self, ensemble_member):
"""
Adds the same type of constraints to the source as
__source_heat_to_discharge_path_constraints for cases where the out carrier
has a prescribed temperature profile. An important difference is that these
are conventional constraints, since every timestep will have a specific value.
"""

constraints = []
parameters = self.parameters(ensemble_member)

for s in self.energy_system_components.get("heat_source", []):
heat_nominal = parameters[f"{s}.Heat_nominal"]
cp = parameters[f"{s}.cp"]
rho = parameters[f"{s}.rho"]
dt = parameters[f"{s}.dT"]

big_m = 2.0 * self.bounds()[f"{s}.HeatOut.Heat"][1]
big_m = (
big_m
if big_m != np.inf
else 2.0 * self.bounds()[f"{s}.Heat_source"][1] * parameters[f"{s}.T_supply"] / dt
)

temp_out_profile, sup_carrier_name, temp_out_prof_start_idx, temp_out_prof_end_idx = (
self.__get_out_port_carrier_temp_profile(parameters, s, "heat_source")
)

if (
temp_out_profile is not None
): # Case where the out carrier has a temp profile assigned to it.
heat_out_vector = self.__state_vector_scaled(f"{s}.HeatOut.Heat", ensemble_member)
discharge_vector = self.__state_vector_scaled(f"{s}.Q", ensemble_member)

constraints.append(
(
(
heat_out_vector
- discharge_vector
* cp
* rho
* temp_out_profile.values[
temp_out_prof_start_idx : temp_out_prof_end_idx + 1
]
)
/ heat_nominal,
0.0,
0.0,
)
)

return constraints

Expand Down Expand Up @@ -1617,6 +1687,56 @@ def __cold_demand_heat_to_discharge_path_constraints(self, ensemble_member):

return constraints

def __get_out_port_carrier_temp_profile(self, parameters, asset_name, asset_type):
"""
This function finds the carrier lined to the asset's out port and grabs the
temperature profile assigned to it, if there is one assigned to it.
It returns the temperature as a timeseries, the name of the carrier, and the
start and end index of the temperature profile according to the problem's timeseries
(this last one only relevant for problems that are sliced).
"""
# TODO: modify profile parser so the temperature profile is not called a price profile.
# TODO: modify this and the profile parser to incorporate the esdl option to
# use power instead of temperature.
# TODO: the start/end indices are needed for a very specific problem-times slicing case.
# Modify the way problems are sliced to remove this.

try:
carriers = self.esdl_carriers
carriers_ids = carriers.keys()
except AttributeError:
carriers = None
carriers_ids = []
sup_carrier_name = None
temp_out_profile = None
temp_out_prof_start_idx = None
temp_out_prof_end_idx = None
carrier_id_types = {"heat_source": ".T_supply_id", "heat_pipe": ".carrier_id"}
for carrier_id in carriers_ids:
if (
carriers[carrier_id]["id_number_mapping"]
== parameters[f"{asset_name}{carrier_id_types[asset_type]}"]
):
sup_carrier_name = carriers[carrier_id]["name"]
try:
temp_out_profile = self.get_timeseries(f"{sup_carrier_name}.price_profile")
temp_out_prof_start_idx = int(
np.where(
self.get_timeseries(f"{sup_carrier_name}.price_profile").times
== self.times()[0]
)[0]
)
temp_out_prof_end_idx = int(
np.where(
self.get_timeseries(f"{sup_carrier_name}.price_profile").times
== self.times()[-1]
)[0]
)
except KeyError:
pass

return temp_out_profile, sup_carrier_name, temp_out_prof_start_idx, temp_out_prof_end_idx

def __pipe_heat_to_discharge_path_constraints(self, ensemble_member):
"""
This function adds constraints linking the flow to the thermal power at the pipe assets.
Expand Down Expand Up @@ -1668,7 +1788,13 @@ def __pipe_heat_to_discharge_path_constraints(self, ensemble_member):
temperatures = self.temperature_regimes(carrier)

for heat in [scaled_heat_in, scaled_heat_out]:
if self.energy_system_options()["neglect_pipe_heat_losses"]:
temp_out_profile, _, _, _ = self.__get_out_port_carrier_temp_profile(
parameters, p, "heat_pipe"
)
if (
self.energy_system_options()["neglect_pipe_heat_losses"]
and temp_out_profile is None
):
temp = parameters[f"{p}.temperature"]
if len(temperatures) == 0:
constraints.append(
Expand Down Expand Up @@ -1705,7 +1831,7 @@ def __pipe_heat_to_discharge_path_constraints(self, ensemble_member):
0.0,
)
)
else:
elif not self.energy_system_options()["neglect_pipe_heat_losses"]:
# Note that during cold delivery the line can be colder than the ground
# temperature.
# In this case we have to bound the heat flowing in the line with the ground
Expand Down Expand Up @@ -3648,6 +3774,10 @@ def constraints(self, ensemble_member):
constraints.extend(self.__heat_matching_demand_insulation_constraints(ensemble_member))

constraints.extend(self.__ates_max_stored_heat_constriants(ensemble_member))
constraints.extend(
self.__source_heat_to_discharge_variable_temp_constraints(ensemble_member)
)

return constraints

def history(self, ensemble_member):
Expand Down
46 changes: 46 additions & 0 deletions tests/models/source_pipe_sink/input/timeseries_prod_test.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
DateTime,demand,elec,heat
19-5-2013 22:00,150000,10,70
19-5-2013 23:00,150000,10,72.0
20-5-2013 00:00,150000,10,74
20-5-2013 01:00,150000,10,76
20-5-2013 02:00,150000,10,80
20-5-2013 03:00,150000,10,82
20-5-2013 04:00,150000,10,84
20-5-2013 05:00,150000,10,86
20-5-2013 06:00,150000,10,82
20-5-2013 07:00,150000,10,80
20-5-2013 08:00,150000,10,78
20-5-2013 09:00,150000,10,76
20-5-2013 10:00,150000,10,74
20-5-2013 11:00,150000,10,70
20-5-2013 12:00,150000,10,70
20-5-2013 13:00,150000,10,70
20-5-2013 14:00,100000,10,70
20-5-2013 15:00,100000,10,70
20-5-2013 16:00,100000,10,70
20-5-2013 17:00,100000,10,70
20-5-2013 18:00,100000,10,70
20-5-2013 19:00,100000,10,70
20-5-2013 20:00,100000,10,70
20-5-2013 21:00,100000,10,70
20-5-2013 22:00,100000,10,70
20-5-2013 23:00,100000,10,70
21-5-2013 00:00,100000,10,70
21-5-2013 01:00,100000,10,70
21-5-2013 02:00,100000,10,70
21-5-2013 03:00,50000,10,70
21-5-2013 04:00,50000,10,70
21-5-2013 05:00,50000,10,70
21-5-2013 06:00,50000,10,70
21-5-2013 07:00,50000,10,70
21-5-2013 08:00,50000,10,70
21-5-2013 09:00,50000,10,70
21-5-2013 10:00,50000,10,70
21-5-2013 11:00,50000,10,70
21-5-2013 12:00,50000,10,70
21-5-2013 13:00,50000,10,70
21-5-2013 14:00,50000,10,70
21-5-2013 15:00,50000,10,70
21-5-2013 16:00,50000,10,70
21-5-2013 17:00,50000,10,70
21-5-2013 18:00,50000,10,70
57 changes: 57 additions & 0 deletions tests/models/source_pipe_sink/model/sourcesink_prof_test.esdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?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" esdlVersion="v2401" name="sourcesink with return network with return network" version="2" id="5d539f68-f98e-466b-9ff5-b908a211e0ab_with_return_network_with_return_network" description="">
<energySystemInformation xsi:type="esdl:EnergySystemInformation" id="11f4eafa-7fbc-4d82-b346-e893326d2c30">
<carriers xsi:type="esdl:Carriers" id="eafbd8f4-1fde-4bb5-8dce-fdb74a1a1097">
<carrier xsi:type="esdl:HeatCommodity" id="435a0034-fab0-4e7e-9a17-edf8de9a2b11" supplyTemperature="70.0" name="heat"/>
<carrier xsi:type="esdl:HeatCommodity" returnTemperature="40.0" id="435a0034-fab0-4e7e-9a17-edf8de9a2b11_ret" name="heat_ret"/>
<carrier xsi:type="esdl:ElectricityCommodity" id="355f3a2f-dac4-4484-b3db-a99b086cbe39" voltage="400.0" name="elec"/>
</carriers>
</energySystemInformation>
<instance xsi:type="esdl:Instance" id="90e7e098-038e-4462-89fe-a8852c501753" name="Untitled instance">
<area xsi:type="esdl:Area" name="Untitled area" id="4fd1adc2-5371-4ab7-806a-b40e49d127e9">
<asset xsi:type="esdl:HeatProducer" name="source" id="a479e4e6-6f75-460d-aeb2-d0e3e02314e0" power="10000000.0">
<port xsi:type="esdl:OutPort" id="b0b1a87c-7b5a-4edb-a732-274d1bf69647" connectedTo="3b5636b1-7b14-46bd-bb27-c0718350b418" carrier="435a0034-fab0-4e7e-9a17-edf8de9a2b11" name="Out"/>
<port xsi:type="esdl:InPort" id="622d7e19-e360-46af-bfbf-eb35ec14548b" carrier="435a0034-fab0-4e7e-9a17-edf8de9a2b11_ret" name="In" connectedTo="0f11bb2d-fb28-4f9d-8992-8d5901f579d7"/>
<geometry xsi:type="esdl:Point" CRS="WGS84" lat="52.08646829489945" lon="4.386527538299561"/>
</asset>
<asset xsi:type="esdl:HeatingDemand" name="demand" id="f6d5923d-ba9a-409d-80a0-26f73b2a574b" power="10000000.0">
<port xsi:type="esdl:InPort" id="b8849fb5-fe97-48d9-91a8-9abcbf365738" carrier="435a0034-fab0-4e7e-9a17-edf8de9a2b11" name="In" connectedTo="76679c8a-43ec-4f6d-81c4-9b43e21696cc"/>
<port xsi:type="esdl:OutPort" id="eb68d4fe-b361-4e64-9f54-a1e05e5712ee" connectedTo="0f4bf90b-218d-4d45-a83e-97a8a6a187af" carrier="435a0034-fab0-4e7e-9a17-edf8de9a2b11_ret" name="Out"/>
<geometry xsi:type="esdl:Point" CRS="WGS84" lat="52.086586960901776" lon="4.398479461669923"/>
</asset>
<asset xsi:type="esdl:Pipe" name="Pipe1" length="1000.0" related="Pipe1_ret" outerDiameter="0.45" id="Pipe1" diameter="DN300" innerDiameter="0.15">
<costInformation xsi:type="esdl:CostInformation" id="a5e06a9f-ad3d-4c95-afcf-28ce7f772ec3">
<investmentCosts xsi:type="esdl:SingleValue" id="1e93bdda-8a74-42d5-960d-d64e4dff2025" value="1962.1" name="Combined investment and installation costs">
<profileQuantityAndUnit xsi:type="esdl:QuantityAndUnitType" id="983f0959-8566-43ce-a380-782d29406ed3" unit="EURO" description="Costs in EUR/m" physicalQuantity="COST" perUnit="METRE"/>
</investmentCosts>
</costInformation>
<port xsi:type="esdl:InPort" id="3b5636b1-7b14-46bd-bb27-c0718350b418" carrier="435a0034-fab0-4e7e-9a17-edf8de9a2b11" name="In" connectedTo="b0b1a87c-7b5a-4edb-a732-274d1bf69647"/>
<port xsi:type="esdl:OutPort" id="76679c8a-43ec-4f6d-81c4-9b43e21696cc" connectedTo="b8849fb5-fe97-48d9-91a8-9abcbf365738" carrier="435a0034-fab0-4e7e-9a17-edf8de9a2b11" name="Out"/>
<geometry xsi:type="esdl:Line" CRS="WGS84">
<point xsi:type="esdl:Point" lat="52.08646829489945" lon="4.386527538299561"/>
<point xsi:type="esdl:Point" lat="52.086586960901776" lon="4.398479461669923"/>
</geometry>
<material xsi:type="esdl:CompoundMatter" compoundType="LAYERED">
<component xsi:type="esdl:CompoundMatterComponent" layerWidth="0.0056">
<matter xsi:type="esdl:Material" id="f4cee538-cc3b-4809-bd66-979f2ce9649b" thermalConductivity="52.15" name="steel"/>
</component>
<component xsi:type="esdl:CompoundMatterComponent" layerWidth="0.05785">
<matter xsi:type="esdl:Material" id="e4c0350c-cd79-45b4-a45c-6259c750b478" thermalConductivity="0.027" name="PUR"/>
</component>
<component xsi:type="esdl:CompoundMatterComponent" layerWidth="0.0052">
<matter xsi:type="esdl:Material" id="9a97f588-10fe-4a34-b0f2-277862151763" thermalConductivity="0.4" name="HDPE"/>
</component>
</material>
<dataSource xsi:type="esdl:DataSource" attribution="https://www.logstor.com/media/6506/product-catalogue-uk-202003.pdf" name="Logstor Product Catalogue Version 2020.03"/>
</asset>
<asset xsi:type="esdl:Pipe" name="Pipe1_ret" length="1000.0" related="Pipe1" outerDiameter="0.45" id="Pipe1_ret" diameter="DN300" innerDiameter="0.15">
<port xsi:type="esdl:InPort" id="0f4bf90b-218d-4d45-a83e-97a8a6a187af" carrier="435a0034-fab0-4e7e-9a17-edf8de9a2b11_ret" name="In_ret" connectedTo="eb68d4fe-b361-4e64-9f54-a1e05e5712ee"/>
<port xsi:type="esdl:OutPort" id="0f11bb2d-fb28-4f9d-8992-8d5901f579d7" connectedTo="622d7e19-e360-46af-bfbf-eb35ec14548b" carrier="435a0034-fab0-4e7e-9a17-edf8de9a2b11_ret" name="Out_ret"/>
<geometry xsi:type="esdl:Line">
<point xsi:type="esdl:Point" CRS="WGS84" lat="52.086676960991774" lon="4.39796569977892"/>
<point xsi:type="esdl:Point" CRS="WGS84" lat="52.086558294989445" lon="4.386013537838319"/>
</geometry>
</asset>
</area>
</instance>
</esdl:EnergySystem>
Loading