Skip to content
Open
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
236 changes: 93 additions & 143 deletions src/mesido/asset_sizing_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -794,18 +794,22 @@ def pre(self):

# Making the variables for max size

def _make_max_size_var(name, lb, ub, nominal):
def _make_max_size_var(name: str, lb: float, ub: float, nominal: float) -> None:
"""
Creates max_size variables for the asset with the given lower and upper bound and the
nominal value.

"""
asset_max_size_var = f"{name}__max_size"
self._asset_max_size_map[name] = asset_max_size_var
self.__asset_max_size_var[asset_max_size_var] = ca.MX.sym(asset_max_size_var)
self.__asset_max_size_bounds[asset_max_size_var] = (lb, ub)
self.__asset_max_size_nominals[asset_max_size_var] = nominal

for asset_name in self.energy_system_components.get("heat_source", []):
ub = bounds[f"{asset_name}.Heat_source"][1]

# Update bound to account for profile constraint being used instead of 1 value

def _get_ub_profile_constraint(asset_name: str, profile_name: str, ub: float) -> float:
"""
Returns upper bound to account for profile constraint being used instead of 1 value
"""
asset = self.esdl_assets[asset_name]
asset_profile_constraints, qty_asset_profile_constraints = get_asset_contraints(
self, asset, esdl.ProfileConstraint
Expand All @@ -819,147 +823,93 @@ def _make_max_size_var(name, lb, ub, nominal):
== esdl.UnitEnum.WATT
and parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL # Optional asset
):
max_profile = max(self.get_timeseries(f"{asset_name}.maximum_heat_source").values)
max_profile = max(self.get_timeseries(f"{asset_name}.{profile_name}").values)
if ub > max_profile:
ub = max_profile

lb = 0.0 if parameters[f"{asset_name}.state"] != AssetStateEnum.ENABLED else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in self.energy_system_components.get("heat_demand", []):
ub = (
bounds[f"{asset_name}.Heat_demand"][1]
if not np.isinf(bounds[f"{asset_name}.Heat_demand"][1])
else bounds[f"{asset_name}.HeatIn.Heat"][1]
)
# Note that we only enforce the upper bound in state enabled if it was explicitly
# specified for the demand
lb = 0.0 if np.isinf(bounds[f"{asset_name}.Heat_demand"][1]) else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in self.energy_system_components.get("airco", []):
ub = bounds[f"{asset_name}.Heat_airco"][1]
# Note that we only enforce the upper bound in state enabled if it was explicitly
# specified for the demand
lb = 0.0 if parameters[f"{asset_name}.state"] != 1 else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in self.energy_system_components.get("cold_demand", []):
ub = (
bounds[f"{asset_name}.Cold_demand"][1]
if not np.isinf(bounds[f"{asset_name}.Cold_demand"][1])
else bounds[f"{asset_name}.HeatIn.Heat"][1]
)
# Note that we only enforce the upper bound in state enabled if it was explicitly
# specified for the demand
lb = 0.0 if np.isinf(bounds[f"{asset_name}.Cold_demand"][1]) else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in [
*self.energy_system_components.get("ates", []),
]:
ub = bounds[f"{asset_name}.Heat_ates"][1]
lb = 0.0 if parameters[f"{asset_name}.state"] != AssetStateEnum.ENABLED else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in self.energy_system_components.get("heat_buffer", []):
ub = (
max(bounds[f"{asset_name}.Stored_heat"][1].values)
if isinstance(bounds[f"{asset_name}.Stored_heat"][1], Timeseries)
else bounds[f"{asset_name}.Stored_heat"][1]
)
lb = 0.0 if parameters[f"{asset_name}.state"] != AssetStateEnum.ENABLED else ub
_make_max_size_var(
name=asset_name,
lb=lb,
ub=ub,
nominal=self.variable_nominal(f"{asset_name}.Stored_heat"),
return ub

def _scalarise_upper_bound(bound_ub):
"""
Scalarises the upperbound from timeserie to the maximum value of the timeserie.
"""
if isinstance(bound_ub, Timeseries):
return np.max(bound_ub.values)
return bound_ub if isinstance(bound_ub, float) else max(bound_ub.values)

def _demand_ub(asset_name: str, primary_suffix: str, secondary_suffix: str):
"""
Returns the upperbound of a variable with a backup variable name under which it might be
saved.

"""
demand_ub = bounds[f"{asset_name}.{primary_suffix}"][1]
return (
demand_ub
if not np.isinf(demand_ub)
else bounds[f"{asset_name}.{secondary_suffix}"][1]
)

for asset_name in [
*self.energy_system_components.get("heat_exchanger", []),
*self.energy_system_components.get("heat_pump", []),
]:
ub = bounds[f"{asset_name}.Secondary_heat"][1]
lb = 0.0 if parameters[f"{asset_name}.state"] != AssetStateEnum.ENABLED else ub
_make_max_size_var(
name=asset_name,
lb=lb,
ub=ub,
nominal=self.variable_nominal(f"{asset_name}.Secondary_heat"),
)

for asset_name in self.energy_system_components.get("gas_demand", []):
# TODO: add bound value for mass flow rate, used 1.0 for now instead of 0.0 which
# Note that we set the nominal to one to avoid division by zero
ub = 0.0
lb = 0.0 if parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=1.0)

for asset_name in self.energy_system_components.get("gas_source", []):
# TODO: add bound value for mass flow rate, used 1.0 for now instead of 0.0 which
# Note that we set the nominal to one to avoid division by zero
ub = 0.0
lb = 0.0 if parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=1.0)

for asset_name in self.energy_system_components.get("gas_tank_storage", []):
ub = bounds[f"{asset_name}.Stored_gas_mass"][1]
ub = ub if isinstance(ub, float) else max(ub.values)
lb = 0.0 if parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in self.energy_system_components.get("gas_substation", []):
ub = bounds[f"{asset_name}.GasIn.Q"][1]
ub = ub if isinstance(ub, float) else max(ub.values)
lb = 0.0 if parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in self.energy_system_components.get("compressor", []):
ub = bounds[f"{asset_name}.GasIn.Q"][1]
ub = ub if isinstance(ub, float) else max(ub.values)
lb = 0.0 if parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in self.energy_system_components.get("electrolyzer", []):
ub = bounds[f"{asset_name}.ElectricityIn.Power"][1]
ub = ub if isinstance(ub, float) else max(ub.values)
lb = 0.0 if parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in self.energy_system_components.get("electricity_demand", []):
v = bounds[f"{asset_name}.Electricity_demand"][1]
ub = v if not np.isinf(v) else bounds[f"{asset_name}.ElectricityIn.Power"][1]
ub = ub if isinstance(ub, float) else max(ub.values)
lb = 0.0 if parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in self.energy_system_components.get("transformer", []):
ub = bounds[f"{asset_name}.ElectricityIn.Power"][1]
ub = ub if isinstance(ub, float) else max(ub.values)
lb = 0.0 if parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in self.energy_system_components.get("electricity_source", []):
ub = (
bounds[f"{asset_name}.Electricity_source"][1]
if not isinstance(bounds[f"{asset_name}.Electricity_source"][1], Timeseries)
else np.max(bounds[f"{asset_name}.Electricity_source"][1].values)
)
lb = 0.0 if parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

for asset_name in self.energy_system_components.get("electricity_storage", []):
ub = (
bounds[f"{asset_name}.Stored_electricity"][1]
if not isinstance(bounds[f"{asset_name}.Stored_electricity"][1], Timeseries)
else np.max(bounds[f"{asset_name}.Stored_electricity"][1].values)
)
lb = 0.0 if parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL else ub
_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

# Making the __aggregation_count variable for each asset
for asset_list in self.energy_system_components.values():
def _make_asset_max_size_vars(
component_type: str,
upper_bound_suffix=None,
upper_bound_suffix_sec=None,
profile_constraint=None,
):
"""
Creates max_size variables for all assets of the given component type with
upper bound suffixes and profile constraint information.
"""
for asset_name in self.energy_system_components.get(component_type, []):
if upper_bound_suffix_sec is None:
ub_raw = bounds[f"{asset_name}.{upper_bound_suffix}"][1]
else:
ub_raw = _demand_ub(asset_name, upper_bound_suffix, upper_bound_suffix_sec)
ub = _scalarise_upper_bound(ub_raw)
if profile_constraint:
ub = _get_ub_profile_constraint(asset_name, profile_constraint, ub)
lb = 0.0 if parameters[f"{asset_name}.state"] == AssetStateEnum.OPTIONAL else ub

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously the lb for a heat_demand the "np.isinf(bounds[f"{asset_name}.Heat_demand"][1])" check was used instead of state. Will this not have an impact now? Same holds for some of the other asset types

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it should work right now. Only in case that that an infinity for the upperbound occurs it might be an issue, but that cannot occur.

_make_max_size_var(name=asset_name, lb=lb, ub=ub, nominal=ub / 2.0)

map_asset_type_to_bound_vars = {
"heat_source": {
"upper_bound_suffix": "Heat_source",
"profile_constraint": "maximum_heat_source",
},
"heat_demand": {
"upper_bound_suffix": "Heat_demand",
"upper_bound_suffix_sec": "HeatIn.Heat",
},
"cold_demand": {
"upper_bound_suffix": "Cold_demand",
"upper_bound_suffix_sec": "HeatIn.Heat",
},
"airco": {"upper_bound_suffix": "Heat_airco"},
"ates": {"upper_bound_suffix": "Heat_ates"},
"low_temperature_ates": {"upper_bound_suffix": "Heat_ates"},
"heat_buffer": {"upper_bound_suffix": "Stored_heat"},
"heat_exchanger": {"upper_bound_suffix": "Secondary_heat"},
"heat_pump": {"upper_bound_suffix": "Secondary_heat"},
"gas_tank_storage": {"upper_bound_suffix": "Stored_gas_mass"},
"gas_substation": {"upper_bound_suffix": "GasIn.Q"},
"gas_demand": {"upper_bound_suffix": "Gas_demand_mass_flow"},
"gas_source": {"upper_bound_suffix": "Gas_source_mass_flow"},
"compressor": {"upper_bound_suffix": "GasIn.Q"},
"electrolyzer": {"upper_bound_suffix": "ElectricityIn.Power"},
"electricity_demand": {
"upper_bound_suffix": "Electricity_demand",
"upper_bound_suffix_sec": "ElectricityIn.Power",
},
"transformer": {"upper_bound_suffix": "ElectricityIn.Power"},
"electricity_source": {"upper_bound_suffix": "Electricity_source"},
"electricity_storage": {"upper_bound_suffix": "Stored_electricity"},
}

# Create the asset sizing variables for each asset.
for asset_type, asset_list in self.energy_system_components.items():
if asset_type in map_asset_type_to_bound_vars:
_make_asset_max_size_vars(asset_type, **map_asset_type_to_bound_vars[asset_type])
elif asset_type not in ["heat_pipe", "gas_pipe", "cable"]:
logger.warning(f"Assets of type {asset_type} is not supported for sizing.")
for asset in asset_list:
aggr_count_var = f"{asset}_aggregation_count"
self._asset_aggregation_count_var_map[asset] = aggr_count_var
Expand Down
Loading