flixopt 1.0.12__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/__init__.py +32 -0
- flixOpt/aggregation.py +430 -0
- flixOpt/calculation.py +629 -0
- flixOpt/commons.py +48 -0
- flixOpt/components.py +614 -0
- flixOpt/config.py +258 -0
- flixOpt/config.yaml +10 -0
- flixOpt/core.py +182 -0
- flixOpt/effects.py +410 -0
- flixOpt/elements.py +489 -0
- flixOpt/features.py +942 -0
- flixOpt/flow_system.py +351 -0
- flixOpt/interface.py +203 -0
- flixOpt/linear_converters.py +325 -0
- flixOpt/math_modeling.py +1145 -0
- flixOpt/plotting.py +712 -0
- flixOpt/results.py +563 -0
- flixOpt/solvers.py +21 -0
- flixOpt/structure.py +733 -0
- flixOpt/utils.py +134 -0
- flixopt-1.0.12.dist-info/LICENSE +21 -0
- flixopt-1.0.12.dist-info/METADATA +174 -0
- flixopt-1.0.12.dist-info/RECORD +29 -0
- flixopt-1.0.12.dist-info/WHEEL +5 -0
- flixopt-1.0.12.dist-info/top_level.txt +3 -0
- pics/architecture_flixOpt.png +0 -0
- pics/flixOpt_plotting.jpg +0 -0
- pics/pics.pptx +0 -0
- tests/ressources/Zeitreihen2020.csv +35137 -0
flixOpt/effects.py
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the effects of the flixOpt framework.
|
|
3
|
+
Furthermore, it contains the EffectCollection, which is used to collect all effects of a system.
|
|
4
|
+
Different Datatypes are used to represent the effects with assigned values by the user,
|
|
5
|
+
which are then transformed into the internal data structure.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Dict, Literal, Optional, Union
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
from .core import Numeric, Numeric_TS, Skalar, TimeSeries
|
|
14
|
+
from .features import ShareAllocationModel
|
|
15
|
+
from .math_modeling import Equation, Variable
|
|
16
|
+
from .structure import Element, ElementModel, SystemModel, _create_time_series
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger('flixOpt')
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Effect(Element):
|
|
22
|
+
"""
|
|
23
|
+
Effect, i.g. costs, CO2 emissions, area, ...
|
|
24
|
+
Components, FLows, and so on can contribute to an Effect. One Effect is chosen as the Objective of the Optimization
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
label: str,
|
|
30
|
+
unit: str,
|
|
31
|
+
description: str,
|
|
32
|
+
meta_data: Optional[Dict] = None,
|
|
33
|
+
is_standard: bool = False,
|
|
34
|
+
is_objective: bool = False,
|
|
35
|
+
specific_share_to_other_effects_operation: 'EffectValues' = None,
|
|
36
|
+
specific_share_to_other_effects_invest: 'EffectValuesInvest' = None,
|
|
37
|
+
minimum_operation: Optional[Skalar] = None,
|
|
38
|
+
maximum_operation: Optional[Skalar] = None,
|
|
39
|
+
minimum_invest: Optional[Skalar] = None,
|
|
40
|
+
maximum_invest: Optional[Skalar] = None,
|
|
41
|
+
minimum_operation_per_hour: Optional[Numeric_TS] = None,
|
|
42
|
+
maximum_operation_per_hour: Optional[Numeric_TS] = None,
|
|
43
|
+
minimum_total: Optional[Skalar] = None,
|
|
44
|
+
maximum_total: Optional[Skalar] = None,
|
|
45
|
+
):
|
|
46
|
+
"""
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
label : str
|
|
50
|
+
name
|
|
51
|
+
unit : str
|
|
52
|
+
unit of effect, i.g. €, kg_CO2, kWh_primaryEnergy
|
|
53
|
+
description : str
|
|
54
|
+
long name
|
|
55
|
+
meta_data : Optional[Dict]
|
|
56
|
+
used to store more information about the element. Is not used internally, but saved in the results
|
|
57
|
+
is_standard : boolean, optional
|
|
58
|
+
true, if Standard-Effect (for direct input of value without effect (alternatively to dict)) , else false
|
|
59
|
+
is_objective : boolean, optional
|
|
60
|
+
true, if optimization target
|
|
61
|
+
specific_share_to_other_effects_operation : {effectType: TS, ...}, i.g. 180 €/t_CO2, input as {costs: 180}, optional
|
|
62
|
+
share to other effects (only operation)
|
|
63
|
+
specific_share_to_other_effects_invest : {effectType: TS, ...}, i.g. 180 €/t_CO2, input as {costs: 180}, optional
|
|
64
|
+
share to other effects (only invest).
|
|
65
|
+
minimum_operation : scalar, optional
|
|
66
|
+
minimal sum (only operation) of the effect
|
|
67
|
+
maximum_operation : scalar, optional
|
|
68
|
+
maximal sum (nur operation) of the effect.
|
|
69
|
+
minimum_operation_per_hour : scalar or TS
|
|
70
|
+
maximum value per hour (only operation) of effect (=sum of all effect-shares) for each timestep!
|
|
71
|
+
maximum_operation_per_hour : scalar or TS
|
|
72
|
+
minimum value per hour (only operation) of effect (=sum of all effect-shares) for each timestep!
|
|
73
|
+
minimum_invest : scalar, optional
|
|
74
|
+
minimal sum (only invest) of the effect
|
|
75
|
+
maximum_invest : scalar, optional
|
|
76
|
+
maximal sum (only invest) of the effect
|
|
77
|
+
minimum_total : sclalar, optional
|
|
78
|
+
min sum of effect (invest+operation).
|
|
79
|
+
maximum_total : scalar, optional
|
|
80
|
+
max sum of effect (invest+operation).
|
|
81
|
+
**kwargs : TYPE
|
|
82
|
+
DESCRIPTION.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
None.
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
super().__init__(label, meta_data=meta_data)
|
|
90
|
+
self.label = label
|
|
91
|
+
self.unit = unit
|
|
92
|
+
self.description = description
|
|
93
|
+
self.is_standard = is_standard
|
|
94
|
+
self.is_objective = is_objective
|
|
95
|
+
self.specific_share_to_other_effects_operation: Union[EffectValues, EffectTimeSeries] = (
|
|
96
|
+
specific_share_to_other_effects_operation or {}
|
|
97
|
+
)
|
|
98
|
+
self.specific_share_to_other_effects_invest: Union[EffectValuesInvest, EffectDictInvest] = (
|
|
99
|
+
specific_share_to_other_effects_invest or {}
|
|
100
|
+
)
|
|
101
|
+
self.minimum_operation = minimum_operation
|
|
102
|
+
self.maximum_operation = maximum_operation
|
|
103
|
+
self.minimum_operation_per_hour: Numeric_TS = minimum_operation_per_hour
|
|
104
|
+
self.maximum_operation_per_hour: Numeric_TS = maximum_operation_per_hour
|
|
105
|
+
self.minimum_invest = minimum_invest
|
|
106
|
+
self.maximum_invest = maximum_invest
|
|
107
|
+
self.minimum_total = minimum_total
|
|
108
|
+
self.maximum_total = maximum_total
|
|
109
|
+
|
|
110
|
+
self._plausibility_checks()
|
|
111
|
+
|
|
112
|
+
def _plausibility_checks(self) -> None:
|
|
113
|
+
# Check circular loops in effects: (Effekte fügen sich gegenseitig Shares hinzu):
|
|
114
|
+
# TODO: Improve checks!! Only most basic case covered...
|
|
115
|
+
|
|
116
|
+
def error_str(effect_label: str, share_ffect_label: str):
|
|
117
|
+
return (
|
|
118
|
+
f' {effect_label} -> has share in: {share_ffect_label}\n'
|
|
119
|
+
f' {share_ffect_label} -> has share in: {effect_label}'
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Effekt darf nicht selber als Share in seinen ShareEffekten auftauchen:
|
|
123
|
+
# operation:
|
|
124
|
+
for target_effect in self.specific_share_to_other_effects_operation.keys():
|
|
125
|
+
assert self not in target_effect.specific_share_to_other_effects_operation.keys(), (
|
|
126
|
+
f'Error: circular operation-shares \n{error_str(target_effect.label, target_effect.label)}'
|
|
127
|
+
)
|
|
128
|
+
# invest:
|
|
129
|
+
for target_effect in self.specific_share_to_other_effects_invest.keys():
|
|
130
|
+
assert self not in target_effect.specific_share_to_other_effects_invest.keys(), (
|
|
131
|
+
f'Error: circular invest-shares \n{error_str(target_effect.label, target_effect.label)}'
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
def transform_data(self):
|
|
135
|
+
self.minimum_operation_per_hour = _create_time_series(
|
|
136
|
+
'minimum_operation_per_hour', self.minimum_operation_per_hour, self
|
|
137
|
+
)
|
|
138
|
+
self.maximum_operation_per_hour = _create_time_series(
|
|
139
|
+
'maximum_operation_per_hour', self.maximum_operation_per_hour, self
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
self.specific_share_to_other_effects_operation = effect_values_to_time_series(
|
|
143
|
+
'specific_share_to_other_effects_operation', self.specific_share_to_other_effects_operation, self
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
def create_model(self) -> 'EffectModel':
|
|
147
|
+
self.model = EffectModel(self)
|
|
148
|
+
return self.model
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class EffectModel(ElementModel):
|
|
152
|
+
def __init__(self, element: Effect):
|
|
153
|
+
super().__init__(element)
|
|
154
|
+
self.element: Effect
|
|
155
|
+
self.invest = ShareAllocationModel(
|
|
156
|
+
self.element, 'invest', False, total_max=self.element.maximum_invest, total_min=self.element.minimum_invest
|
|
157
|
+
)
|
|
158
|
+
self.operation = ShareAllocationModel(
|
|
159
|
+
self.element,
|
|
160
|
+
'operation',
|
|
161
|
+
True,
|
|
162
|
+
total_max=self.element.maximum_operation,
|
|
163
|
+
total_min=self.element.minimum_operation,
|
|
164
|
+
min_per_hour=self.element.minimum_operation_per_hour.active_data
|
|
165
|
+
if self.element.minimum_operation_per_hour is not None
|
|
166
|
+
else None,
|
|
167
|
+
max_per_hour=self.element.maximum_operation_per_hour.active_data
|
|
168
|
+
if self.element.maximum_operation_per_hour is not None
|
|
169
|
+
else None,
|
|
170
|
+
)
|
|
171
|
+
self.all = ShareAllocationModel(
|
|
172
|
+
self.element, 'all', False, total_max=self.element.maximum_total, total_min=self.element.minimum_total
|
|
173
|
+
)
|
|
174
|
+
self.sub_models.extend([self.invest, self.operation, self.all])
|
|
175
|
+
|
|
176
|
+
def do_modeling(self, system_model: SystemModel):
|
|
177
|
+
for model in self.sub_models:
|
|
178
|
+
model.do_modeling(system_model)
|
|
179
|
+
|
|
180
|
+
self.all.add_share(system_model, 'operation', self.operation.sum, 1)
|
|
181
|
+
self.all.add_share(system_model, 'invest', self.invest.sum, 1)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
EffectDict = Dict[Optional['Effect'], Numeric]
|
|
185
|
+
EffectDictInvest = Dict[Optional['Effect'], Skalar]
|
|
186
|
+
|
|
187
|
+
EffectValues = Optional[Union[Numeric_TS, EffectDict]] # Datatype for User Input
|
|
188
|
+
EffectValuesInvest = Optional[Union[Skalar, EffectDictInvest]] # Datatype for User Input
|
|
189
|
+
|
|
190
|
+
EffectTimeSeries = Dict[Optional['Effect'], TimeSeries] # Final Internal Data Structure
|
|
191
|
+
ElementTimeSeries = Dict[Optional[Element], TimeSeries] # Final Internal Data Structure
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def nested_values_to_time_series(
|
|
195
|
+
nested_values: Dict[Element, Numeric_TS], label_suffix: str, parent_element: Element
|
|
196
|
+
) -> ElementTimeSeries:
|
|
197
|
+
"""
|
|
198
|
+
Creates TimeSeries from nested values, which are a Dict of Elements to values.
|
|
199
|
+
The resulting label of the TimeSeries is the label of the parent_element, followed by the label of the element in
|
|
200
|
+
the nested_values and the label_suffix.
|
|
201
|
+
"""
|
|
202
|
+
return {
|
|
203
|
+
element: _create_time_series(f'{element.label}_{label_suffix}', value, parent_element)
|
|
204
|
+
for element, value in nested_values.items()
|
|
205
|
+
if element is not None
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def effect_values_to_time_series(
|
|
210
|
+
label_suffix: str, nested_values: EffectValues, parent_element: Element
|
|
211
|
+
) -> Optional[EffectTimeSeries]:
|
|
212
|
+
"""
|
|
213
|
+
Creates TimeSeries from EffectValues. The resulting label of the TimeSeries is the label of the parent_element,
|
|
214
|
+
followed by the label of the Effect in the nested_values and the label_suffix.
|
|
215
|
+
If the key in the EffectValues is None, the alias 'Standart_Effect' is used
|
|
216
|
+
"""
|
|
217
|
+
nested_values = as_effect_dict(nested_values)
|
|
218
|
+
if nested_values is None:
|
|
219
|
+
return None
|
|
220
|
+
else:
|
|
221
|
+
standard_value = nested_values.pop(None, None)
|
|
222
|
+
transformed_values = nested_values_to_time_series(nested_values, label_suffix, parent_element)
|
|
223
|
+
if standard_value is not None:
|
|
224
|
+
transformed_values[None] = _create_time_series(
|
|
225
|
+
f'Standard_Effect_{label_suffix}', standard_value, parent_element
|
|
226
|
+
)
|
|
227
|
+
return transformed_values
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def as_effect_dict(effect_values: EffectValues) -> Optional[EffectDict]:
|
|
231
|
+
"""
|
|
232
|
+
Converts effect values into a dictionary. If a scalar is provided, it is associated with a default effect type.
|
|
233
|
+
|
|
234
|
+
Examples
|
|
235
|
+
--------
|
|
236
|
+
costs = 20 -> {None: 20}
|
|
237
|
+
costs = None -> None
|
|
238
|
+
costs = {effect1: 20, effect2: 0.3} -> {effect1: 20, effect2: 0.3}
|
|
239
|
+
|
|
240
|
+
Parameters
|
|
241
|
+
----------
|
|
242
|
+
effect_values : None, int, float, TimeSeries, or dict
|
|
243
|
+
The effect values to convert, either a scalar, TimeSeries, or a dictionary.
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
dict or None
|
|
248
|
+
A dictionary with None or Effect as the key, or None if input is None.
|
|
249
|
+
"""
|
|
250
|
+
return (
|
|
251
|
+
effect_values
|
|
252
|
+
if isinstance(effect_values, dict)
|
|
253
|
+
else {None: effect_values}
|
|
254
|
+
if effect_values is not None
|
|
255
|
+
else None
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def effect_values_from_effect_time_series(effect_time_series: EffectTimeSeries) -> Dict[Optional[Effect], Numeric]:
|
|
260
|
+
return {effect: time_series.active_data for effect, time_series in effect_time_series.items()}
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class EffectCollection:
|
|
264
|
+
"""
|
|
265
|
+
Handling all Effects
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
def __init__(self, label: str):
|
|
269
|
+
self.label = label
|
|
270
|
+
self.model: Optional[EffectCollectionModel] = None
|
|
271
|
+
self.effects: Dict[str, Effect] = {}
|
|
272
|
+
|
|
273
|
+
def create_model(self, system_model: SystemModel) -> 'EffectCollectionModel':
|
|
274
|
+
self.model = EffectCollectionModel(self, system_model)
|
|
275
|
+
return self.model
|
|
276
|
+
|
|
277
|
+
def add_effect(self, effect: 'Effect') -> None:
|
|
278
|
+
if effect.is_standard and self.standard_effect is not None:
|
|
279
|
+
raise Exception(f'A standard-effect already exists! ({self.standard_effect.label=})')
|
|
280
|
+
if effect.is_objective and self.objective_effect is not None:
|
|
281
|
+
raise Exception(f'A objective-effect already exists! ({self.objective_effect.label=})')
|
|
282
|
+
if effect in self.effects.values():
|
|
283
|
+
raise Exception(f'Effect already added! ({effect.label=})')
|
|
284
|
+
if effect.label in self.effects:
|
|
285
|
+
raise Exception(f'Effect with label "{effect.label=}" already added!')
|
|
286
|
+
self.effects[effect.label] = effect
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def standard_effect(self) -> Optional[Effect]:
|
|
290
|
+
for effect in self.effects.values():
|
|
291
|
+
if effect.is_standard:
|
|
292
|
+
return effect
|
|
293
|
+
|
|
294
|
+
@property
|
|
295
|
+
def objective_effect(self) -> Optional[Effect]:
|
|
296
|
+
for effect in self.effects.values():
|
|
297
|
+
if effect.is_objective:
|
|
298
|
+
return effect
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def label_full(self):
|
|
302
|
+
return self.label
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class EffectCollectionModel(ElementModel):
|
|
306
|
+
# TODO: Maybe all EffectModels should be sub_models of this Model? Including Objective and Penalty?
|
|
307
|
+
def __init__(self, element: EffectCollection, system_model: SystemModel):
|
|
308
|
+
super().__init__(element)
|
|
309
|
+
self.element = element
|
|
310
|
+
self._system_model = system_model
|
|
311
|
+
self._effect_models: Dict[Effect, EffectModel] = {}
|
|
312
|
+
self.penalty: Optional[ShareAllocationModel] = None
|
|
313
|
+
self.objective: Optional[Equation] = None
|
|
314
|
+
|
|
315
|
+
def do_modeling(self, system_model: SystemModel):
|
|
316
|
+
self._effect_models = {effect: effect.create_model() for effect in self.element.effects.values()}
|
|
317
|
+
self.penalty = ShareAllocationModel(self.element, 'penalty', False)
|
|
318
|
+
self.sub_models.extend(list(self._effect_models.values()) + [self.penalty])
|
|
319
|
+
for model in self.sub_models:
|
|
320
|
+
model.do_modeling(system_model)
|
|
321
|
+
|
|
322
|
+
self.add_share_between_effects()
|
|
323
|
+
|
|
324
|
+
self.objective = Equation('OBJECTIVE', 'OBJECTIVE', is_objective=True)
|
|
325
|
+
self.objective.add_summand(self._objective_effect_model.operation.sum, 1)
|
|
326
|
+
self.objective.add_summand(self._objective_effect_model.invest.sum, 1)
|
|
327
|
+
self.objective.add_summand(self.penalty.sum, 1)
|
|
328
|
+
|
|
329
|
+
@property
|
|
330
|
+
def _objective_effect_model(self) -> EffectModel:
|
|
331
|
+
return self._effect_models[self.element.objective_effect]
|
|
332
|
+
|
|
333
|
+
def _add_share_to_effects(
|
|
334
|
+
self,
|
|
335
|
+
name: str,
|
|
336
|
+
element: Element,
|
|
337
|
+
target: Literal['operation', 'invest'],
|
|
338
|
+
effect_values: EffectDict,
|
|
339
|
+
factor: Numeric,
|
|
340
|
+
variable: Optional[Variable] = None,
|
|
341
|
+
) -> None:
|
|
342
|
+
# an alle Effects, die einen Wert haben, anhängen:
|
|
343
|
+
for effect, value in effect_values.items():
|
|
344
|
+
if effect is None: # Falls None, dann Standard-effekt nutzen:
|
|
345
|
+
effect = self.element.standard_effect
|
|
346
|
+
assert effect in self.element.effects.values(), f'Effect {effect.label} was used but not added to model!'
|
|
347
|
+
|
|
348
|
+
if target == 'operation':
|
|
349
|
+
model = self._effect_models[effect].operation
|
|
350
|
+
elif target == 'invest':
|
|
351
|
+
model = self._effect_models[effect].invest
|
|
352
|
+
else:
|
|
353
|
+
raise ValueError(f'Target {target} not supported!')
|
|
354
|
+
|
|
355
|
+
name_of_share = f'{element.label_full}__{name}'
|
|
356
|
+
total_factor = np.multiply(value, factor)
|
|
357
|
+
model.add_share(self._system_model, name_of_share, variable, total_factor)
|
|
358
|
+
|
|
359
|
+
def add_share_to_invest(
|
|
360
|
+
self,
|
|
361
|
+
name: str,
|
|
362
|
+
element: Element,
|
|
363
|
+
effect_values: EffectDictInvest,
|
|
364
|
+
factor: Numeric,
|
|
365
|
+
variable: Optional[Variable] = None,
|
|
366
|
+
) -> None:
|
|
367
|
+
# TODO: Add checks
|
|
368
|
+
self._add_share_to_effects(name, element, 'invest', effect_values, factor, variable)
|
|
369
|
+
|
|
370
|
+
def add_share_to_operation(
|
|
371
|
+
self,
|
|
372
|
+
name: str,
|
|
373
|
+
element: Element,
|
|
374
|
+
effect_values: EffectTimeSeries,
|
|
375
|
+
factor: Numeric,
|
|
376
|
+
variable: Optional[Variable] = None,
|
|
377
|
+
) -> None:
|
|
378
|
+
# TODO: Add checks
|
|
379
|
+
self._add_share_to_effects(
|
|
380
|
+
name, element, 'operation', effect_values_from_effect_time_series(effect_values), factor, variable
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
def add_share_to_penalty(
|
|
384
|
+
self,
|
|
385
|
+
name: Optional[str],
|
|
386
|
+
variable: Variable,
|
|
387
|
+
factor: Numeric,
|
|
388
|
+
) -> None:
|
|
389
|
+
assert variable is not None, 'A Variable must be passed to add a share to penalty! Else its a constant Penalty!'
|
|
390
|
+
self.penalty.add_share(self._system_model, name, variable, factor, True)
|
|
391
|
+
|
|
392
|
+
def add_share_between_effects(self):
|
|
393
|
+
for origin_effect in self.element.effects.values():
|
|
394
|
+
# 1. operation: -> hier sind es Zeitreihen (share_TS)
|
|
395
|
+
for target_effect, time_series in origin_effect.specific_share_to_other_effects_operation.items():
|
|
396
|
+
target_model = self._effect_models[target_effect].operation
|
|
397
|
+
origin_model = self._effect_models[origin_effect].operation
|
|
398
|
+
target_model.add_share(
|
|
399
|
+
self._system_model,
|
|
400
|
+
f'{origin_effect.label_full}_operation',
|
|
401
|
+
origin_model.sum_TS,
|
|
402
|
+
time_series.active_data,
|
|
403
|
+
)
|
|
404
|
+
# 2. invest: -> hier ist es Skalar (share)
|
|
405
|
+
for target_effect, factor in origin_effect.specific_share_to_other_effects_invest.items():
|
|
406
|
+
target_model = self._effect_models[target_effect].invest
|
|
407
|
+
origin_model = self._effect_models[origin_effect].invest
|
|
408
|
+
target_model.add_share(
|
|
409
|
+
self._system_model, f'{origin_effect.label_full}_invest', origin_model.sum, factor
|
|
410
|
+
)
|