flixopt 2.0.1__py3-none-any.whl → 2.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of flixopt might be problematic. Click here for more details.
- docs/release-notes/v2.0.1.md +1 -1
- docs/release-notes/v2.1.0.md +31 -0
- docs/release-notes/v2.1.1.md +11 -0
- flixopt/components.py +17 -10
- flixopt/elements.py +58 -17
- flixopt/features.py +407 -331
- flixopt/interface.py +5 -5
- flixopt/results.py +1 -1
- flixopt/structure.py +5 -5
- {flixopt-2.0.1.dist-info → flixopt-2.1.1.dist-info}/METADATA +7 -6
- {flixopt-2.0.1.dist-info → flixopt-2.1.1.dist-info}/RECORD +14 -12
- {flixopt-2.0.1.dist-info → flixopt-2.1.1.dist-info}/WHEEL +1 -1
- {flixopt-2.0.1.dist-info → flixopt-2.1.1.dist-info}/licenses/LICENSE +0 -0
- {flixopt-2.0.1.dist-info → flixopt-2.1.1.dist-info}/top_level.txt +0 -0
docs/release-notes/v2.0.1.md
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Release v2.1.0
|
|
2
|
+
|
|
3
|
+
**Release Date:** 2025-04-11
|
|
4
|
+
|
|
5
|
+
## Improvements
|
|
6
|
+
|
|
7
|
+
* Add logger warning if relative_minimum is used without on_off_parameters in Flow, as this prevents the flow_rate from switching "OFF"
|
|
8
|
+
* Python 3.13 support added
|
|
9
|
+
* Greatly improved internal testing infrastructure by leveraging linopy's testing framework
|
|
10
|
+
|
|
11
|
+
## Bug Fixes
|
|
12
|
+
|
|
13
|
+
* Bugfixing the lower bound of `flow_rate` when using optional investments without OnOffParameters.
|
|
14
|
+
* Fixes a Bug that prevented divest effects from working.
|
|
15
|
+
* added lower bounds of 0 to two unbounded vars (only numerical better)
|
|
16
|
+
|
|
17
|
+
## Breaking Changes
|
|
18
|
+
|
|
19
|
+
* We restructured the modeling of the On/Off state of FLows or Components. This leads to slightly renaming of variables and constraints.
|
|
20
|
+
|
|
21
|
+
### Variable renaming
|
|
22
|
+
* "...|consecutive_on_hours" is now "...|ConsecutiveOn|hours"
|
|
23
|
+
* "...|consecutive_off_hours" is now "...|ConsecutiveOff|hours"
|
|
24
|
+
|
|
25
|
+
### Constraint renaming
|
|
26
|
+
* "...|consecutive_on_hours_con1" is now "...|ConsecutiveOn|con1"
|
|
27
|
+
* "...|consecutive_on_hours_con2a" is now "...|ConsecutiveOn|con2a"
|
|
28
|
+
* "...|consecutive_on_hours_con2b" is now "...|ConsecutiveOn|con2b"
|
|
29
|
+
* "...|consecutive_on_hours_initial" is now "...|ConsecutiveOn|initial"
|
|
30
|
+
* "...|consecutive_on_hours_minimum_duration" is now "...|ConsecutiveOn|minimum"
|
|
31
|
+
The same goes for "...|consecutive_off..." --> "...|ConsecutiveOff|..."
|
flixopt/components.py
CHANGED
|
@@ -43,7 +43,10 @@ class LinearConverter(Component):
|
|
|
43
43
|
label: The label of the Element. Used to identify it in the FlowSystem
|
|
44
44
|
inputs: The input Flows
|
|
45
45
|
outputs: The output Flows
|
|
46
|
-
on_off_parameters: Information about on and off
|
|
46
|
+
on_off_parameters: Information about on and off state of LinearConverter.
|
|
47
|
+
Component is On/Off, if all connected Flows are On/Off. This induces an On-Variable (binary) in all Flows!
|
|
48
|
+
If possible, use OnOffParameters in a single Flow instead to keep the number of binary variables low.
|
|
49
|
+
See class OnOffParameters.
|
|
47
50
|
conversion_factors: linear relation between flows.
|
|
48
51
|
Either 'conversion_factors' or 'piecewise_conversion' can be used!
|
|
49
52
|
piecewise_conversion: Define a piecewise linear relation between flow rates of different flows.
|
|
@@ -60,6 +63,7 @@ class LinearConverter(Component):
|
|
|
60
63
|
return self.model
|
|
61
64
|
|
|
62
65
|
def _plausibility_checks(self) -> None:
|
|
66
|
+
super()._plausibility_checks()
|
|
63
67
|
if not self.conversion_factors and not self.piecewise_conversion:
|
|
64
68
|
raise PlausibilityError('Either conversion_factors or piecewise_conversion must be defined!')
|
|
65
69
|
if self.conversion_factors and self.piecewise_conversion:
|
|
@@ -213,6 +217,7 @@ class Storage(Component):
|
|
|
213
217
|
"""
|
|
214
218
|
Check for infeasible or uncommon combinations of parameters
|
|
215
219
|
"""
|
|
220
|
+
super()._plausibility_checks()
|
|
216
221
|
if utils.is_number(self.initial_charge_state):
|
|
217
222
|
if isinstance(self.capacity_in_flow_hours, InvestParameters):
|
|
218
223
|
if self.capacity_in_flow_hours.fixed_size is None:
|
|
@@ -301,6 +306,7 @@ class Transmission(Component):
|
|
|
301
306
|
self.absolute_losses = absolute_losses
|
|
302
307
|
|
|
303
308
|
def _plausibility_checks(self):
|
|
309
|
+
super()._plausibility_checks()
|
|
304
310
|
# check buses:
|
|
305
311
|
if self.in2 is not None:
|
|
306
312
|
assert self.in2.bus == self.out1.bus, (
|
|
@@ -396,6 +402,7 @@ class LinearConverterModel(ComponentModel):
|
|
|
396
402
|
super().__init__(model, element)
|
|
397
403
|
self.element: LinearConverter = element
|
|
398
404
|
self.on_off: Optional[OnOffModel] = None
|
|
405
|
+
self.piecewise_conversion: Optional[PiecewiseConversion] = None
|
|
399
406
|
|
|
400
407
|
def do_modeling(self):
|
|
401
408
|
super().do_modeling()
|
|
@@ -426,16 +433,16 @@ class LinearConverterModel(ComponentModel):
|
|
|
426
433
|
for flow, piecewise in self.element.piecewise_conversion.items()
|
|
427
434
|
}
|
|
428
435
|
|
|
429
|
-
piecewise_conversion =
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
+
self.piecewise_conversion = self.add(
|
|
437
|
+
PiecewiseModel(
|
|
438
|
+
model=self._model,
|
|
439
|
+
label_of_element=self.label_of_element,
|
|
440
|
+
piecewise_variables=piecewise_conversion,
|
|
441
|
+
zero_point=self.on_off.on if self.on_off is not None else False,
|
|
442
|
+
as_time_series=True,
|
|
443
|
+
)
|
|
436
444
|
)
|
|
437
|
-
piecewise_conversion.do_modeling()
|
|
438
|
-
self.sub_models.append(piecewise_conversion)
|
|
445
|
+
self.piecewise_conversion.do_modeling()
|
|
439
446
|
|
|
440
447
|
|
|
441
448
|
class StorageModel(ComponentModel):
|
flixopt/elements.py
CHANGED
|
@@ -47,8 +47,8 @@ class Component(Element):
|
|
|
47
47
|
inputs: input flows.
|
|
48
48
|
outputs: output flows.
|
|
49
49
|
on_off_parameters: Information about on and off state of Component.
|
|
50
|
-
Component is On/Off, if all connected Flows are On/Off.
|
|
51
|
-
|
|
50
|
+
Component is On/Off, if all connected Flows are On/Off. This induces an On-Variable (binary) in all Flows!
|
|
51
|
+
If possible, use OnOffParameters in a single Flow instead to keep the number of binary variables low.
|
|
52
52
|
See class OnOffParameters.
|
|
53
53
|
prevent_simultaneous_flows: Define a Group of Flows. Only one them can be on at a time.
|
|
54
54
|
Induces On-Variable in all Flows! If possible, use OnOffParameters in a single Flow instead.
|
|
@@ -57,6 +57,7 @@ class Component(Element):
|
|
|
57
57
|
super().__init__(label, meta_data=meta_data)
|
|
58
58
|
self.inputs: List['Flow'] = inputs or []
|
|
59
59
|
self.outputs: List['Flow'] = outputs or []
|
|
60
|
+
self._check_unique_flow_labels()
|
|
60
61
|
self.on_off_parameters = on_off_parameters
|
|
61
62
|
self.prevent_simultaneous_flows: List['Flow'] = prevent_simultaneous_flows or []
|
|
62
63
|
|
|
@@ -77,9 +78,15 @@ class Component(Element):
|
|
|
77
78
|
infos['outputs'] = [flow.infos(use_numpy, use_element_label) for flow in self.outputs]
|
|
78
79
|
return infos
|
|
79
80
|
|
|
81
|
+
def _check_unique_flow_labels(self):
|
|
82
|
+
all_flow_labels = [flow.label for flow in self.inputs + self.outputs]
|
|
83
|
+
|
|
84
|
+
if len(set(all_flow_labels)) != len(all_flow_labels):
|
|
85
|
+
duplicates = {label for label in all_flow_labels if all_flow_labels.count(label) > 1}
|
|
86
|
+
raise ValueError(f'Flow names must be unique! "{self.label_full}" got 2 or more of: {duplicates}')
|
|
87
|
+
|
|
80
88
|
def _plausibility_checks(self) -> None:
|
|
81
|
-
|
|
82
|
-
pass
|
|
89
|
+
self._check_unique_flow_labels()
|
|
83
90
|
|
|
84
91
|
|
|
85
92
|
@register_class_for_io
|
|
@@ -186,7 +193,8 @@ class Flow(Element):
|
|
|
186
193
|
(relative_minimum and relative_maximum are ignored)
|
|
187
194
|
used for fixed load or supply profiles, i.g. heat demand, wind-power, solarthermal
|
|
188
195
|
If the load-profile is just an upper limit, use relative_maximum instead.
|
|
189
|
-
previous_flow_rate: previous flow rate of the
|
|
196
|
+
previous_flow_rate: previous flow rate of the flow. Used to determine if and how long the
|
|
197
|
+
flow is already on / off. If None, the flow is considered to be off for one timestep.
|
|
190
198
|
meta_data: used to store more information about the Element. Is not used internally, but saved in the results. Only use python native types.
|
|
191
199
|
"""
|
|
192
200
|
super().__init__(label, meta_data=meta_data)
|
|
@@ -313,8 +321,8 @@ class FlowModel(ElementModel):
|
|
|
313
321
|
# eq relative_minimum(t) * size <= flow_rate(t) <= relative_maximum(t) * size
|
|
314
322
|
self.flow_rate: linopy.Variable = self.add(
|
|
315
323
|
self._model.add_variables(
|
|
316
|
-
lower=self.
|
|
317
|
-
upper=self.
|
|
324
|
+
lower=self.flow_rate_lower_bound,
|
|
325
|
+
upper=self.flow_rate_upper_bound,
|
|
318
326
|
coords=self._model.coords,
|
|
319
327
|
name=f'{self.label_full}|flow_rate',
|
|
320
328
|
),
|
|
@@ -329,7 +337,7 @@ class FlowModel(ElementModel):
|
|
|
329
337
|
label_of_element=self.label_of_element,
|
|
330
338
|
on_off_parameters=self.element.on_off_parameters,
|
|
331
339
|
defining_variables=[self.flow_rate],
|
|
332
|
-
defining_bounds=[self.
|
|
340
|
+
defining_bounds=[self.flow_rate_bounds_on],
|
|
333
341
|
previous_values=[self.element.previous_flow_rate],
|
|
334
342
|
),
|
|
335
343
|
'on_off',
|
|
@@ -344,7 +352,8 @@ class FlowModel(ElementModel):
|
|
|
344
352
|
label_of_element=self.label_of_element,
|
|
345
353
|
parameters=self.element.size,
|
|
346
354
|
defining_variable=self.flow_rate,
|
|
347
|
-
relative_bounds_of_defining_variable=self.
|
|
355
|
+
relative_bounds_of_defining_variable=(self.flow_rate_lower_bound_relative,
|
|
356
|
+
self.flow_rate_upper_bound_relative),
|
|
348
357
|
on_variable=self.on_off.on if self.on_off is not None else None,
|
|
349
358
|
),
|
|
350
359
|
'investment',
|
|
@@ -353,7 +362,7 @@ class FlowModel(ElementModel):
|
|
|
353
362
|
|
|
354
363
|
self.total_flow_hours = self.add(
|
|
355
364
|
self._model.add_variables(
|
|
356
|
-
lower=self.element.flow_hours_total_min if self.element.flow_hours_total_min is not None else
|
|
365
|
+
lower=self.element.flow_hours_total_min if self.element.flow_hours_total_min is not None else 0,
|
|
357
366
|
upper=self.element.flow_hours_total_max if self.element.flow_hours_total_max is not None else np.inf,
|
|
358
367
|
coords=None,
|
|
359
368
|
name=f'{self.label_full}|total_flow_hours',
|
|
@@ -419,9 +428,9 @@ class FlowModel(ElementModel):
|
|
|
419
428
|
)
|
|
420
429
|
|
|
421
430
|
@property
|
|
422
|
-
def
|
|
431
|
+
def flow_rate_bounds_on(self) -> Tuple[NumericData, NumericData]:
|
|
423
432
|
"""Returns absolute flow rate bounds. Important for OnOffModel"""
|
|
424
|
-
relative_minimum, relative_maximum = self.
|
|
433
|
+
relative_minimum, relative_maximum = self.flow_rate_lower_bound_relative, self.flow_rate_upper_bound_relative
|
|
425
434
|
size = self.element.size
|
|
426
435
|
if not isinstance(size, InvestParameters):
|
|
427
436
|
return relative_minimum * size, relative_maximum * size
|
|
@@ -430,12 +439,44 @@ class FlowModel(ElementModel):
|
|
|
430
439
|
return relative_minimum * size.minimum_size, relative_maximum * size.maximum_size
|
|
431
440
|
|
|
432
441
|
@property
|
|
433
|
-
def
|
|
434
|
-
"""Returns relative
|
|
442
|
+
def flow_rate_lower_bound_relative(self) -> NumericData:
|
|
443
|
+
"""Returns the lower bound of the flow_rate relative to its size"""
|
|
435
444
|
fixed_profile = self.element.fixed_relative_profile
|
|
436
445
|
if fixed_profile is None:
|
|
437
|
-
return self.element.relative_minimum.active_data
|
|
438
|
-
return fixed_profile.active_data
|
|
446
|
+
return self.element.relative_minimum.active_data
|
|
447
|
+
return fixed_profile.active_data
|
|
448
|
+
|
|
449
|
+
@property
|
|
450
|
+
def flow_rate_upper_bound_relative(self) -> NumericData:
|
|
451
|
+
""" Returns the upper bound of the flow_rate relative to its size"""
|
|
452
|
+
fixed_profile = self.element.fixed_relative_profile
|
|
453
|
+
if fixed_profile is None:
|
|
454
|
+
return self.element.relative_maximum.active_data
|
|
455
|
+
return fixed_profile.active_data
|
|
456
|
+
|
|
457
|
+
@property
|
|
458
|
+
def flow_rate_lower_bound(self) -> NumericData:
|
|
459
|
+
"""
|
|
460
|
+
Returns the minimum bound the flow_rate can reach.
|
|
461
|
+
Further constraining might be done in OnOffModel and InvestmentModel
|
|
462
|
+
"""
|
|
463
|
+
if self.element.on_off_parameters is not None:
|
|
464
|
+
return 0
|
|
465
|
+
if isinstance(self.element.size, InvestParameters):
|
|
466
|
+
if self.element.size.optional:
|
|
467
|
+
return 0
|
|
468
|
+
return self.flow_rate_lower_bound_relative * self.element.size.minimum_size
|
|
469
|
+
return self.flow_rate_lower_bound_relative * self.element.size
|
|
470
|
+
|
|
471
|
+
@property
|
|
472
|
+
def flow_rate_upper_bound(self) -> NumericData:
|
|
473
|
+
"""
|
|
474
|
+
Returns the maximum bound the flow_rate can reach.
|
|
475
|
+
Further constraining might be done in OnOffModel and InvestmentModel
|
|
476
|
+
"""
|
|
477
|
+
if isinstance(self.element.size, InvestParameters):
|
|
478
|
+
return self.flow_rate_upper_bound_relative * self.element.size.maximum_size
|
|
479
|
+
return self.flow_rate_upper_bound_relative * self.element.size
|
|
439
480
|
|
|
440
481
|
|
|
441
482
|
class BusModel(ElementModel):
|
|
@@ -513,7 +554,7 @@ class ComponentModel(ElementModel):
|
|
|
513
554
|
self.element.on_off_parameters,
|
|
514
555
|
self.label_of_element,
|
|
515
556
|
defining_variables=[flow.model.flow_rate for flow in all_flows],
|
|
516
|
-
defining_bounds=[flow.model.
|
|
557
|
+
defining_bounds=[flow.model.flow_rate_bounds_on for flow in all_flows],
|
|
517
558
|
previous_values=[flow.previous_flow_rate for flow in all_flows],
|
|
518
559
|
)
|
|
519
560
|
)
|