Skip to content

Commit

Permalink
52 modelling temperature state in ates and constraining network tempe…
Browse files Browse the repository at this point in the history
…rature (#105)

Created HT-ATES models with varying temperature state,
Created the continuous and discrete temperature variables for ATES.
Added constraint to have same discrete ATES temperature as the chosen network temperature during discharging and network temperature should be equal or larger during charging. Temperature losses are created when discharging the ATES based on linearised inequalities from convex functions. Full explanation of philosophy is in the code.
To utilise this functionality, the option: include_ates_temperature_options needs to set to True. Default value is False as it requires specific setup of the problem.

Added limitation of the flowrate for sizing with aggregation count for ATES and also included limitation on heat discharge of ATES based on ATES temperature and the maximum flowrate.

Additional cyclic constraint on the temperature is required in the workflow when ATES temperatures are used.
Additional constraint in the workflow is required to ensure that only HEX or HP is active at the same moment in time, in the future this might be covered by compound assets.


bugfixes pipe heat to discharge constraints for varying temperature when option no_heat_losses is selected.

Inequality on continues and discrete temperature to ensure we can specifically select the boolean with the closest related variable, this does result in a slightly slower computation, but it ensures that in other configurations also the correct boolean is chosen. This allows to have large temperature steps even at smaller timesteps and thus solving relatively fast and will also allow to work with grow workflow with variable timestep.
In the future maybe look at a two-stage approach where first large timesteps are used as a hot-start or further constrained problem for the second stage with smaller timesteps or ordering of temperature booleans over time. Furthermore the problem is very challenging for open-source solvers.

---------

Co-authored-by: Rojer <jim.rojer@tno.nl>
Co-authored-by: Thijs van der Klauw <52158067+Tklauw-TNO@users.noreply.github.com>
Co-authored-by: Thijs van der Klauw <thijs.vanderklauw@tno.nl>
  • Loading branch information
4 people authored Mar 14, 2024
1 parent be29741 commit 8af21df
Show file tree
Hide file tree
Showing 10 changed files with 10,612 additions and 41 deletions.
55 changes: 38 additions & 17 deletions src/rtctools_heat_network/asset_sizing_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1918,6 +1918,30 @@ def __max_size_constraints(self, ensemble_member):

return constraints

def __add_optional_asset_path_constraints(
self, constraints, asset_name, nominal_value, nominal_var, single_power, state_var
):
aggregation_count = self.__asset_aggregation_count_var[
self._asset_aggregation_count_var_map[asset_name]
]
constraint_nominal = (nominal_value * nominal_var) ** 0.5

constraints.append(
(
(single_power * aggregation_count - state_var) / constraint_nominal,
0.0,
np.inf,
)
)
constraints.append(
(
(-single_power * aggregation_count - state_var) / constraint_nominal,
-np.inf,
0.0,
)
)
return constraints

def __optional_asset_path_constraints(self, ensemble_member):
"""
This function adds constraints that set the _aggregation_count variable. This variable is
Expand All @@ -1943,6 +1967,17 @@ def __optional_asset_path_constraints(self, ensemble_member):
*self.energy_system_components.get("geothermal", []),
*self.energy_system_components.get("ates", []),
]:
# changing flow bounds as a result of different aggregation count, additional
# step for geothermal and ates as they have subsurface flow limits.
state_var = self.state(f"{asset_name}.Q")
single_flow = (
bounds[f"{asset_name}.Q"][1] / parameters[f"{asset_name}.nr_of_doublets"]
)
nominal_value = 2.0 * bounds[f"{asset_name}.Q"][1]
nominal_var = self.variable_nominal(f"{asset_name}.Q")
constraints = self.__add_optional_asset_path_constraints(
constraints, asset_name, nominal_value, nominal_var, single_flow, state_var
)
state_var = self.state(f"{asset_name}.Heat_flow")
single_power = parameters[f"{asset_name}.single_doublet_power"]
nominal_value = 2.0 * bounds[f"{asset_name}.Heat_flow"][1]
Expand All @@ -1965,25 +2000,11 @@ def __optional_asset_path_constraints(self, ensemble_member):
single_power = bounds[f"{asset_name}.Heat_flow"][1]
nominal_value = single_power
nominal_var = self.variable_nominal(f"{asset_name}.Heat_flow")
aggregation_count = self.__asset_aggregation_count_var[
self._asset_aggregation_count_var_map[asset_name]
]
constraint_nominal = (nominal_value * nominal_var) ** 0.5

constraints.append(
(
(single_power * aggregation_count - state_var) / constraint_nominal,
0.0,
np.inf,
)
)
constraints.append(
(
(-single_power * aggregation_count - state_var) / constraint_nominal,
-np.inf,
0.0,
)
constraints = self.__add_optional_asset_path_constraints(
constraints, asset_name, nominal_value, nominal_var, single_power, state_var
)

elif parameters[f"{asset_name}.state"] == 1:
aggregation_count = self.__asset_aggregation_count_var[
self._asset_aggregation_count_var_map[asset_name]
Expand Down
36 changes: 31 additions & 5 deletions src/rtctools_heat_network/esdl/esdl_heat_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,13 +611,19 @@ def convert_heat_exchanger(self, asset: Asset) -> Tuple[Type[HeatExchanger], MOD
/ 2.0
)

max_heat_transport = (
params_t["Primary"]["T_supply"]
* max_power
/ (params_t["Primary"]["T_supply"] - params_t["Primary"]["T_return"])
)

prim_heat = dict(
HeatIn=dict(
Heat=dict(min=-max_power, max=max_power, nominal=max_power / 2.0),
Heat=dict(min=-max_heat_transport, max=max_heat_transport, nominal=max_power / 2.0),
Hydraulic_power=dict(nominal=params_q["Primary"]["Q_nominal"] * 16.0e5),
),
HeatOut=dict(
Heat=dict(min=-max_power, max=max_power, nominal=max_power / 2.0),
Heat=dict(min=-max_heat_transport, max=max_heat_transport, nominal=max_power / 2.0),
Hydraulic_power=dict(nominal=params_q["Primary"]["Q_nominal"] * 16.0e5),
),
Q_nominal=max_power
Expand All @@ -630,11 +636,11 @@ def convert_heat_exchanger(self, asset: Asset) -> Tuple[Type[HeatExchanger], MOD
)
sec_heat = dict(
HeatIn=dict(
Heat=dict(min=-max_power, max=max_power, nominal=max_power / 2.0),
Heat=dict(min=-max_heat_transport, max=max_heat_transport, nominal=max_power / 2.0),
Hydraulic_power=dict(nominal=params_q["Secondary"]["Q_nominal"] * 16.0e5),
),
HeatOut=dict(
Heat=dict(min=-max_power, max=max_power, nominal=max_power / 2.0),
Heat=dict(min=-max_heat_transport, max=max_heat_transport, nominal=max_power / 2.0),
Hydraulic_power=dict(nominal=params_q["Secondary"]["Q_nominal"] * 16.0e5),
),
Q_nominal=max_power
Expand Down Expand Up @@ -895,7 +901,16 @@ def convert_ates(self, asset: Asset) -> Tuple[Type[ATES], MODIFIERS]:
if not efficiency:
efficiency = 0.7

q_nominal = self._get_connected_q_nominal(asset)
# TODO: temporary value for standard dT on which capacity is based, Q in m3/s
temperatures = self._supply_return_temperature_modifiers(asset)
dt = temperatures["T_supply"] - temperatures["T_return"]
rho = self.rho
cp = self.cp
q_max_ates = hfr_discharge_max / (cp * rho * dt)

q_nominal = min(
self._get_connected_q_nominal(asset), q_max_ates * asset.attributes["aggregationCount"]
)

modifiers = dict(
technical_life=self.get_asset_attribute_value(
Expand All @@ -909,6 +924,17 @@ def convert_ates(self, asset: Asset) -> Tuple[Type[ATES], MODIFIERS]:
asset, "discountRate", default_value=0.0, min_value=0.0, max_value=100.0
),
Q_nominal=q_nominal,
Q=dict(
min=-q_max_ates * asset.attributes["aggregationCount"],
max=q_max_ates * asset.attributes["aggregationCount"],
nominal=q_nominal,
),
T_amb=asset.attributes["aquiferMidTemperature"],
Temperature_ates=dict(
min=temperatures["T_return"], # or potentially 0
max=temperatures["T_supply"],
nominal=temperatures["T_return"],
),
single_doublet_power=single_doublet_power,
heat_loss_coeff=(1.0 - efficiency ** (1.0 / 100.0)) / (3600.0 * 24.0),
state=self.get_state(asset),
Expand Down
Loading

0 comments on commit 8af21df

Please sign in to comment.