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
29 changes: 10 additions & 19 deletions flixOpt/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ def do_modeling(self, system_model: SystemModel):
'flow_rate',
self,
system_model.nr_of_time_steps,
fixed_value=self.fixed_relative_flow_rate,
lower_bound=self.absolute_flow_rate_bounds[0] if self.element.on_off_parameters is None else 0,
upper_bound=self.absolute_flow_rate_bounds[1] if self.element.on_off_parameters is None else None,
previous_values=self.element.previous_flow_rate,
Expand All @@ -328,7 +327,9 @@ def do_modeling(self, system_model: SystemModel):
self.element.size,
self.flow_rate,
self.relative_flow_rate_bounds,
fixed_relative_profile=self.fixed_relative_flow_rate,
fixed_relative_profile=(None
if self.element.fixed_relative_profile is None
else self.element.fixed_relative_profile.active_data),
on_variable=self._on.on if self._on is not None else None,
)
self._investment.do_modeling(system_model)
Expand Down Expand Up @@ -392,35 +393,25 @@ def with_investment(self) -> bool:
"""Checks if the element's size is investment-driven."""
return isinstance(self.element.size, InvestParameters)

@property
def fixed_relative_flow_rate(self) -> Optional[np.ndarray]:
"""Returns a fixed flow rate if defined by the element."""
if self.element.fixed_relative_profile is not None:
return self.element.fixed_relative_profile.active_data
return None

@property
def absolute_flow_rate_bounds(self) -> Tuple[Numeric, Numeric]:
"""Returns absolute flow rate bounds. Iportant for OnOffModel"""
"""Returns absolute flow rate bounds. Important for OnOffModel"""
rel_min, rel_max = self.relative_flow_rate_bounds
size = self.element.size
if self.with_investment:
if size.fixed_size is not None:
return rel_min * size.fixed_size, rel_max * size.fixed_size
return rel_min * size.minimum_size, rel_max * size.maximum_size
else:
if not self.with_investment:
return rel_min * size, rel_max * size
if size.fixed_size is not None:
return rel_min * size.fixed_size, rel_max * size.fixed_size
return rel_min * size.minimum_size, rel_max * size.maximum_size


@property
def relative_flow_rate_bounds(self) -> Tuple[Numeric, Numeric]:
"""Returns relative flow rate bounds."""
fixed_profile = self.element.fixed_relative_profile
if fixed_profile is None:
return self.element.relative_minimum.active_data, self.element.relative_maximum.active_data
return (
np.minimum(fixed_profile.active_data, self.element.relative_minimum.active_data),
np.maximum(fixed_profile.active_data, self.element.relative_maximum.active_data),
)
return fixed_profile.active_data, fixed_profile.active_data


class BusModel(ElementModel):
Expand Down
2 changes: 1 addition & 1 deletion flixOpt/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ def _add_on_constraints(self, system_model: SystemModel, time_indices: Union[lis
# eq: sum( Leistung(t,i)) - sum(Leistung_max(i)) * On(t) <= 0
# --> damit Gleichungswerte nicht zu groß werden, noch durch nr_of_flows geteilt:
# eq: sum( Leistung(t,i) / nr_of_flows ) - sum(Leistung_max(i)) / nr_of_flows * On(t) <= 0
absolute_maximum: Numeric = 0
absolute_maximum: Numeric = 0.0
for variable, bounds in zip(self._defining_variables, self._defining_bounds, strict=False):
eq_on_2.add_summand(variable, 1 / nr_of_defining_variables, time_indices)
absolute_maximum += bounds[
Expand Down
55 changes: 55 additions & 0 deletions tests/test_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,61 @@ def test_optional_invest(self):
err_msg='"Boiler__Q_th__IsInvested" does not have the right value',
)

def test_fixed_relative_profile(self):
self.flow_system = self.create_model(self.datetime_array)
self.flow_system.add_elements(
fx.linear_converters.Boiler(
'Boiler',
0.5,
Q_fu=fx.Flow('Q_fu', bus=self.get_element('Gas')),
Q_th=fx.Flow(
'Q_th',
bus=self.get_element('Fernwärme'),
size=fx.InvestParameters(optional=True, minimum_size=40, fix_effects=10, specific_effects=1),
),
),
fx.linear_converters.Boiler(
'Boiler_optional',
0.5,
Q_fu=fx.Flow('Q_fu', bus=self.get_element('Gas')),
Q_th=fx.Flow(
'Q_th',
bus=self.get_element('Fernwärme'),
size=fx.InvestParameters(optional=True, minimum_size=50, fix_effects=10, specific_effects=1),
),
),
)
self.flow_system.add_elements(
fx.Source(
'Wärmequelle',
source=fx.Flow('Q_th',
bus=self.get_element('Fernwärme'),
fixed_relative_profile=np.linspace(0, 5, len(self.datetime_array)),
size=fx.InvestParameters(optional=False, minimum_size=2, maximum_size=5),
)
)
)
self.get_element('Fernwärme').excess_penalty_per_flow_hour = 1e5

self.solve_and_load(self.flow_system)
source = self.get_element('Wärmequelle')
assert_allclose(
source.source.model.flow_rate.result,
np.linspace(0, 5, len(self.datetime_array)) * source.source.model._investment.size.result,
rtol=self.mip_gap,
atol=1e-10,
err_msg='The total costs does not have the right value',
)
assert_allclose(
source.source.model._investment.size.result,
2,
rtol=self.mip_gap,
atol=1e-10,
err_msg='The total costs does not have the right value',
)




class TestOnOff(BaseTest):
"""
Expand Down