flixopt 2.1.7__py3-none-any.whl → 2.1.8__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.

flixopt/elements.py CHANGED
@@ -2,21 +2,24 @@
2
2
  This module contains the basic elements of the flixopt framework.
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  import logging
6
8
  import warnings
7
- from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union
9
+ from typing import TYPE_CHECKING
8
10
 
9
- import linopy
10
11
  import numpy as np
11
12
 
12
13
  from .config import CONFIG
13
- from .core import NumericData, NumericDataTS, PlausibilityError, Scalar, TimeSeriesCollection
14
- from .effects import EffectValuesUser
14
+ from .core import NumericData, NumericDataTS, PlausibilityError, Scalar
15
15
  from .features import InvestmentModel, OnOffModel, PreventSimultaneousUsageModel
16
16
  from .interface import InvestParameters, OnOffParameters
17
17
  from .structure import Element, ElementModel, SystemModel, register_class_for_io
18
18
 
19
19
  if TYPE_CHECKING:
20
+ import linopy
21
+
22
+ from .effects import EffectValuesUser
20
23
  from .flow_system import FlowSystem
21
24
 
22
25
  logger = logging.getLogger('flixopt')
@@ -25,54 +28,78 @@ logger = logging.getLogger('flixopt')
25
28
  @register_class_for_io
26
29
  class Component(Element):
27
30
  """
28
- A Component contains incoming and outgoing [`Flows`][flixopt.elements.Flow]. It defines how these Flows interact with each other.
29
- The On or Off state of the Component is defined by all its Flows. Its on, if any of its FLows is On.
30
- It's mathematically advisable to define the On/Off state in a FLow rather than a Component if possible,
31
- as this introduces less binary variables to the Model
32
- Constraints to the On/Off state are defined by the [`on_off_parameters`][flixopt.interface.OnOffParameters].
31
+ Base class for all system components that transform, convert, or process flows.
32
+
33
+ Components are the active elements in energy systems that define how input and output
34
+ Flows interact with each other. They represent equipment, processes, or logical
35
+ operations that transform energy or materials between different states, carriers,
36
+ or locations.
37
+
38
+ Components serve as connection points between Buses through their associated Flows,
39
+ enabling the modeling of complex energy system topologies and operational constraints.
40
+
41
+ Args:
42
+ label: The label of the Element. Used to identify it in the FlowSystem.
43
+ inputs: list of input Flows feeding into the component. These represent
44
+ energy/material consumption by the component.
45
+ outputs: list of output Flows leaving the component. These represent
46
+ energy/material production by the component.
47
+ on_off_parameters: Defines binary operation constraints and costs when the
48
+ component has discrete on/off states. Creates binary variables for all
49
+ connected Flows. For better performance, prefer defining OnOffParameters
50
+ on individual Flows when possible.
51
+ prevent_simultaneous_flows: list of Flows that cannot be active simultaneously.
52
+ Creates binary variables to enforce mutual exclusivity. Use sparingly as
53
+ it increases computational complexity.
54
+ meta_data: Used to store additional information. Not used internally but saved
55
+ in results. Only use Python native types.
56
+
57
+ Note:
58
+ Component operational state is determined by its connected Flows:
59
+ - Component is "on" if ANY of its Flows is active (flow_rate > 0)
60
+ - Component is "off" only when ALL Flows are inactive (flow_rate = 0)
61
+
62
+ Binary variables and constraints:
63
+ - on_off_parameters creates binary variables for ALL connected Flows
64
+ - prevent_simultaneous_flows creates binary variables for specified Flows
65
+ - For better computational performance, prefer Flow-level OnOffParameters
66
+
67
+ Component is an abstract base class. In practice, use specialized subclasses:
68
+ - LinearConverter: Linear input/output relationships
69
+ - Storage: Temporal energy/material storage
70
+ - Transmission: Transport between locations
71
+ - Source/Sink: System boundaries
72
+
33
73
  """
34
74
 
35
75
  def __init__(
36
76
  self,
37
77
  label: str,
38
- inputs: Optional[List['Flow']] = None,
39
- outputs: Optional[List['Flow']] = None,
40
- on_off_parameters: Optional[OnOffParameters] = None,
41
- prevent_simultaneous_flows: Optional[List['Flow']] = None,
42
- meta_data: Optional[Dict] = None,
78
+ inputs: list[Flow] | None = None,
79
+ outputs: list[Flow] | None = None,
80
+ on_off_parameters: OnOffParameters | None = None,
81
+ prevent_simultaneous_flows: list[Flow] | None = None,
82
+ meta_data: dict | None = None,
43
83
  ):
44
- """
45
- Args:
46
- label: The label of the Element. Used to identify it in the FlowSystem
47
- inputs: input flows.
48
- outputs: output flows.
49
- on_off_parameters: Information about on and off state of Component.
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
- See class OnOffParameters.
53
- prevent_simultaneous_flows: Define a Group of Flows. Only one them can be on at a time.
54
- Induces On-Variable in all Flows! If possible, use OnOffParameters in a single Flow instead.
55
- meta_data: used to store more information about the Element. Is not used internally, but saved in the results. Only use python native types.
56
- """
57
84
  super().__init__(label, meta_data=meta_data)
58
- self.inputs: List['Flow'] = inputs or []
59
- self.outputs: List['Flow'] = outputs or []
85
+ self.inputs: list[Flow] = inputs or []
86
+ self.outputs: list[Flow] = outputs or []
60
87
  self._check_unique_flow_labels()
61
88
  self.on_off_parameters = on_off_parameters
62
- self.prevent_simultaneous_flows: List['Flow'] = prevent_simultaneous_flows or []
89
+ self.prevent_simultaneous_flows: list[Flow] = prevent_simultaneous_flows or []
63
90
 
64
- self.flows: Dict[str, Flow] = {flow.label: flow for flow in self.inputs + self.outputs}
91
+ self.flows: dict[str, Flow] = {flow.label: flow for flow in self.inputs + self.outputs}
65
92
 
66
- def create_model(self, model: SystemModel) -> 'ComponentModel':
93
+ def create_model(self, model: SystemModel) -> ComponentModel:
67
94
  self._plausibility_checks()
68
95
  self.model = ComponentModel(model, self)
69
96
  return self.model
70
97
 
71
- def transform_data(self, flow_system: 'FlowSystem') -> None:
98
+ def transform_data(self, flow_system: FlowSystem) -> None:
72
99
  if self.on_off_parameters is not None:
73
100
  self.on_off_parameters.transform_data(flow_system, self.label_full)
74
101
 
75
- def infos(self, use_numpy=True, use_element_label: bool = False) -> Dict:
102
+ def infos(self, use_numpy=True, use_element_label: bool = False) -> dict:
76
103
  infos = super().infos(use_numpy, use_element_label)
77
104
  infos['inputs'] = [flow.infos(use_numpy, use_element_label) for flow in self.inputs]
78
105
  infos['outputs'] = [flow.infos(use_numpy, use_element_label) for flow in self.outputs]
@@ -92,38 +119,89 @@ class Component(Element):
92
119
  @register_class_for_io
93
120
  class Bus(Element):
94
121
  """
95
- A Bus represents a nodal balance between the flow rates of its incoming and outgoing Flows.
122
+ Buses represent nodal balances between flow rates, serving as connection points.
123
+
124
+ A Bus enforces energy or material balance constraints where the sum of all incoming
125
+ flows must equal the sum of all outgoing flows at each time step. Buses represent
126
+ physical or logical connection points for energy carriers (electricity, heat, gas)
127
+ or material flows between different Components.
128
+
129
+ Args:
130
+ label: The label of the Element. Used to identify it in the FlowSystem.
131
+ excess_penalty_per_flow_hour: Penalty costs for bus balance violations.
132
+ When None, no excess/deficit is allowed (hard constraint). When set to a
133
+ value > 0, allows bus imbalances at penalty cost. Default is 1e5 (high penalty).
134
+ meta_data: Used to store additional information. Not used internally but saved
135
+ in results. Only use Python native types.
136
+
137
+ Examples:
138
+ Electrical bus with strict balance:
139
+
140
+ ```python
141
+ electricity_bus = Bus(
142
+ label='main_electrical_bus',
143
+ excess_penalty_per_flow_hour=None, # No imbalance allowed
144
+ )
145
+ ```
146
+
147
+ Heat network with penalty for imbalances:
148
+
149
+ ```python
150
+ heat_network = Bus(
151
+ label='district_heating_network',
152
+ excess_penalty_per_flow_hour=1000, # €1000/MWh penalty for imbalance
153
+ )
154
+ ```
155
+
156
+ Material flow with time-varying penalties:
157
+
158
+ ```python
159
+ material_hub = Bus(
160
+ label='material_processing_hub',
161
+ excess_penalty_per_flow_hour=waste_disposal_costs, # Time series
162
+ )
163
+ ```
164
+
165
+ Note:
166
+ The bus balance equation enforced is: Σ(inflows) = Σ(outflows) + excess - deficit
167
+
168
+ When excess_penalty_per_flow_hour is None, excess and deficit are forced to zero.
169
+ When a penalty cost is specified, the optimization can choose to violate the
170
+ balance if economically beneficial, paying the penalty.
171
+ The penalty is added to the objective directly.
172
+
173
+ Empty `inputs` and `outputs` lists are initialized and populated automatically
174
+ by the FlowSystem during system setup.
96
175
  """
97
176
 
98
177
  def __init__(
99
- self, label: str, excess_penalty_per_flow_hour: Optional[NumericDataTS] = 1e5, meta_data: Optional[Dict] = None
178
+ self,
179
+ label: str,
180
+ excess_penalty_per_flow_hour: NumericData | NumericDataTS | None = 1e5,
181
+ meta_data: dict | None = None,
100
182
  ):
101
- """
102
- Args:
103
- label: The label of the Element. Used to identify it in the FlowSystem
104
- excess_penalty_per_flow_hour: excess costs / penalty costs (bus balance compensation)
105
- (none/ 0 -> no penalty). The default is 1e5.
106
- (Take care: if you use a timeseries (no scalar), timeseries is aggregated if calculation_type = aggregated!)
107
- meta_data: used to store more information about the Element. Is not used internally, but saved in the results. Only use python native types.
108
- """
109
183
  super().__init__(label, meta_data=meta_data)
110
184
  self.excess_penalty_per_flow_hour = excess_penalty_per_flow_hour
111
- self.inputs: List[Flow] = []
112
- self.outputs: List[Flow] = []
185
+ self.inputs: list[Flow] = []
186
+ self.outputs: list[Flow] = []
113
187
 
114
- def create_model(self, model: SystemModel) -> 'BusModel':
188
+ def create_model(self, model: SystemModel) -> BusModel:
115
189
  self._plausibility_checks()
116
190
  self.model = BusModel(model, self)
117
191
  return self.model
118
192
 
119
- def transform_data(self, flow_system: 'FlowSystem'):
193
+ def transform_data(self, flow_system: FlowSystem):
120
194
  self.excess_penalty_per_flow_hour = flow_system.create_time_series(
121
195
  f'{self.label_full}|excess_penalty_per_flow_hour', self.excess_penalty_per_flow_hour
122
196
  )
123
197
 
124
198
  def _plausibility_checks(self) -> None:
125
- if self.excess_penalty_per_flow_hour is not None and (self.excess_penalty_per_flow_hour == 0).all():
126
- logger.warning(f'In Bus {self.label}, the excess_penalty_per_flow_hour is 0. Use "None" or a value > 0.')
199
+ if self.excess_penalty_per_flow_hour is not None:
200
+ zero_penalty = np.all(np.equal(self.excess_penalty_per_flow_hour, 0))
201
+ if zero_penalty:
202
+ logger.warning(
203
+ f'In Bus {self.label}, the excess_penalty_per_flow_hour is 0. Use "None" or a value > 0.'
204
+ )
127
205
 
128
206
  @property
129
207
  def with_excess(self) -> bool:
@@ -145,60 +223,167 @@ class Connection:
145
223
 
146
224
  @register_class_for_io
147
225
  class Flow(Element):
148
- r"""
149
- A **Flow** moves energy (or material) between a [Bus][flixopt.elements.Bus] and a [Component][flixopt.elements.Component] in a predefined direction.
150
- The flow-rate is the main optimization variable of the **Flow**.
226
+ """Define a directed flow of energy or material between bus and component.
227
+
228
+ A Flow represents the transfer of energy (electricity, heat, fuel) or material
229
+ between a Bus and a Component in a specific direction. The flow rate is the
230
+ primary optimization variable, with constraints and costs defined through
231
+ various parameters. Flows can have fixed or variable sizes, operational
232
+ constraints, and complex on/off behavior.
233
+
234
+ Key Concepts:
235
+ **Flow Rate**: The instantaneous rate of energy/material transfer (optimization variable) [kW, m³/h, kg/h]
236
+ **Flow Hours**: Amount of energy/material transferred per timestep. [kWh, m³, kg]
237
+ **Flow Size**: The maximum capacity or nominal rating of the flow [kW, m³/h, kg/h]
238
+ **Relative Bounds**: Flow rate limits expressed as fractions of flow size
239
+
240
+ Integration with Parameter Classes:
241
+ - **InvestParameters**: Used for `size` when flow Size is an investment decision
242
+ - **OnOffParameters**: Used for `on_off_parameters` when flow has discrete states
243
+
244
+ Args:
245
+ label: Unique identifier for the flow within its component.
246
+ The full label combines component and flow labels.
247
+ bus: Label of the bus this flow connects to. Must match a bus in the FlowSystem.
248
+ size: Flow capacity or nominal rating. Can be:
249
+ - Scalar value for fixed capacity
250
+ - InvestParameters for investment-based sizing decisions
251
+ - None to use large default value (CONFIG.modeling.BIG)
252
+ relative_minimum: Minimum flow rate as fraction of size.
253
+ Example: 0.2 means flow cannot go below 20% of rated capacity.
254
+ relative_maximum: Maximum flow rate as fraction of size (typically 1.0).
255
+ Values >1.0 allow temporary overload operation.
256
+ load_factor_min: Minimum average utilization over the time horizon (0-1).
257
+ Calculated as total flow hours divided by (size × total time).
258
+ load_factor_max: Maximum average utilization over the time horizon (0-1).
259
+ Useful for equipment duty cycle limits or maintenance scheduling.
260
+ effects_per_flow_hour: Operational costs and impacts per unit of flow-time.
261
+ Dictionary mapping effect names to unit costs (e.g., fuel costs, emissions).
262
+ on_off_parameters: Binary operation constraints using OnOffParameters.
263
+ Enables modeling of startup costs, minimum run times, cycling limits.
264
+ Only relevant when relative_minimum > 0 or discrete operation is required.
265
+ flow_hours_total_max: Maximum cumulative flow-hours over time horizon.
266
+ Alternative to load_factor_max for absolute energy/material limits.
267
+ flow_hours_total_min: Minimum cumulative flow-hours over time horizon.
268
+ Alternative to load_factor_min for contractual or operational requirements.
269
+ fixed_relative_profile: Predetermined flow pattern as fraction of size.
270
+ When specified, flow rate becomes: size × fixed_relative_profile(t).
271
+ Used for: demand profiles, renewable generation, fixed schedules.
272
+ previous_flow_rate: Initial flow state for startup/shutdown dynamics.
273
+ Used with on_off_parameters to determine initial on/off status.
274
+ If None, assumes flow was off in previous time period.
275
+ meta_data: Additional information stored with results but not used in optimization.
276
+ Must contain only Python native types (dict, list, str, int, float, bool).
277
+
278
+ Examples:
279
+ Basic power flow with fixed capacity:
280
+
281
+ ```python
282
+ generator_output = Flow(
283
+ label='electricity_out',
284
+ bus='electricity_grid',
285
+ size=100, # 100 MW capacity
286
+ relative_minimum=0.4, # Cannot operate below 40 MW
287
+ effects_per_flow_hour={'fuel_cost': 45, 'co2_emissions': 0.8},
288
+ )
289
+ ```
290
+
291
+ Investment decision for battery capacity:
292
+
293
+ ```python
294
+ battery_flow = Flow(
295
+ label='electricity_storage',
296
+ bus='electricity_grid',
297
+ size=InvestParameters(
298
+ minimum_size=10, # Minimum 10 MWh
299
+ maximum_size=100, # Maximum 100 MWh
300
+ specific_effects={'cost': 150_000}, # €150k/MWh annualized
301
+ ),
302
+ )
303
+ ```
304
+
305
+ Heat pump with startup costs and minimum run times:
306
+
307
+ ```python
308
+ heat_pump = Flow(
309
+ label='heat_output',
310
+ bus='heating_network',
311
+ size=50, # 50 kW thermal
312
+ relative_minimum=0.3, # Minimum 15 kW output when on
313
+ effects_per_flow_hour={'electricity_cost': 25, 'maintenance': 2},
314
+ on_off_parameters=OnOffParameters(
315
+ effects_per_switch_on={'startup_cost': 100, 'wear': 0.1},
316
+ consecutive_on_hours_min=2, # Must run at least 2 hours
317
+ consecutive_off_hours_min=1, # Must stay off at least 1 hour
318
+ switch_on_total_max=200, # Maximum 200 starts per year
319
+ ),
320
+ )
321
+ ```
322
+
323
+ Fixed renewable generation profile:
324
+
325
+ ```python
326
+ solar_generation = Flow(
327
+ label='solar_power',
328
+ bus='electricity_grid',
329
+ size=25, # 25 MW installed capacity
330
+ fixed_relative_profile=np.array([0, 0.1, 0.4, 0.8, 0.9, 0.7, 0.3, 0.1, 0]),
331
+ effects_per_flow_hour={'maintenance_costs': 5}, # €5/MWh maintenance
332
+ )
333
+ ```
334
+
335
+ Industrial process with annual utilization limits:
336
+
337
+ ```python
338
+ production_line = Flow(
339
+ label='product_output',
340
+ bus='product_market',
341
+ size=1000, # 1000 units/hour capacity
342
+ load_factor_min=0.6, # Must achieve 60% annual utilization
343
+ load_factor_max=0.85, # Cannot exceed 85% for maintenance
344
+ effects_per_flow_hour={'variable_cost': 12, 'quality_control': 0.5},
345
+ )
346
+ ```
347
+
348
+ Design Considerations:
349
+ **Size vs Load Factors**: Use `flow_hours_total_min/max` for absolute limits,
350
+ `load_factor_min/max` for utilization-based constraints.
351
+
352
+ **Relative Bounds**: Set `relative_minimum > 0` only when equipment cannot
353
+ operate below that level. Use `on_off_parameters` for discrete on/off behavior.
354
+
355
+ **Fixed Profiles**: Use `fixed_relative_profile` for known exact patterns,
356
+ `relative_maximum` for upper bounds on optimization variables.
357
+
358
+ Notes:
359
+ - Default size (CONFIG.modeling.BIG) is used when size=None
360
+ - list inputs for previous_flow_rate are converted to NumPy arrays
361
+ - Flow direction is determined by component input/output designation
362
+
363
+ Deprecated:
364
+ Passing Bus objects to `bus` parameter. Use bus label strings instead.
365
+
151
366
  """
152
367
 
153
368
  def __init__(
154
369
  self,
155
370
  label: str,
156
371
  bus: str,
157
- size: Union[Scalar, InvestParameters] = None,
158
- fixed_relative_profile: Optional[NumericDataTS] = None,
372
+ size: Scalar | InvestParameters | None = None,
373
+ fixed_relative_profile: NumericDataTS | None = None,
159
374
  relative_minimum: NumericDataTS = 0,
160
375
  relative_maximum: NumericDataTS = 1,
161
- effects_per_flow_hour: Optional[EffectValuesUser] = None,
162
- on_off_parameters: Optional[OnOffParameters] = None,
163
- flow_hours_total_max: Optional[Scalar] = None,
164
- flow_hours_total_min: Optional[Scalar] = None,
165
- load_factor_min: Optional[Scalar] = None,
166
- load_factor_max: Optional[Scalar] = None,
167
- previous_flow_rate: Optional[NumericData] = None,
168
- meta_data: Optional[Dict] = None,
376
+ effects_per_flow_hour: EffectValuesUser | None = None,
377
+ on_off_parameters: OnOffParameters | None = None,
378
+ flow_hours_total_max: Scalar | None = None,
379
+ flow_hours_total_min: Scalar | None = None,
380
+ load_factor_min: Scalar | None = None,
381
+ load_factor_max: Scalar | None = None,
382
+ previous_flow_rate: NumericData | None = None,
383
+ meta_data: dict | None = None,
169
384
  ):
170
- r"""
171
- Args:
172
- label: The label of the FLow. Used to identify it in the FlowSystem. Its `full_label` consists of the label of the Component and the label of the Flow.
173
- bus: blabel of the bus the flow is connected to.
174
- size: size of the flow. If InvestmentParameters is used, size is optimized.
175
- If size is None, a default value is used.
176
- relative_minimum: min value is relative_minimum multiplied by size
177
- relative_maximum: max value is relative_maximum multiplied by size. If size = max then relative_maximum=1
178
- load_factor_min: minimal load factor general: avg Flow per nominalVal/investSize
179
- (e.g. boiler, kW/kWh=h; solarthermal: kW/m²;
180
- def: :math:`load\_factor:= sumFlowHours/ (nominal\_val \cdot \Delta t_{tot})`
181
- load_factor_max: maximal load factor (see minimal load factor)
182
- effects_per_flow_hour: operational costs, costs per flow-"work"
183
- on_off_parameters: If present, flow can be "off", i.e. be zero (only relevant if relative_minimum > 0)
184
- Therefore a binary var "on" is used. Further, several other restrictions and effects can be modeled
185
- through this On/Off State (See OnOffParameters)
186
- flow_hours_total_max: maximum flow-hours ("flow-work")
187
- (if size is not const, maybe load_factor_max is the better choice!)
188
- flow_hours_total_min: minimum flow-hours ("flow-work")
189
- (if size is not predefined, maybe load_factor_min is the better choice!)
190
- fixed_relative_profile: fixed relative values for flow (if given).
191
- flow_rate(t) := fixed_relative_profile(t) * size(t)
192
- With this value, the flow_rate is no optimization-variable anymore.
193
- (relative_minimum and relative_maximum are ignored)
194
- used for fixed load or supply profiles, i.g. heat demand, wind-power, solarthermal
195
- If the load-profile is just an upper limit, use relative_maximum instead.
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.
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.
199
- """
200
385
  super().__init__(label, meta_data=meta_data)
201
- self.size = size or CONFIG.modeling.BIG # Default size
386
+ self.size = CONFIG.modeling.BIG if size is None else size
202
387
  self.relative_minimum = relative_minimum
203
388
  self.relative_maximum = relative_maximum
204
389
  self.fixed_relative_profile = fixed_relative_profile
@@ -216,7 +401,7 @@ class Flow(Element):
216
401
  )
217
402
 
218
403
  self.component: str = 'UnknownComponent'
219
- self.is_input_in_component: Optional[bool] = None
404
+ self.is_input_in_component: bool | None = None
220
405
  if isinstance(bus, Bus):
221
406
  self.bus = bus.label_full
222
407
  warnings.warn(
@@ -230,12 +415,12 @@ class Flow(Element):
230
415
  self.bus = bus
231
416
  self._bus_object = None
232
417
 
233
- def create_model(self, model: SystemModel) -> 'FlowModel':
418
+ def create_model(self, model: SystemModel) -> FlowModel:
234
419
  self._plausibility_checks()
235
420
  self.model = FlowModel(model, self)
236
421
  return self.model
237
422
 
238
- def transform_data(self, flow_system: 'FlowSystem'):
423
+ def transform_data(self, flow_system: FlowSystem):
239
424
  self.relative_minimum = flow_system.create_time_series(
240
425
  f'{self.label_full}|relative_minimum', self.relative_minimum
241
426
  )
@@ -253,12 +438,12 @@ class Flow(Element):
253
438
  if isinstance(self.size, InvestParameters):
254
439
  self.size.transform_data(flow_system)
255
440
 
256
- def infos(self, use_numpy: bool = True, use_element_label: bool = False) -> Dict:
441
+ def infos(self, use_numpy: bool = True, use_element_label: bool = False) -> dict:
257
442
  infos = super().infos(use_numpy, use_element_label)
258
443
  infos['is_input_in_component'] = self.is_input_in_component
259
444
  return infos
260
445
 
261
- def to_dict(self) -> Dict:
446
+ def to_dict(self) -> dict:
262
447
  data = super().to_dict()
263
448
  if isinstance(data.get('previous_flow_rate'), np.ndarray):
264
449
  data['previous_flow_rate'] = data['previous_flow_rate'].tolist()
@@ -311,11 +496,11 @@ class FlowModel(ElementModel):
311
496
  def __init__(self, model: SystemModel, element: Flow):
312
497
  super().__init__(model, element)
313
498
  self.element: Flow = element
314
- self.flow_rate: Optional[linopy.Variable] = None
315
- self.total_flow_hours: Optional[linopy.Variable] = None
499
+ self.flow_rate: linopy.Variable | None = None
500
+ self.total_flow_hours: linopy.Variable | None = None
316
501
 
317
- self.on_off: Optional[OnOffModel] = None
318
- self._investment: Optional[InvestmentModel] = None
502
+ self.on_off: OnOffModel | None = None
503
+ self._investment: InvestmentModel | None = None
319
504
 
320
505
  def do_modeling(self):
321
506
  # eq relative_minimum(t) * size <= flow_rate(t) <= relative_maximum(t) * size
@@ -430,7 +615,7 @@ class FlowModel(ElementModel):
430
615
  )
431
616
 
432
617
  @property
433
- def flow_rate_bounds_on(self) -> Tuple[NumericData, NumericData]:
618
+ def flow_rate_bounds_on(self) -> tuple[NumericData, NumericData]:
434
619
  """Returns absolute flow rate bounds. Important for OnOffModel"""
435
620
  relative_minimum, relative_maximum = self.flow_rate_lower_bound_relative, self.flow_rate_upper_bound_relative
436
621
  size = self.element.size
@@ -485,8 +670,8 @@ class BusModel(ElementModel):
485
670
  def __init__(self, model: SystemModel, element: Bus):
486
671
  super().__init__(model, element)
487
672
  self.element: Bus = element
488
- self.excess_input: Optional[linopy.Variable] = None
489
- self.excess_output: Optional[linopy.Variable] = None
673
+ self.excess_input: linopy.Variable | None = None
674
+ self.excess_output: linopy.Variable | None = None
490
675
 
491
676
  def do_modeling(self) -> None:
492
677
  # inputs == outputs
@@ -528,7 +713,7 @@ class ComponentModel(ElementModel):
528
713
  def __init__(self, model: SystemModel, element: Component):
529
714
  super().__init__(model, element)
530
715
  self.element: Component = element
531
- self.on_off: Optional[OnOffModel] = None
716
+ self.on_off: OnOffModel | None = None
532
717
 
533
718
  def do_modeling(self):
534
719
  """Initiates all FlowModels"""