Skip to content
Merged
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
62 changes: 32 additions & 30 deletions flixopt/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,11 @@ def create_model(self, model: FlowSystemModel) -> LinearConverterModel:
self.submodel = LinearConverterModel(model, self)
return self.submodel

def _set_flow_system(self, flow_system) -> None:
def link_to_flow_system(self, flow_system, prefix: str = '') -> None:
"""Propagate flow_system reference to parent Component and piecewise_conversion."""
super()._set_flow_system(flow_system)
super().link_to_flow_system(flow_system, prefix)
if self.piecewise_conversion is not None:
self.piecewise_conversion._set_flow_system(flow_system)
self.piecewise_conversion.link_to_flow_system(flow_system, self._sub_prefix('PiecewiseConversion'))

def _plausibility_checks(self) -> None:
super()._plausibility_checks()
Expand Down Expand Up @@ -216,14 +216,13 @@ def _plausibility_checks(self) -> None:
f'({flow.label_full}).'
)

def transform_data(self, name_prefix: str = '') -> None:
prefix = '|'.join(filter(None, [name_prefix, self.label_full]))
super().transform_data(prefix)
def transform_data(self) -> None:
super().transform_data()
if self.conversion_factors:
self.conversion_factors = self._transform_conversion_factors()
if self.piecewise_conversion:
self.piecewise_conversion.has_time_dim = True
self.piecewise_conversion.transform_data(f'{prefix}|PiecewiseConversion')
self.piecewise_conversion.transform_data()

def _transform_conversion_factors(self) -> list[dict[str, xr.DataArray]]:
"""Converts all conversion factors to internal datatypes"""
Expand Down Expand Up @@ -427,49 +426,50 @@ def create_model(self, model: FlowSystemModel) -> StorageModel:
self.submodel = StorageModel(model, self)
return self.submodel

def _set_flow_system(self, flow_system) -> None:
def link_to_flow_system(self, flow_system, prefix: str = '') -> None:
"""Propagate flow_system reference to parent Component and capacity_in_flow_hours if it's InvestParameters."""
super()._set_flow_system(flow_system)
super().link_to_flow_system(flow_system, prefix)
if isinstance(self.capacity_in_flow_hours, InvestParameters):
self.capacity_in_flow_hours._set_flow_system(flow_system)
self.capacity_in_flow_hours.link_to_flow_system(flow_system, self._sub_prefix('InvestParameters'))

def transform_data(self, name_prefix: str = '') -> None:
prefix = '|'.join(filter(None, [name_prefix, self.label_full]))
super().transform_data(prefix)
def transform_data(self) -> None:
super().transform_data()
self.relative_minimum_charge_state = self._fit_coords(
f'{prefix}|relative_minimum_charge_state', self.relative_minimum_charge_state
f'{self.prefix}|relative_minimum_charge_state', self.relative_minimum_charge_state
)
self.relative_maximum_charge_state = self._fit_coords(
f'{prefix}|relative_maximum_charge_state', self.relative_maximum_charge_state
f'{self.prefix}|relative_maximum_charge_state', self.relative_maximum_charge_state
)
self.eta_charge = self._fit_coords(f'{self.prefix}|eta_charge', self.eta_charge)
self.eta_discharge = self._fit_coords(f'{self.prefix}|eta_discharge', self.eta_discharge)
self.relative_loss_per_hour = self._fit_coords(
f'{self.prefix}|relative_loss_per_hour', self.relative_loss_per_hour
)
self.eta_charge = self._fit_coords(f'{prefix}|eta_charge', self.eta_charge)
self.eta_discharge = self._fit_coords(f'{prefix}|eta_discharge', self.eta_discharge)
self.relative_loss_per_hour = self._fit_coords(f'{prefix}|relative_loss_per_hour', self.relative_loss_per_hour)
if not isinstance(self.initial_charge_state, str):
self.initial_charge_state = self._fit_coords(
f'{prefix}|initial_charge_state', self.initial_charge_state, dims=['period', 'scenario']
f'{self.prefix}|initial_charge_state', self.initial_charge_state, dims=['period', 'scenario']
)
self.minimal_final_charge_state = self._fit_coords(
f'{prefix}|minimal_final_charge_state', self.minimal_final_charge_state, dims=['period', 'scenario']
f'{self.prefix}|minimal_final_charge_state', self.minimal_final_charge_state, dims=['period', 'scenario']
)
self.maximal_final_charge_state = self._fit_coords(
f'{prefix}|maximal_final_charge_state', self.maximal_final_charge_state, dims=['period', 'scenario']
f'{self.prefix}|maximal_final_charge_state', self.maximal_final_charge_state, dims=['period', 'scenario']
)
self.relative_minimum_final_charge_state = self._fit_coords(
f'{prefix}|relative_minimum_final_charge_state',
f'{self.prefix}|relative_minimum_final_charge_state',
self.relative_minimum_final_charge_state,
dims=['period', 'scenario'],
)
self.relative_maximum_final_charge_state = self._fit_coords(
f'{prefix}|relative_maximum_final_charge_state',
f'{self.prefix}|relative_maximum_final_charge_state',
self.relative_maximum_final_charge_state,
dims=['period', 'scenario'],
)
if isinstance(self.capacity_in_flow_hours, InvestParameters):
self.capacity_in_flow_hours.transform_data(f'{prefix}|InvestParameters')
self.capacity_in_flow_hours.transform_data()
else:
self.capacity_in_flow_hours = self._fit_coords(
f'{prefix}|capacity_in_flow_hours', self.capacity_in_flow_hours, dims=['period', 'scenario']
f'{self.prefix}|capacity_in_flow_hours', self.capacity_in_flow_hours, dims=['period', 'scenario']
)

def _plausibility_checks(self) -> None:
Expand Down Expand Up @@ -714,11 +714,10 @@ def create_model(self, model) -> TransmissionModel:
self.submodel = TransmissionModel(model, self)
return self.submodel

def transform_data(self, name_prefix: str = '') -> None:
prefix = '|'.join(filter(None, [name_prefix, self.label_full]))
super().transform_data(prefix)
self.relative_losses = self._fit_coords(f'{prefix}|relative_losses', self.relative_losses)
self.absolute_losses = self._fit_coords(f'{prefix}|absolute_losses', self.absolute_losses)
def transform_data(self) -> None:
super().transform_data()
self.relative_losses = self._fit_coords(f'{self.prefix}|relative_losses', self.relative_losses)
self.absolute_losses = self._fit_coords(f'{self.prefix}|absolute_losses', self.absolute_losses)


class TransmissionModel(ComponentModel):
Expand All @@ -729,6 +728,9 @@ def __init__(self, model: FlowSystemModel, element: Transmission):
for flow in element.inputs + element.outputs:
if flow.status_parameters is None:
flow.status_parameters = StatusParameters()
flow.status_parameters.link_to_flow_system(
model.flow_system, f'{flow.label_full}|status_parameters'
)

super().__init__(model, element)

Expand Down
38 changes: 22 additions & 16 deletions flixopt/effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,50 +237,56 @@ def __init__(
self.minimum_over_periods = minimum_over_periods
self.maximum_over_periods = maximum_over_periods

def transform_data(self, name_prefix: str = '') -> None:
prefix = '|'.join(filter(None, [name_prefix, self.label_full]))
self.minimum_per_hour = self._fit_coords(f'{prefix}|minimum_per_hour', self.minimum_per_hour)
self.maximum_per_hour = self._fit_coords(f'{prefix}|maximum_per_hour', self.maximum_per_hour)
def link_to_flow_system(self, flow_system, prefix: str = '') -> None:
"""Link this effect to a FlowSystem.

Elements use their label_full as prefix by default, ignoring the passed prefix.
"""
super().link_to_flow_system(flow_system, self.label_full)

def transform_data(self) -> None:
self.minimum_per_hour = self._fit_coords(f'{self.prefix}|minimum_per_hour', self.minimum_per_hour)
self.maximum_per_hour = self._fit_coords(f'{self.prefix}|maximum_per_hour', self.maximum_per_hour)

self.share_from_temporal = self._fit_effect_coords(
prefix=None,
effect_values=self.share_from_temporal,
suffix=f'(temporal)->{prefix}(temporal)',
suffix=f'(temporal)->{self.prefix}(temporal)',
dims=['time', 'period', 'scenario'],
)
self.share_from_periodic = self._fit_effect_coords(
prefix=None,
effect_values=self.share_from_periodic,
suffix=f'(periodic)->{prefix}(periodic)',
suffix=f'(periodic)->{self.prefix}(periodic)',
dims=['period', 'scenario'],
)

self.minimum_temporal = self._fit_coords(
f'{prefix}|minimum_temporal', self.minimum_temporal, dims=['period', 'scenario']
f'{self.prefix}|minimum_temporal', self.minimum_temporal, dims=['period', 'scenario']
)
self.maximum_temporal = self._fit_coords(
f'{prefix}|maximum_temporal', self.maximum_temporal, dims=['period', 'scenario']
f'{self.prefix}|maximum_temporal', self.maximum_temporal, dims=['period', 'scenario']
)
self.minimum_periodic = self._fit_coords(
f'{prefix}|minimum_periodic', self.minimum_periodic, dims=['period', 'scenario']
f'{self.prefix}|minimum_periodic', self.minimum_periodic, dims=['period', 'scenario']
)
self.maximum_periodic = self._fit_coords(
f'{prefix}|maximum_periodic', self.maximum_periodic, dims=['period', 'scenario']
f'{self.prefix}|maximum_periodic', self.maximum_periodic, dims=['period', 'scenario']
)
self.minimum_total = self._fit_coords(
f'{prefix}|minimum_total', self.minimum_total, dims=['period', 'scenario']
f'{self.prefix}|minimum_total', self.minimum_total, dims=['period', 'scenario']
)
self.maximum_total = self._fit_coords(
f'{prefix}|maximum_total', self.maximum_total, dims=['period', 'scenario']
f'{self.prefix}|maximum_total', self.maximum_total, dims=['period', 'scenario']
)
self.minimum_over_periods = self._fit_coords(
f'{prefix}|minimum_over_periods', self.minimum_over_periods, dims=['scenario']
f'{self.prefix}|minimum_over_periods', self.minimum_over_periods, dims=['scenario']
)
self.maximum_over_periods = self._fit_coords(
f'{prefix}|maximum_over_periods', self.maximum_over_periods, dims=['scenario']
f'{self.prefix}|maximum_over_periods', self.maximum_over_periods, dims=['scenario']
)
self.period_weights = self._fit_coords(
f'{prefix}|period_weights', self.period_weights, dims=['period', 'scenario']
f'{self.prefix}|period_weights', self.period_weights, dims=['period', 'scenario']
)

def create_model(self, model: FlowSystemModel) -> EffectModel:
Expand Down Expand Up @@ -670,7 +676,7 @@ def _do_modeling(self):
penalty_effect = self.effects._create_penalty_effect()
# Link to FlowSystem (should already be linked, but ensure it)
if penalty_effect._flow_system is None:
penalty_effect._set_flow_system(self._model.flow_system)
penalty_effect.link_to_flow_system(self._model.flow_system)

# Create EffectModel for each effect
for effect in self.effects.values():
Expand Down
91 changes: 52 additions & 39 deletions flixopt/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
Element,
ElementModel,
FlowSystemModel,
Interface,
register_class_for_io,
)

Expand Down Expand Up @@ -111,21 +110,23 @@ def create_model(self, model: FlowSystemModel) -> ComponentModel:
self.submodel = ComponentModel(model, self)
return self.submodel

def _set_flow_system(self, flow_system) -> None:
"""Propagate flow_system reference to nested Interface objects and flows."""
super()._set_flow_system(flow_system)
def link_to_flow_system(self, flow_system, prefix: str = '') -> None:
"""Propagate flow_system reference to nested Interface objects and flows.

Elements use their label_full as prefix by default, ignoring the passed prefix.
"""
super().link_to_flow_system(flow_system, self.label_full)
if self.status_parameters is not None:
self.status_parameters._set_flow_system(flow_system)
self.status_parameters.link_to_flow_system(flow_system, self._sub_prefix('status_parameters'))
for flow in self.inputs + self.outputs:
flow._set_flow_system(flow_system)
flow.link_to_flow_system(flow_system)

def transform_data(self, name_prefix: str = '') -> None:
prefix = '|'.join(filter(None, [name_prefix, self.label_full]))
def transform_data(self) -> None:
if self.status_parameters is not None:
self.status_parameters.transform_data(prefix)
self.status_parameters.transform_data()

for flow in self.inputs + self.outputs:
flow.transform_data() # Flow doesnt need the name_prefix
flow.transform_data()

def _check_unique_flow_labels(self):
all_flow_labels = [flow.label for flow in self.inputs + self.outputs]
Expand Down Expand Up @@ -269,16 +270,18 @@ def create_model(self, model: FlowSystemModel) -> BusModel:
self.submodel = BusModel(model, self)
return self.submodel

def _set_flow_system(self, flow_system) -> None:
"""Propagate flow_system reference to nested flows."""
super()._set_flow_system(flow_system)
def link_to_flow_system(self, flow_system, prefix: str = '') -> None:
"""Propagate flow_system reference to nested flows.

Elements use their label_full as prefix by default, ignoring the passed prefix.
"""
super().link_to_flow_system(flow_system, self.label_full)
for flow in self.inputs + self.outputs:
flow._set_flow_system(flow_system)
flow.link_to_flow_system(flow_system)

def transform_data(self, name_prefix: str = '') -> None:
prefix = '|'.join(filter(None, [name_prefix, self.label_full]))
def transform_data(self) -> None:
self.imbalance_penalty_per_flow_hour = self._fit_coords(
f'{prefix}|imbalance_penalty_per_flow_hour', self.imbalance_penalty_per_flow_hour
f'{self.prefix}|imbalance_penalty_per_flow_hour', self.imbalance_penalty_per_flow_hour
)

def _plausibility_checks(self) -> None:
Expand Down Expand Up @@ -505,45 +508,49 @@ def create_model(self, model: FlowSystemModel) -> FlowModel:
self.submodel = FlowModel(model, self)
return self.submodel

def _set_flow_system(self, flow_system) -> None:
"""Propagate flow_system reference to nested Interface objects."""
super()._set_flow_system(flow_system)
def link_to_flow_system(self, flow_system, prefix: str = '') -> None:
"""Propagate flow_system reference to nested Interface objects.

Elements use their label_full as prefix by default, ignoring the passed prefix.
"""
super().link_to_flow_system(flow_system, self.label_full)
if self.status_parameters is not None:
self.status_parameters._set_flow_system(flow_system)
if isinstance(self.size, Interface):
self.size._set_flow_system(flow_system)

def transform_data(self, name_prefix: str = '') -> None:
prefix = '|'.join(filter(None, [name_prefix, self.label_full]))
self.relative_minimum = self._fit_coords(f'{prefix}|relative_minimum', self.relative_minimum)
self.relative_maximum = self._fit_coords(f'{prefix}|relative_maximum', self.relative_maximum)
self.fixed_relative_profile = self._fit_coords(f'{prefix}|fixed_relative_profile', self.fixed_relative_profile)
self.effects_per_flow_hour = self._fit_effect_coords(prefix, self.effects_per_flow_hour, 'per_flow_hour')
self.status_parameters.link_to_flow_system(flow_system, self._sub_prefix('status_parameters'))
if isinstance(self.size, InvestParameters):
self.size.link_to_flow_system(flow_system, self._sub_prefix('InvestParameters'))

def transform_data(self) -> None:
self.relative_minimum = self._fit_coords(f'{self.prefix}|relative_minimum', self.relative_minimum)
self.relative_maximum = self._fit_coords(f'{self.prefix}|relative_maximum', self.relative_maximum)
self.fixed_relative_profile = self._fit_coords(
f'{self.prefix}|fixed_relative_profile', self.fixed_relative_profile
)
self.effects_per_flow_hour = self._fit_effect_coords(self.prefix, self.effects_per_flow_hour, 'per_flow_hour')
self.flow_hours_max = self._fit_coords(
f'{prefix}|flow_hours_max', self.flow_hours_max, dims=['period', 'scenario']
f'{self.prefix}|flow_hours_max', self.flow_hours_max, dims=['period', 'scenario']
)
self.flow_hours_min = self._fit_coords(
f'{prefix}|flow_hours_min', self.flow_hours_min, dims=['period', 'scenario']
f'{self.prefix}|flow_hours_min', self.flow_hours_min, dims=['period', 'scenario']
)
self.flow_hours_max_over_periods = self._fit_coords(
f'{prefix}|flow_hours_max_over_periods', self.flow_hours_max_over_periods, dims=['scenario']
f'{self.prefix}|flow_hours_max_over_periods', self.flow_hours_max_over_periods, dims=['scenario']
)
self.flow_hours_min_over_periods = self._fit_coords(
f'{prefix}|flow_hours_min_over_periods', self.flow_hours_min_over_periods, dims=['scenario']
f'{self.prefix}|flow_hours_min_over_periods', self.flow_hours_min_over_periods, dims=['scenario']
)
self.load_factor_max = self._fit_coords(
f'{prefix}|load_factor_max', self.load_factor_max, dims=['period', 'scenario']
f'{self.prefix}|load_factor_max', self.load_factor_max, dims=['period', 'scenario']
)
self.load_factor_min = self._fit_coords(
f'{prefix}|load_factor_min', self.load_factor_min, dims=['period', 'scenario']
f'{self.prefix}|load_factor_min', self.load_factor_min, dims=['period', 'scenario']
)

if self.status_parameters is not None:
self.status_parameters.transform_data(prefix)
self.status_parameters.transform_data()
if isinstance(self.size, InvestParameters):
self.size.transform_data(prefix)
self.size.transform_data()
else:
self.size = self._fit_coords(f'{prefix}|size', self.size, dims=['period', 'scenario'])
self.size = self._fit_coords(f'{self.prefix}|size', self.size, dims=['period', 'scenario'])

def _plausibility_checks(self) -> None:
# TODO: Incorporate into Variable? (Lower_bound can not be greater than upper bound
Expand Down Expand Up @@ -955,11 +962,17 @@ def _do_modeling(self):
for flow in all_flows:
if flow.status_parameters is None:
flow.status_parameters = StatusParameters()
flow.status_parameters.link_to_flow_system(
self._model.flow_system, f'{flow.label_full}|status_parameters'
)

if self.element.prevent_simultaneous_flows:
for flow in self.element.prevent_simultaneous_flows:
if flow.status_parameters is None:
flow.status_parameters = StatusParameters()
flow.status_parameters.link_to_flow_system(
self._model.flow_system, f'{flow.label_full}|status_parameters'
)

# Create FlowModels (which creates their variables and constraints)
for flow in all_flows:
Expand Down
Loading