Skip to content

Commit

Permalink
bugfixes in esdl_heat_model with information parsing for hydrogen, al…
Browse files Browse the repository at this point in the history
…so retrieving info from coolprop and in electricity_physics_mixin to utilise bigm on minimum load of electrolyser
  • Loading branch information
FJanssen-TNO committed Mar 19, 2024
1 parent aba8308 commit 1bf9162
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 53 deletions.
10 changes: 7 additions & 3 deletions src/rtctools_heat_network/electricity_physics_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,11 @@ def __electrolyzer_path_constaint(self, ensemble_member):
power_consumed_vect = ca.repmat(power_consumed, len(linear_coef_a))
gas_mass_flow_out_vect = ca.repmat(gas_mass_flow_out, len(linear_coef_a))
gass_mass_out_linearized_vect = linear_coef_a * power_consumed_vect + linear_coef_b
var_name = self.__asset_is_switched_on_map[asset]
asset_is_switched_on = self.state(var_name)

gass_mass_out_max = linear_coef_a[-1] * self.bounds()[f"{asset}.Power_consumed"][1] + linear_coef_b[-1]
big_m = gass_mass_out_max * 2
nominal = (
self.variable_nominal(f"{asset}.Gas_mass_flow_out")
* min(linear_coef_a)
Expand All @@ -489,7 +494,7 @@ def __electrolyzer_path_constaint(self, ensemble_member):
constraints.extend(
[
(
(gas_mass_flow_out_vect - gass_mass_out_linearized_vect) / nominal,
(gas_mass_flow_out_vect - gass_mass_out_linearized_vect - (1 - asset_is_switched_on) * big_m) / nominal,
-np.inf,
0.0,
),
Expand All @@ -498,8 +503,7 @@ def __electrolyzer_path_constaint(self, ensemble_member):

# Add constraints to ensure the electrolyzer is switched off when it reaches a power
# input below the minimum operating value
var_name = self.__asset_is_switched_on_map[asset]
asset_is_switched_on = self.state(var_name)


big_m = self.bounds()[f"{asset}.ElectricityIn.Power"][1] * 1.5 * 10.0
constraints.append(
Expand Down
8 changes: 4 additions & 4 deletions src/rtctools_heat_network/esdl/esdl_heat_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def get_density(self, asset_name, carrier):
"T",
273.15 + temperature,
"P",
carrier.pressure,
carrier.pressure*1e5,
NetworkSettings.NETWORK_COMPOSITION_GAS,
)
elif NetworkSettings.NETWORK_TYPE_HYDROGEN in carrier.name:
Expand All @@ -110,7 +110,7 @@ def get_density(self, asset_name, carrier):
"T",
273.15 + temperature,
"P",
carrier.pressure,
carrier.pressure*1e5,
str(NetworkSettings.NETWORK_TYPE_HYDROGEN).upper(),
)
else:
Expand Down Expand Up @@ -1216,7 +1216,7 @@ def convert_gas_demand(self, asset: Asset) -> Tuple[Type[GasDemand], MODIFIERS]:
modifiers = dict(
Q_nominal=self._get_connected_q_nominal(asset),
id_mapping_carrier=id_mapping,
Gas_demand_mass_flow=dict(min=0., max=asset.attributes["power"]*hydrogen_specfic_energy),
# Gas_demand_mass_flow=dict(min=0., max=asset.attributes["power"]*hydrogen_specfic_energy),
density=self.get_density(asset.name, asset.in_ports[0].carrier),
GasIn=dict(
Q=dict(
Expand Down Expand Up @@ -1344,7 +1344,7 @@ def convert_gas_tank_storage(self, asset: Asset) -> Tuple[Type[GasTankStorage],
Q_nominal=self._get_connected_q_nominal(asset),
density=self.get_density(asset.name, asset.in_ports[0].carrier),
volume=asset.attributes["workingVolume"],
Gas_tank_flow=dict(min=-hydrogen_specific_energy*asset.attributes["maxDischargeRate"], max=hydrogen_specific_energy*asset.attributes["maxChargeRate"]),
# Gas_tank_flow=dict(min=-hydrogen_specific_energy*asset.attributes["maxDischargeRate"], max=hydrogen_specific_energy*asset.attributes["maxChargeRate"]),
# TODO: Fix -> Gas network is currenlty non-limiting, mass flow is decoupled from the
# volumetric flow
# Gas_tank_flow=dict(
Expand Down
10 changes: 5 additions & 5 deletions tests/models/emerge/model/emerge.esdl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<esdl:EnergySystem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:esdl="http://www.tno.nl/esdl" esdlVersion="v2401" name="Untitled EnergySystem" version="4" id="415d9426-dae2-4194-88ad-607793871425" description="">
<energySystemInformation xsi:type="esdl:EnergySystemInformation" id="7997daa8-8ded-4f31-96b2-6842510f43f2">
<carriers xsi:type="esdl:Carriers" id="6e119417-37a2-4dfd-a493-95f4b68d0235">
<carrier xsi:type="esdl:ElectricityCommodity" id="e1077d81-b32a-4004-9a33-db65c96b5f4c" voltage="132000.0" name="Elec"/>
<carrier xsi:type="esdl:GasCommodity" id="14831e6c-c3bb-4763-8300-9658a365ee54" pressure="30.0" name="Hydrogen"/>
<carrier xsi:type="esdl:ElectricityCommodity" id="e1077d81-b32a-4004-9a33-db65c96b5f4c" voltage="50000.0" name="Elec"/>
<carrier xsi:type="esdl:GasCommodity" id="14831e6c-c3bb-4763-8300-9658a365ee54" pressure="15.0" name="Hydrogen"/>
</carriers>
</energySystemInformation>
<instance xsi:type="esdl:Instance" id="791c9938-3b6c-47b3-bcc5-0328ffd687bd" name="Untitled Instance">
Expand Down Expand Up @@ -70,23 +70,23 @@
<port xsi:type="esdl:OutPort" id="c3a78fa8-b482-4bcd-bad2-fd4c971b2565" connectedTo="f613dd95-9882-45a3-a569-4cd723444c90 1ab8ab85-2824-4606-a2ac-709596d8367b" carrier="14831e6c-c3bb-4763-8300-9658a365ee54" name="Out"/>
<geometry xsi:type="esdl:Point" CRS="WGS84" lat="52.5992941670283" lon="3.750457763671875"/>
</asset>
<asset xsi:type="esdl:Pipe" name="Pipe_ec1a" id="ec1a89cf-c7fb-4574-a93b-048b9d06640a" innerDiameter="50.0" length="7646.2">
<asset xsi:type="esdl:Pipe" name="Pipe_ec1a" id="ec1a89cf-c7fb-4574-a93b-048b9d06640a" diameter="DN1200" length="7646.2">
<port xsi:type="esdl:InPort" id="5e660a4f-e8ca-4e9d-8568-6292d36b5994" carrier="14831e6c-c3bb-4763-8300-9658a365ee54" name="In" connectedTo="5e58c0e2-5db4-4f6a-8aab-2c880d499c14"/>
<port xsi:type="esdl:OutPort" id="73a0c91a-a167-4a3d-b7e3-7603d32db9ac" connectedTo="272d0690-117e-4f69-bd34-ed24b2dc6f0b" carrier="14831e6c-c3bb-4763-8300-9658a365ee54" name="Out"/>
<geometry xsi:type="esdl:Line" CRS="WGS84">
<point xsi:type="esdl:Point" lat="52.579688026538726" lon="3.6419677734375004"/>
<point xsi:type="esdl:Point" lat="52.5992941670283" lon="3.750457763671875"/>
</geometry>
</asset>
<asset xsi:type="esdl:Pipe" name="Pipe_97ee" id="97ee0a46-1a47-4bec-b131-c0a13246f298" innerDiameter="50.0" length="5839.7">
<asset xsi:type="esdl:Pipe" name="Pipe_97ee" id="97ee0a46-1a47-4bec-b131-c0a13246f298" diameter="DN1200" length="5839.7">
<port xsi:type="esdl:InPort" id="f613dd95-9882-45a3-a569-4cd723444c90" carrier="14831e6c-c3bb-4763-8300-9658a365ee54" name="In" connectedTo="c3a78fa8-b482-4bcd-bad2-fd4c971b2565"/>
<port xsi:type="esdl:OutPort" id="0e6e6a7d-5b9a-42ff-8902-4e8db530b248" connectedTo="b2bd6049-3e2e-443c-aa36-ba893135cf82" carrier="14831e6c-c3bb-4763-8300-9658a365ee54" name="Out"/>
<geometry xsi:type="esdl:Line" CRS="WGS84">
<point xsi:type="esdl:Point" lat="52.5992941670283" lon="3.750457763671875"/>
<point xsi:type="esdl:Point" lat="52.566334145326486" lon="3.8177490234375004"/>
</geometry>
</asset>
<asset xsi:type="esdl:Pipe" name="Pipe_910d" id="910d3cd0-715a-423c-b67a-52fb967c85b4" innerDiameter="50.0" length="64730.1">
<asset xsi:type="esdl:Pipe" name="Pipe_910d" id="910d3cd0-715a-423c-b67a-52fb967c85b4" diameter="DN1200" length="64730.1">
<port xsi:type="esdl:InPort" id="1ab8ab85-2824-4606-a2ac-709596d8367b" carrier="14831e6c-c3bb-4763-8300-9658a365ee54" name="In" connectedTo="c3a78fa8-b482-4bcd-bad2-fd4c971b2565"/>
<port xsi:type="esdl:OutPort" id="f0b7ab61-b983-45ed-9274-887ccaadfce0" connectedTo="121a1b8d-758b-4b35-92fa-894435965e85" carrier="14831e6c-c3bb-4763-8300-9658a365ee54" name="Out"/>
<geometry xsi:type="esdl:Line" CRS="WGS84">
Expand Down
97 changes: 56 additions & 41 deletions tests/models/emerge/src/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from rtctools.optimization.collocated_integrated_optimization_problem import (
CollocatedIntegratedOptimizationProblem,
)
from rtctools.optimization.goal_programming_mixin import GoalProgrammingMixin
from rtctools.optimization.goal_programming_mixin_base import Goal
from rtctools.optimization.linearized_order_goal_programming_mixin import (
LinearizedOrderGoalProgrammingMixin,
Expand Down Expand Up @@ -73,9 +74,9 @@ def __init__(self, asset_name: str):
----------
source : string of the source name that is going to be minimized
"""
self.target_max = 0.0
self.function_range = (0.0, 1.0e8)
self.function_nominal = 1.0e7
# self.target_max = 0
# self.function_range = (-1.0e9, 1.0e9)
# self.function_nominal = 1.0e7

self.asset_name = asset_name

Expand All @@ -94,6 +95,7 @@ def function(
-------
The negative hydrogen production state of the optimization problem.
"""
#TODO: not yet scaled
return -optimization_problem.extra_variable(f"{self.asset_name}__revenue", ensemble_member)

class MinCost(Goal):
Expand All @@ -104,7 +106,7 @@ class MinCost(Goal):

def __init__(self, asset_name: str):
self.target_max = 0.0
self.function_range = (0.0, 1.0e8)
self.function_range = (0.0, 1.0e9)
self.function_nominal = 1.0e7

self.asset_name = asset_name
Expand All @@ -116,7 +118,7 @@ def function(self, optimization_problem: CollocatedIntegratedOptimizationProblem

class EmergeTest(
ESDLAdditionalVarsMixin,
PhysicsMixin,
TechnoEconomicMixin,
LinearizedOrderGoalProgrammingMixin,
SinglePassGoalProgrammingMixin,
ESDLMixin,
Expand All @@ -135,43 +137,56 @@ def __init__(self, *args, **kwargs):
self.gas_network_settings["head_loss_option"] = HeadLossOption.NO_HEADLOSS


def path_goals(self):
"""
This function adds the minimization goal for minimizing the heat production.
# def path_goals(self):
# """
# This function adds the minimization goal for minimizing the heat production.
#
# Returns
# -------
# The appended list of goals
# """
# goals = super().path_goals().copy()
#
# for s in self.energy_system_components["electrolyzer"]: # ["name_electrolyzer_1", "name_electrolyzer_2", ...]
# goals.append(MaxHydrogenProduction(s))
#
# return goals

def goals(self):

goals = super().goals().copy()

for asset_name in self.energy_system_components["electricity_demand"]:
goals.append(MaxRevenue(asset_name))
# goals.append(MinCost(asset_name))

for asset_name in self.energy_system_components["gas_demand"]:
goals.append(MaxRevenue(asset_name))
# goals.append(MinCost(asset_name))

# for asset_name in [*self.energy_system_components.get("electricity_source", []),
# *self.energy_system_components.get("gas_tank_storage", []),
# #TODO: battery
# *self.energy_system_components.get("electrolyzer", []),
# *self.energy_system_components.get("heat_pump_elec", [])]:
# goals.append(MinCost(asset_name))

Returns
-------
The appended list of goals
"""
goals = super().path_goals().copy()

for s in self.energy_system_components["electrolyzer"]: # ["name_electrolyzer_1", "name_electrolyzer_2", ...]
goals.append(MaxHydrogenProduction(s))

return goals

# def goals(self):
#
# goals = super().goals().copy()
#
# for asset_name in self.energy_system_components["electricity_demand"]:
# goals.append(MaxRevenue(asset_name))
# # goals.append(MinCost(asset_name))
#
# for asset_name in self.energy_system_components["gas_demand"]:
# goals.append(MaxRevenue(asset_name))
# # goals.append(MinCost(asset_name))
#
# # for asset_name in [*self.energy_system_components.get("electricity_source", []),
# # *self.energy_system_components.get("gas_tank_storage", []),
# # #TODO: battery
# # *self.energy_system_components.get("electrolyzer", []),
# # *self.energy_system_components.get("heat_pump_elec", [])]:
# # goals.append(MinCost(asset_name))
# #
#
#
# return goals
def constraints(self, ensemble_member):
constraints = super().constraints(ensemble_member)

for gs in self.energy_system_components.get("gas_tank_storage", []):
canonical, sign = self.alias_relation.canonical_signed(f"{gs}.Stored_gas_mass")
storage_t0 = sign * self.state_vector(canonical, ensemble_member)[0]
constraints.append((storage_t0, 0.0, 0.0))
canonical, sign = self.alias_relation.canonical_signed(f"{gs}.Gas_tank_flow")
gas_flow_t0 = sign * self.state_vector(canonical, ensemble_member)[0]
constraints.append((gas_flow_t0, 0.0, 0.0))

return constraints

def solver_options(self):
"""
Expand All @@ -185,8 +200,8 @@ def solver_options(self):
options["solver"] = "gurobi"
return options

# def times(self, variable=None):
# return super().times(variable)[:25]
def times(self, variable=None):
return super().times(variable)[:25]

def energy_system_options(self):
"""
Expand All @@ -208,10 +223,10 @@ def energy_system_options(self):
if __name__ == "__main__":
elect = run_optimization_problem(
EmergeTest,
esdl_file_name="h2.esdl",
esdl_file_name="emerge.esdl",
esdl_parser=ESDLFileParser,
profile_reader=ProfileReaderFromFile,
input_timeseries_file="timeseries_h2.csv",
input_timeseries_file="timeseries.csv",
)
results = elect.extract_results()
a = 1

0 comments on commit 1bf9162

Please sign in to comment.