flixopt 2.2.0b0__py3-none-any.whl → 2.2.0rc2__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/examples/00-Minimal Example.md +1 -1
- docs/examples/01-Basic Example.md +1 -1
- docs/examples/02-Complex Example.md +1 -1
- docs/examples/index.md +1 -1
- docs/faq/contribute.md +26 -14
- docs/faq/index.md +1 -1
- docs/javascripts/mathjax.js +1 -1
- docs/user-guide/Mathematical Notation/Bus.md +1 -1
- docs/user-guide/Mathematical Notation/Effects, Penalty & Objective.md +13 -13
- docs/user-guide/Mathematical Notation/Flow.md +1 -1
- docs/user-guide/Mathematical Notation/LinearConverter.md +2 -2
- docs/user-guide/Mathematical Notation/Piecewise.md +1 -1
- docs/user-guide/Mathematical Notation/Storage.md +1 -1
- docs/user-guide/Mathematical Notation/index.md +1 -1
- docs/user-guide/Mathematical Notation/others.md +1 -1
- docs/user-guide/index.md +2 -2
- flixopt/__init__.py +5 -0
- flixopt/aggregation.py +0 -1
- flixopt/calculation.py +40 -72
- flixopt/commons.py +10 -1
- flixopt/components.py +326 -154
- flixopt/core.py +459 -966
- flixopt/effects.py +67 -270
- flixopt/elements.py +76 -84
- flixopt/features.py +172 -154
- flixopt/flow_system.py +70 -99
- flixopt/interface.py +315 -147
- flixopt/io.py +27 -56
- flixopt/linear_converters.py +3 -3
- flixopt/network_app.py +755 -0
- flixopt/plotting.py +16 -34
- flixopt/results.py +108 -806
- flixopt/structure.py +11 -67
- flixopt/utils.py +9 -6
- {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/METADATA +63 -42
- flixopt-2.2.0rc2.dist-info/RECORD +54 -0
- {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/WHEEL +1 -1
- scripts/extract_release_notes.py +45 -0
- docs/release-notes/_template.txt +0 -32
- docs/release-notes/index.md +0 -7
- docs/release-notes/v2.0.0.md +0 -93
- docs/release-notes/v2.0.1.md +0 -12
- docs/release-notes/v2.1.0.md +0 -31
- docs/release-notes/v2.2.0.md +0 -55
- docs/user-guide/Mathematical Notation/Investment.md +0 -115
- flixopt-2.2.0b0.dist-info/RECORD +0 -59
- {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/licenses/LICENSE +0 -0
- {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/top_level.txt +0 -0
flixopt/elements.py
CHANGED
|
@@ -10,10 +10,10 @@ import linopy
|
|
|
10
10
|
import numpy as np
|
|
11
11
|
|
|
12
12
|
from .config import CONFIG
|
|
13
|
-
from .core import
|
|
14
|
-
from .effects import
|
|
15
|
-
from .features import InvestmentModel, OnOffModel, PreventSimultaneousUsageModel
|
|
16
|
-
from .interface import InvestParameters, OnOffParameters
|
|
13
|
+
from .core import NumericData, NumericDataTS, PlausibilityError, Scalar, TimeSeriesCollection
|
|
14
|
+
from .effects import EffectValuesUser
|
|
15
|
+
from .features import InvestmentModel, OnOffModel, PiecewiseEffectsPerFlowHourModel, PreventSimultaneousUsageModel
|
|
16
|
+
from .interface import InvestParameters, OnOffParameters, PiecewiseEffectsPerFlowHour
|
|
17
17
|
from .structure import Element, ElementModel, SystemModel, register_class_for_io
|
|
18
18
|
|
|
19
19
|
if TYPE_CHECKING:
|
|
@@ -96,7 +96,7 @@ class Bus(Element):
|
|
|
96
96
|
"""
|
|
97
97
|
|
|
98
98
|
def __init__(
|
|
99
|
-
self, label: str, excess_penalty_per_flow_hour: Optional[
|
|
99
|
+
self, label: str, excess_penalty_per_flow_hour: Optional[NumericDataTS] = 1e5, meta_data: Optional[Dict] = None
|
|
100
100
|
):
|
|
101
101
|
"""
|
|
102
102
|
Args:
|
|
@@ -154,17 +154,18 @@ class Flow(Element):
|
|
|
154
154
|
self,
|
|
155
155
|
label: str,
|
|
156
156
|
bus: str,
|
|
157
|
-
size: Union[
|
|
158
|
-
fixed_relative_profile: Optional[
|
|
159
|
-
relative_minimum:
|
|
160
|
-
relative_maximum:
|
|
161
|
-
effects_per_flow_hour: Optional[
|
|
157
|
+
size: Union[Scalar, InvestParameters] = None,
|
|
158
|
+
fixed_relative_profile: Optional[NumericDataTS] = None,
|
|
159
|
+
relative_minimum: NumericDataTS = 0,
|
|
160
|
+
relative_maximum: NumericDataTS = 1,
|
|
161
|
+
effects_per_flow_hour: Optional[EffectValuesUser] = None,
|
|
162
|
+
piecewise_effects_per_flow_hour: Optional[PiecewiseEffectsPerFlowHour] = None,
|
|
162
163
|
on_off_parameters: Optional[OnOffParameters] = None,
|
|
163
|
-
flow_hours_total_max: Optional[
|
|
164
|
-
flow_hours_total_min: Optional[
|
|
165
|
-
load_factor_min: Optional[
|
|
166
|
-
load_factor_max: Optional[
|
|
167
|
-
previous_flow_rate: Optional[
|
|
164
|
+
flow_hours_total_max: Optional[Scalar] = None,
|
|
165
|
+
flow_hours_total_min: Optional[Scalar] = None,
|
|
166
|
+
load_factor_min: Optional[Scalar] = None,
|
|
167
|
+
load_factor_max: Optional[Scalar] = None,
|
|
168
|
+
previous_flow_rate: Optional[NumericData] = None,
|
|
168
169
|
meta_data: Optional[Dict] = None,
|
|
169
170
|
):
|
|
170
171
|
r"""
|
|
@@ -180,6 +181,7 @@ class Flow(Element):
|
|
|
180
181
|
def: :math:`load\_factor:= sumFlowHours/ (nominal\_val \cdot \Delta t_{tot})`
|
|
181
182
|
load_factor_max: maximal load factor (see minimal load factor)
|
|
182
183
|
effects_per_flow_hour: operational costs, costs per flow-"work"
|
|
184
|
+
piecewise_effects_per_flow_hour: piecewise relation between flow hours and effects
|
|
183
185
|
on_off_parameters: If present, flow can be "off", i.e. be zero (only relevant if relative_minimum > 0)
|
|
184
186
|
Therefore a binary var "on" is used. Further, several other restrictions and effects can be modeled
|
|
185
187
|
through this On/Off State (See OnOffParameters)
|
|
@@ -207,6 +209,7 @@ class Flow(Element):
|
|
|
207
209
|
self.load_factor_max = load_factor_max
|
|
208
210
|
# self.positive_gradient = TimeSeries('positive_gradient', positive_gradient, self)
|
|
209
211
|
self.effects_per_flow_hour = effects_per_flow_hour if effects_per_flow_hour is not None else {}
|
|
212
|
+
self.piecewise_effects_per_flow_hour = piecewise_effects_per_flow_hour
|
|
210
213
|
self.flow_hours_total_max = flow_hours_total_max
|
|
211
214
|
self.flow_hours_total_min = flow_hours_total_min
|
|
212
215
|
self.on_off_parameters = on_off_parameters
|
|
@@ -248,25 +251,12 @@ class Flow(Element):
|
|
|
248
251
|
self.effects_per_flow_hour = flow_system.create_effect_time_series(
|
|
249
252
|
self.label_full, self.effects_per_flow_hour, 'per_flow_hour'
|
|
250
253
|
)
|
|
251
|
-
self.
|
|
252
|
-
|
|
253
|
-
)
|
|
254
|
-
self.flow_hours_total_min = flow_system.create_time_series(
|
|
255
|
-
f'{self.label_full}|flow_hours_total_min', self.flow_hours_total_min, has_time_dim=False
|
|
256
|
-
)
|
|
257
|
-
self.load_factor_max = flow_system.create_time_series(
|
|
258
|
-
f'{self.label_full}|load_factor_max', self.load_factor_max, has_time_dim=False
|
|
259
|
-
)
|
|
260
|
-
self.load_factor_min = flow_system.create_time_series(
|
|
261
|
-
f'{self.label_full}|load_factor_min', self.load_factor_min, has_time_dim=False
|
|
262
|
-
)
|
|
263
|
-
|
|
254
|
+
if self.piecewise_effects_per_flow_hour is not None:
|
|
255
|
+
self.piecewise_effects_per_flow_hour.transform_data(flow_system, self.label_full)
|
|
264
256
|
if self.on_off_parameters is not None:
|
|
265
257
|
self.on_off_parameters.transform_data(flow_system, self.label_full)
|
|
266
258
|
if isinstance(self.size, InvestParameters):
|
|
267
|
-
self.size.transform_data(flow_system
|
|
268
|
-
else:
|
|
269
|
-
self.size = flow_system.create_time_series(f'{self.label_full}|size', self.size, has_time_dim=False)
|
|
259
|
+
self.size.transform_data(flow_system)
|
|
270
260
|
|
|
271
261
|
def infos(self, use_numpy: bool = True, use_element_label: bool = False) -> Dict:
|
|
272
262
|
infos = super().infos(use_numpy, use_element_label)
|
|
@@ -284,8 +274,8 @@ class Flow(Element):
|
|
|
284
274
|
if np.any(self.relative_minimum > self.relative_maximum):
|
|
285
275
|
raise PlausibilityError(self.label_full + ': Take care, that relative_minimum <= relative_maximum!')
|
|
286
276
|
|
|
287
|
-
if
|
|
288
|
-
|
|
277
|
+
if (
|
|
278
|
+
self.size == CONFIG.modeling.BIG and self.fixed_relative_profile is not None
|
|
289
279
|
): # Default Size --> Most likely by accident
|
|
290
280
|
logger.warning(
|
|
291
281
|
f'Flow "{self.label}" has no size assigned, but a "fixed_relative_profile". '
|
|
@@ -294,14 +284,15 @@ class Flow(Element):
|
|
|
294
284
|
)
|
|
295
285
|
|
|
296
286
|
if self.fixed_relative_profile is not None and self.on_off_parameters is not None:
|
|
297
|
-
|
|
298
|
-
f'Flow {self.label} has both a fixed_relative_profile and an on_off_parameters.'
|
|
299
|
-
f'
|
|
287
|
+
raise ValueError(
|
|
288
|
+
f'Flow {self.label} has both a fixed_relative_profile and an on_off_parameters. This is not supported. '
|
|
289
|
+
f'Use relative_minimum and relative_maximum instead, '
|
|
290
|
+
f'if you want to allow flows to be switched on and off.'
|
|
300
291
|
)
|
|
301
292
|
|
|
302
293
|
if (self.relative_minimum > 0).any() and self.on_off_parameters is None:
|
|
303
294
|
logger.warning(
|
|
304
|
-
f'Flow {self.label} has a relative_minimum of {self.relative_minimum.
|
|
295
|
+
f'Flow {self.label} has a relative_minimum of {self.relative_minimum.active_data} and no on_off_parameters. '
|
|
305
296
|
f'This prevents the flow_rate from switching off (flow_rate = 0). '
|
|
306
297
|
f'Consider using on_off_parameters to allow the flow to be switched on and off.'
|
|
307
298
|
)
|
|
@@ -337,7 +328,7 @@ class FlowModel(ElementModel):
|
|
|
337
328
|
self._model.add_variables(
|
|
338
329
|
lower=self.flow_rate_lower_bound,
|
|
339
330
|
upper=self.flow_rate_upper_bound,
|
|
340
|
-
coords=self._model.
|
|
331
|
+
coords=self._model.coords,
|
|
341
332
|
name=f'{self.label_full}|flow_rate',
|
|
342
333
|
),
|
|
343
334
|
'flow_rate',
|
|
@@ -366,8 +357,10 @@ class FlowModel(ElementModel):
|
|
|
366
357
|
label_of_element=self.label_of_element,
|
|
367
358
|
parameters=self.element.size,
|
|
368
359
|
defining_variable=self.flow_rate,
|
|
369
|
-
relative_bounds_of_defining_variable=(
|
|
370
|
-
|
|
360
|
+
relative_bounds_of_defining_variable=(
|
|
361
|
+
self.flow_rate_lower_bound_relative,
|
|
362
|
+
self.flow_rate_upper_bound_relative,
|
|
363
|
+
),
|
|
371
364
|
on_variable=self.on_off.on if self.on_off is not None else None,
|
|
372
365
|
),
|
|
373
366
|
'investment',
|
|
@@ -376,9 +369,9 @@ class FlowModel(ElementModel):
|
|
|
376
369
|
|
|
377
370
|
self.total_flow_hours = self.add(
|
|
378
371
|
self._model.add_variables(
|
|
379
|
-
lower=
|
|
380
|
-
upper=
|
|
381
|
-
coords=
|
|
372
|
+
lower=self.element.flow_hours_total_min if self.element.flow_hours_total_min is not None else 0,
|
|
373
|
+
upper=self.element.flow_hours_total_max if self.element.flow_hours_total_max is not None else np.inf,
|
|
374
|
+
coords=None,
|
|
382
375
|
name=f'{self.label_full}|total_flow_hours',
|
|
383
376
|
),
|
|
384
377
|
'total_flow_hours',
|
|
@@ -386,7 +379,7 @@ class FlowModel(ElementModel):
|
|
|
386
379
|
|
|
387
380
|
self.add(
|
|
388
381
|
self._model.add_constraints(
|
|
389
|
-
self.total_flow_hours == (self.flow_rate * self._model.hours_per_step).sum(
|
|
382
|
+
self.total_flow_hours == (self.flow_rate * self._model.hours_per_step).sum(),
|
|
390
383
|
name=f'{self.label_full}|total_flow_hours',
|
|
391
384
|
),
|
|
392
385
|
'total_flow_hours',
|
|
@@ -398,33 +391,40 @@ class FlowModel(ElementModel):
|
|
|
398
391
|
# Shares
|
|
399
392
|
self._create_shares()
|
|
400
393
|
|
|
401
|
-
def results_structure(self):
|
|
402
|
-
return {
|
|
403
|
-
**super().results_structure(),
|
|
404
|
-
'start': self.element.bus if self.element.is_input_in_component else self.element.component,
|
|
405
|
-
'end': self.element.component if self.element.is_input_in_component else self.element.bus,
|
|
406
|
-
'component': self.element.component,
|
|
407
|
-
}
|
|
408
|
-
|
|
409
394
|
def _create_shares(self):
|
|
410
395
|
# Arbeitskosten:
|
|
411
396
|
if self.element.effects_per_flow_hour != {}:
|
|
412
397
|
self._model.effects.add_share_to_effects(
|
|
413
398
|
name=self.label_full, # Use the full label of the element
|
|
414
399
|
expressions={
|
|
415
|
-
effect: self.flow_rate * self._model.hours_per_step * factor.
|
|
400
|
+
effect: self.flow_rate * self._model.hours_per_step * factor.active_data
|
|
416
401
|
for effect, factor in self.element.effects_per_flow_hour.items()
|
|
417
402
|
},
|
|
418
403
|
target='operation',
|
|
419
404
|
)
|
|
420
405
|
|
|
406
|
+
if self.element.piecewise_effects_per_flow_hour is not None:
|
|
407
|
+
self.piecewise_effects = self.add(
|
|
408
|
+
PiecewiseEffectsPerFlowHourModel(
|
|
409
|
+
model=self._model,
|
|
410
|
+
label_of_element=self.label_of_element,
|
|
411
|
+
piecewise_origin=(
|
|
412
|
+
self.flow_rate.name,
|
|
413
|
+
self.element.piecewise_effects_per_flow_hour.piecewise_flow_rate,
|
|
414
|
+
),
|
|
415
|
+
piecewise_shares=self.element.piecewise_effects_per_flow_hour.piecewise_shares,
|
|
416
|
+
zero_point=self.on_off.on if self.on_off is not None else False,
|
|
417
|
+
),
|
|
418
|
+
)
|
|
419
|
+
self.piecewise_effects.do_modeling()
|
|
420
|
+
|
|
421
421
|
def _create_bounds_for_load_factor(self):
|
|
422
422
|
# TODO: Add Variable load_factor for better evaluation?
|
|
423
423
|
|
|
424
424
|
# eq: var_sumFlowHours <= size * dt_tot * load_factor_max
|
|
425
425
|
if self.element.load_factor_max is not None:
|
|
426
426
|
name_short = 'load_factor_max'
|
|
427
|
-
flow_hours_per_size_max = self._model.hours_per_step.sum(
|
|
427
|
+
flow_hours_per_size_max = self._model.hours_per_step.sum() * self.element.load_factor_max
|
|
428
428
|
size = self.element.size if self._investment is None else self._investment.size
|
|
429
429
|
|
|
430
430
|
self.add(
|
|
@@ -438,7 +438,7 @@ class FlowModel(ElementModel):
|
|
|
438
438
|
# eq: size * sum(dt)* load_factor_min <= var_sumFlowHours
|
|
439
439
|
if self.element.load_factor_min is not None:
|
|
440
440
|
name_short = 'load_factor_min'
|
|
441
|
-
flow_hours_per_size_min = self._model.hours_per_step.sum(
|
|
441
|
+
flow_hours_per_size_min = self._model.hours_per_step.sum() * self.element.load_factor_min
|
|
442
442
|
size = self.element.size if self._investment is None else self._investment.size
|
|
443
443
|
|
|
444
444
|
self.add(
|
|
@@ -450,36 +450,34 @@ class FlowModel(ElementModel):
|
|
|
450
450
|
)
|
|
451
451
|
|
|
452
452
|
@property
|
|
453
|
-
def flow_rate_bounds_on(self) -> Tuple[
|
|
453
|
+
def flow_rate_bounds_on(self) -> Tuple[NumericData, NumericData]:
|
|
454
454
|
"""Returns absolute flow rate bounds. Important for OnOffModel"""
|
|
455
455
|
relative_minimum, relative_maximum = self.flow_rate_lower_bound_relative, self.flow_rate_upper_bound_relative
|
|
456
456
|
size = self.element.size
|
|
457
457
|
if not isinstance(size, InvestParameters):
|
|
458
|
-
return relative_minimum *
|
|
458
|
+
return relative_minimum * size, relative_maximum * size
|
|
459
459
|
if size.fixed_size is not None:
|
|
460
|
-
return
|
|
461
|
-
|
|
462
|
-
return (relative_minimum * extract_data(size.minimum_size),
|
|
463
|
-
relative_maximum * extract_data(size.maximum_size))
|
|
460
|
+
return relative_minimum * size.fixed_size, relative_maximum * size.fixed_size
|
|
461
|
+
return relative_minimum * size.minimum_size, relative_maximum * size.maximum_size
|
|
464
462
|
|
|
465
463
|
@property
|
|
466
|
-
def flow_rate_lower_bound_relative(self) ->
|
|
464
|
+
def flow_rate_lower_bound_relative(self) -> NumericData:
|
|
467
465
|
"""Returns the lower bound of the flow_rate relative to its size"""
|
|
468
466
|
fixed_profile = self.element.fixed_relative_profile
|
|
469
467
|
if fixed_profile is None:
|
|
470
|
-
return
|
|
471
|
-
return
|
|
468
|
+
return self.element.relative_minimum.active_data
|
|
469
|
+
return fixed_profile.active_data
|
|
472
470
|
|
|
473
471
|
@property
|
|
474
|
-
def flow_rate_upper_bound_relative(self) ->
|
|
475
|
-
"""
|
|
472
|
+
def flow_rate_upper_bound_relative(self) -> NumericData:
|
|
473
|
+
"""Returns the upper bound of the flow_rate relative to its size"""
|
|
476
474
|
fixed_profile = self.element.fixed_relative_profile
|
|
477
475
|
if fixed_profile is None:
|
|
478
|
-
return
|
|
479
|
-
return
|
|
476
|
+
return self.element.relative_maximum.active_data
|
|
477
|
+
return fixed_profile.active_data
|
|
480
478
|
|
|
481
479
|
@property
|
|
482
|
-
def flow_rate_lower_bound(self) ->
|
|
480
|
+
def flow_rate_lower_bound(self) -> NumericData:
|
|
483
481
|
"""
|
|
484
482
|
Returns the minimum bound the flow_rate can reach.
|
|
485
483
|
Further constraining might be done in OnOffModel and InvestmentModel
|
|
@@ -489,18 +487,18 @@ class FlowModel(ElementModel):
|
|
|
489
487
|
if isinstance(self.element.size, InvestParameters):
|
|
490
488
|
if self.element.size.optional:
|
|
491
489
|
return 0
|
|
492
|
-
return self.flow_rate_lower_bound_relative *
|
|
493
|
-
return self.flow_rate_lower_bound_relative *
|
|
490
|
+
return self.flow_rate_lower_bound_relative * self.element.size.minimum_size
|
|
491
|
+
return self.flow_rate_lower_bound_relative * self.element.size
|
|
494
492
|
|
|
495
493
|
@property
|
|
496
|
-
def flow_rate_upper_bound(self) ->
|
|
494
|
+
def flow_rate_upper_bound(self) -> NumericData:
|
|
497
495
|
"""
|
|
498
496
|
Returns the maximum bound the flow_rate can reach.
|
|
499
497
|
Further constraining might be done in OnOffModel and InvestmentModel
|
|
500
498
|
"""
|
|
501
499
|
if isinstance(self.element.size, InvestParameters):
|
|
502
|
-
return self.flow_rate_upper_bound_relative *
|
|
503
|
-
return self.flow_rate_upper_bound_relative *
|
|
500
|
+
return self.flow_rate_upper_bound_relative * self.element.size.maximum_size
|
|
501
|
+
return self.flow_rate_upper_bound_relative * self.element.size
|
|
504
502
|
|
|
505
503
|
|
|
506
504
|
class BusModel(ElementModel):
|
|
@@ -521,18 +519,14 @@ class BusModel(ElementModel):
|
|
|
521
519
|
# Fehlerplus/-minus:
|
|
522
520
|
if self.element.with_excess:
|
|
523
521
|
excess_penalty = np.multiply(
|
|
524
|
-
self._model.hours_per_step, self.element.excess_penalty_per_flow_hour.
|
|
522
|
+
self._model.hours_per_step, self.element.excess_penalty_per_flow_hour.active_data
|
|
525
523
|
)
|
|
526
524
|
self.excess_input = self.add(
|
|
527
|
-
self._model.add_variables(
|
|
528
|
-
lower=0, coords=self._model.get_coords(), name=f'{self.label_full}|excess_input'
|
|
529
|
-
),
|
|
525
|
+
self._model.add_variables(lower=0, coords=self._model.coords, name=f'{self.label_full}|excess_input'),
|
|
530
526
|
'excess_input',
|
|
531
527
|
)
|
|
532
528
|
self.excess_output = self.add(
|
|
533
|
-
self._model.add_variables(
|
|
534
|
-
lower=0, coords=self._model.get_coords(), name=f'{self.label_full}|excess_output'
|
|
535
|
-
),
|
|
529
|
+
self._model.add_variables(lower=0, coords=self._model.coords, name=f'{self.label_full}|excess_output'),
|
|
536
530
|
'excess_output',
|
|
537
531
|
)
|
|
538
532
|
eq_bus_balance.lhs -= -self.excess_input + self.excess_output
|
|
@@ -547,8 +541,7 @@ class BusModel(ElementModel):
|
|
|
547
541
|
inputs.append(self.excess_input.name)
|
|
548
542
|
if self.excess_output is not None:
|
|
549
543
|
outputs.append(self.excess_output.name)
|
|
550
|
-
return {**super().results_structure(), 'inputs': inputs, 'outputs': outputs
|
|
551
|
-
'flows': [flow.label_full for flow in self.element.inputs + self.element.outputs]}
|
|
544
|
+
return {**super().results_structure(), 'inputs': inputs, 'outputs': outputs}
|
|
552
545
|
|
|
553
546
|
|
|
554
547
|
class ComponentModel(ElementModel):
|
|
@@ -601,5 +594,4 @@ class ComponentModel(ElementModel):
|
|
|
601
594
|
**super().results_structure(),
|
|
602
595
|
'inputs': [flow.model.flow_rate.name for flow in self.element.inputs],
|
|
603
596
|
'outputs': [flow.model.flow_rate.name for flow in self.element.outputs],
|
|
604
|
-
'flows': [flow.label_full for flow in self.element.inputs + self.element.outputs],
|
|
605
597
|
}
|