Skip to content
Draft
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
8,761 changes: 8,761 additions & 0 deletions examples/gas_electricity_network/input/HeatingDemand_W.csv

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions src/mesido/asset_sizing_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@ def __init__(self, *args, **kwargs):
self.__electricity_cable_topo_global_cable_class_count_map = {}
self.__electricity_cable_topo_global_cable_class_count_var_bounds = {}

# list with entry per ensemble member containing dicts of electricity cable parameter
# values for maximum current and resistance
self.__electricity_cable_topo_max_current_resistance_parameters = []

# Variable for the maximum size of an asset
self._asset_max_size_map = {}
self.__asset_max_size_var = {}
Expand Down Expand Up @@ -197,6 +201,7 @@ def pre(self):
for _ in range(self.ensemble_size):
self.__heat_pipe_topo_diameter_area_parameters.append({})
self.__heat_pipe_topo_heat_loss_parameters.append({})
self.__electricity_cable_topo_max_current_resistance_parameters.append({})

unique_pipe_classes = self.get_unique_pipe_classes()
for pc in unique_pipe_classes:
Expand Down Expand Up @@ -307,6 +312,14 @@ def pre(self):
)
if investment_cost == 0.0:
RuntimeWarning(f"{cable} has an investment cost of 0. €/m")

for ensemble_member in range(self.ensemble_size):
d = self.__electricity_cable_topo_max_current_resistance_parameters[
ensemble_member
]

d[f"{cable}.max_current"] = cable_classes[0].maximum_current
d[f"{cable}.resistance"] = resistance
else:
resistances = [c.resistance for c in cable_classes]
self.__electricity_cable_topo_resistance_var_bounds[res_var_name] = (
Expand Down Expand Up @@ -1051,6 +1064,13 @@ def get_optimized_pipe_class(self, pipe: str) -> PipeClass:
"""
return self.__heat_pipe_topo_pipe_class_result[pipe]

def get_optimized_electricity_cable_class(self, cable):
"""
Return the optimized electricity class for a specific cable. If no
optimized cable class is available (yet), a `KeyError` is returned.
"""
return self.__electricity_cable_topo_cable_class_result[cable]

def get_optimized_gas_pipe_class(self, pipe):
"""
Return the optimized gas pipe class for a specific pipe. If no
Expand Down Expand Up @@ -1216,6 +1236,10 @@ def parameters(self, ensemble_member):
# parameters in e.g. constraints when those are variable, we set them
# to NaN in that case. In post(), they are set to their resulting
# values once again.
if self.__electricity_cable_topo_max_current_resistance_parameters:
parameters.update(
self.__electricity_cable_topo_max_current_resistance_parameters[ensemble_member]
)
if self.__heat_pipe_topo_diameter_area_parameters:
parameters.update(self.__heat_pipe_topo_diameter_area_parameters[ensemble_member])
if self.__heat_pipe_topo_heat_loss_parameters:
Expand Down Expand Up @@ -2311,6 +2335,29 @@ def compiler_options(self):
options["resolve_parameter_values"] = True
return options

def __cable_class_to_results(self):
"""
This functions writes all resulting electricity cable class results to a dict.
"""
for ensemble_member in range(self.ensemble_size):
results = self.extract_results(ensemble_member)

for cable in self.energy_system_components.get("electricity_cable", []):
cable_classes = self.electricity_cable_classes(cable)

if not cable_classes:
continue
elif len(cable_classes) == 1:
cable_class = cable_classes[0]
else:
cable_class = next(
c
for c, s in self._electricity_cable_topo_cable_class_map[cable].items()
if round(results[s][0]) == 1.0
)

self.__electricity_cable_topo_cable_class_result[cable] = cable_class

def __pipe_class_to_results(self):
"""
This functions writes all resulting pipe class results to a dict.
Expand Down Expand Up @@ -2351,6 +2398,18 @@ def __pipe_class_to_results(self):

self.__gas_pipe_topo_pipe_class_result[pipe] = pipe_class

def __cable_max_current_and_resistance_to_parameters(self):
"""
This function is used to update the parameters object with the results of the cable class
optimization
"""
for ensemble_member in range(self.ensemble_size):
d = self.__electricity_cable_topo_max_current_resistance_parameters[ensemble_member]
for cable in self._electricity_cable_topo_cable_class_map:
cable_class = self.get_optimized_electricity_cable_class(cable)
d[f"{cable}.max_current"] = cable_class.maximum_current
d[f"{cable}.resistance"] = cable_class.resistance

def _pipe_heat_loss_to_parameters(self):
"""
This function is used to set the optimized milp losses in the parameters object.
Expand Down Expand Up @@ -2395,6 +2454,7 @@ def priority_completed(self, priority):
"""

self.__pipe_class_to_results()
self.__cable_class_to_results()

# The head loss mixin wants to do some check for the head loss
# minimization priority that involves the diameter/area. We assume
Expand Down Expand Up @@ -2422,3 +2482,6 @@ def post(self):
self.__pipe_class_to_results()
self.__pipe_diameter_to_parameters()
self._pipe_heat_loss_to_parameters()

self.__cable_class_to_results()
self.__cable_max_current_and_resistance_to_parameters()
36 changes: 21 additions & 15 deletions src/mesido/electricity_physics_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ def __voltage_loss_path_constraints(self, ensemble_member):
constraint_nominal = self.variable_nominal(v_loss)

# TODO: still have to check for proper scaling
options = self.energy_system_options()
if cable in self._electricity_cable_topo_cable_class_map.keys():
cable_classes = self._electricity_cable_topo_cable_class_map[cable]
variables = {
Expand All @@ -487,24 +488,29 @@ def __voltage_loss_path_constraints(self, ensemble_member):

for var_size, variable in variables.items():
if var_size != "None":
expr = resistances[var_size] * c_length * current
constraints.append(
(
(v_loss - expr + big_m * (1 - variable)) / constraint_nominal,
0.0,
np.inf,
if options["include_electric_cable_power_loss"]:
expr = resistances[var_size] * c_length * current
constraints.append(
(
(v_loss - expr + big_m * (1 - variable)) / constraint_nominal,
0.0,
np.inf,
)
)
)
constraints.append(
(
(v_loss - expr - big_m * (1 - variable)) / constraint_nominal,
-np.inf,
0.0,
constraints.append(
(
(v_loss - expr - big_m * (1 - variable)) / constraint_nominal,
-np.inf,
0.0,
)
)
)

else:
constraints.append(((v_loss) / v_nom, 0.0, 0.0))
else:
constraints.append(((v_loss - r * current) / constraint_nominal, 0.0, 0.0))
if options["include_electric_cable_power_loss"]:
constraints.append(((v_loss - r * current) / constraint_nominal, 0.0, 0.0))
else:
constraints.append(((v_loss) / v_nom, 0.0, 0.0))

return constraints

Expand Down
2 changes: 1 addition & 1 deletion src/mesido/esdl/asset_to_component_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def get_energy_content(asset_name, carrier) -> float:
elif str(NetworkSettings.NETWORK_TYPE_HYDROGEN).upper() in str(carrier.name).upper():
# This value can be lower / higher heating value depending on the case
# Currently the lower heating value is used below (120.0 MJ/kg)
energy_content_j_kg = 120.0 * 10.0**6 / density_kg_m3
energy_content_j_kg = 120.0 * 10.0**6
else:
raise logger.error(
f"Neither gas/hydrogen was used in the carrier " f"name of pipe {asset_name}."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def __init__(self, name, **modifiers):
# from: https://pandapower.readthedocs.io/en/v2.6.0/std_types/basic.html
self.max_current = nan
self.min_voltage = nan
self.max_voltage = self.min_voltage * 2.0
self.max_voltage = self.min_voltage #* 2.0 TODO: still to be fixed
self.nominal_current = nan
self.nominal_voltage = nan
self.r = nan
Expand Down
55 changes: 54 additions & 1 deletion src/mesido/workflows/gas_elect_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from mesido.esdl.esdl_mixin import ESDLMixin
from mesido.head_loss_class import HeadLossOption
from mesido.network_common import NetworkSettings
from mesido.pipe_class import CableClass
from mesido.techno_economic_mixin import TechnoEconomicMixin
from mesido.workflows.goals.minimize_tco_goal import MinimizeTCO
from mesido.workflows.io.write_output import ScenarioOutput
Expand Down Expand Up @@ -106,7 +107,7 @@ def energy_system_options(self):

# Setting when started with head loss inclusions
self.gas_network_settings["minimum_velocity"] = 0.0
self.gas_network_settings["maximum_velocity"] = 15.0
self.gas_network_settings["maximum_velocity"] = 60.0

# TODO: resolve scaling and potential other issues preventing HIGHS to optimize the system
# when LINEARIZED_N_LINES_EQUALITY head loss setting is used
Expand Down Expand Up @@ -186,6 +187,58 @@ def post(self):
solver_stats = self.solver_stats
self._write_json_output(results, parameters, bounds, aliases, solver_stats)

def electricity_cable_dict_to_classes(self, c, cables_dict, enabled_cable_type):
cable_classes_list = []
for cable_name, properties in cables_dict.items():
cable_classes_list.append(
CableClass(
name=cable_name,
maximum_current=properties["maximum_current"],
resistance=properties["resistance"],
investment_costs=properties["investment_costs"],
)
)

cable_state = self.parameters(0)[f"{c}.state"]
if cable_state == 0: # Disabled
for cable_class in cable_classes_list:
if cable_class.name == "None":
cable_list = [cable_class]

elif cable_state == 1: # Enabled
for cable_class in cable_classes_list:
if cable_class.name == enabled_cable_type:
cable_list = [cable_class]

elif cable_state == 2: # Optional
cable_list = cable_classes_list

return cable_list

def electricity_cable_classes(self, c):
cables_dict = {
"None": {
"maximum_current": 0.0,
"resistance": 0.0,
"investment_costs": 0.0,
},
"CableType1": {
"maximum_current": 11000.0,
"resistance": 3.0,
"investment_costs": 60000.0,
},
"CableType2": {
"maximum_current": 12000.0,
"resistance": 4.0,
"investment_costs": 65000.0,
},
}

enabled_cable_type = "CableType1"
cable_list = self.electricity_cable_dict_to_classes(c, cables_dict, enabled_cable_type)

return cable_list


@main_decorator
def main(runinfo_path, log_level):
Expand Down
Loading