flixopt 1.0.12__py3-none-any.whl → 2.0.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.

Files changed (73) hide show
  1. docs/examples/00-Minimal Example.md +5 -0
  2. docs/examples/01-Basic Example.md +5 -0
  3. docs/examples/02-Complex Example.md +10 -0
  4. docs/examples/03-Calculation Modes.md +5 -0
  5. docs/examples/index.md +5 -0
  6. docs/faq/contribute.md +49 -0
  7. docs/faq/index.md +3 -0
  8. docs/images/architecture_flixOpt-pre2.0.0.png +0 -0
  9. docs/images/architecture_flixOpt.png +0 -0
  10. docs/images/flixopt-icon.svg +1 -0
  11. docs/javascripts/mathjax.js +18 -0
  12. docs/release-notes/_template.txt +32 -0
  13. docs/release-notes/index.md +7 -0
  14. docs/release-notes/v2.0.0.md +93 -0
  15. docs/release-notes/v2.0.1.md +12 -0
  16. docs/user-guide/Mathematical Notation/Bus.md +33 -0
  17. docs/user-guide/Mathematical Notation/Effects, Penalty & Objective.md +132 -0
  18. docs/user-guide/Mathematical Notation/Flow.md +26 -0
  19. docs/user-guide/Mathematical Notation/LinearConverter.md +21 -0
  20. docs/user-guide/Mathematical Notation/Piecewise.md +49 -0
  21. docs/user-guide/Mathematical Notation/Storage.md +44 -0
  22. docs/user-guide/Mathematical Notation/index.md +22 -0
  23. docs/user-guide/Mathematical Notation/others.md +3 -0
  24. docs/user-guide/index.md +124 -0
  25. {flixOpt → flixopt}/__init__.py +5 -2
  26. {flixOpt → flixopt}/aggregation.py +113 -140
  27. flixopt/calculation.py +455 -0
  28. {flixOpt → flixopt}/commons.py +7 -4
  29. flixopt/components.py +630 -0
  30. {flixOpt → flixopt}/config.py +9 -8
  31. {flixOpt → flixopt}/config.yaml +3 -3
  32. flixopt/core.py +970 -0
  33. flixopt/effects.py +386 -0
  34. flixopt/elements.py +534 -0
  35. flixopt/features.py +1042 -0
  36. flixopt/flow_system.py +409 -0
  37. flixopt/interface.py +265 -0
  38. flixopt/io.py +308 -0
  39. flixopt/linear_converters.py +331 -0
  40. flixopt/plotting.py +1340 -0
  41. flixopt/results.py +898 -0
  42. flixopt/solvers.py +77 -0
  43. flixopt/structure.py +630 -0
  44. flixopt/utils.py +62 -0
  45. flixopt-2.0.1.dist-info/METADATA +145 -0
  46. flixopt-2.0.1.dist-info/RECORD +57 -0
  47. {flixopt-1.0.12.dist-info → flixopt-2.0.1.dist-info}/WHEEL +1 -1
  48. flixopt-2.0.1.dist-info/top_level.txt +6 -0
  49. pics/architecture_flixOpt-pre2.0.0.png +0 -0
  50. pics/architecture_flixOpt.png +0 -0
  51. pics/flixopt-icon.svg +1 -0
  52. pics/pics.pptx +0 -0
  53. scripts/gen_ref_pages.py +54 -0
  54. site/release-notes/_template.txt +32 -0
  55. flixOpt/calculation.py +0 -629
  56. flixOpt/components.py +0 -614
  57. flixOpt/core.py +0 -182
  58. flixOpt/effects.py +0 -410
  59. flixOpt/elements.py +0 -489
  60. flixOpt/features.py +0 -942
  61. flixOpt/flow_system.py +0 -351
  62. flixOpt/interface.py +0 -203
  63. flixOpt/linear_converters.py +0 -325
  64. flixOpt/math_modeling.py +0 -1145
  65. flixOpt/plotting.py +0 -712
  66. flixOpt/results.py +0 -563
  67. flixOpt/solvers.py +0 -21
  68. flixOpt/structure.py +0 -733
  69. flixOpt/utils.py +0 -134
  70. flixopt-1.0.12.dist-info/METADATA +0 -174
  71. flixopt-1.0.12.dist-info/RECORD +0 -29
  72. flixopt-1.0.12.dist-info/top_level.txt +0 -3
  73. {flixopt-1.0.12.dist-info → flixopt-2.0.1.dist-info/licenses}/LICENSE +0 -0
flixOpt/flow_system.py DELETED
@@ -1,351 +0,0 @@
1
- """
2
- This module contains the FlowSystem class, which is used to collect instances of many other classes by the end User.
3
- """
4
-
5
- import json
6
- import logging
7
- import pathlib
8
- from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union
9
-
10
- import numpy as np
11
-
12
- from . import utils
13
- from .core import TimeSeries
14
- from .effects import Effect, EffectCollection
15
- from .elements import Bus, Component, Flow
16
- from .structure import Element, SystemModel, get_compact_representation, get_str_representation
17
-
18
- if TYPE_CHECKING:
19
- import pyvis
20
-
21
- logger = logging.getLogger('flixOpt')
22
-
23
-
24
- class FlowSystem:
25
- """
26
- A FlowSystem organizes the high level Elements (Components & Effects).
27
- """
28
-
29
- def __init__(
30
- self,
31
- time_series: np.ndarray[np.datetime64],
32
- last_time_step_hours: Optional[Union[int, float]] = None,
33
- previous_dt_in_hours: Optional[Union[int, float, np.ndarray]] = None,
34
- ):
35
- """
36
- Parameters
37
- ----------
38
- time_series : np.ndarray of datetime64
39
- timeseries of the data. Must be in datetime64 format. Don't use precisions below 'us'. !np.datetime64[ns]!
40
- last_time_step_hours :
41
- The duration of last time step.
42
- Storages needs this time-duration for calculation of charge state
43
- after last time step.
44
- If None, then last time increment of time_series is used.
45
- previous_dt_in_hours : Union[int, float, np.ndarray]
46
- The duration of previous time steps.
47
- If None, the first time increment of time_series is used.
48
- This is needed to calculate previous durations (for example consecutive_on_hours).
49
- If you use an array, take care that its long enough to cover all previous values!
50
- """
51
- self.time_series = time_series if isinstance(time_series, np.ndarray) else np.array(time_series)
52
- if self.time_series.dtype == np.dtype('datetime64[ns]'):
53
- self.time_series = self.time_series.astype('datetime64[us]')
54
-
55
- self.last_time_step_hours = (
56
- self.time_series[-1] - self.time_series[-2] if last_time_step_hours is None else last_time_step_hours
57
- )
58
- self.time_series_with_end = np.append(self.time_series, self.time_series[-1] + self.last_time_step_hours)
59
- self.previous_dt_in_hours: Union[int, float, np.ndarray] = (
60
- ((self.time_series[1] - self.time_series[0]) / np.timedelta64(1, 'h'))
61
- if previous_dt_in_hours is None
62
- else previous_dt_in_hours
63
- )
64
-
65
- utils.check_time_series('time series of FlowSystem', self.time_series_with_end)
66
-
67
- # defaults:
68
- self.components: Dict[str, Component] = {}
69
- self.effect_collection: EffectCollection = EffectCollection('Effects') # Organizes Effects, Penalty & Objective
70
- self.model: Optional[SystemModel] = None
71
-
72
- def add_effects(self, *args: Effect) -> None:
73
- for new_effect in list(args):
74
- logger.info(f'Registered new Effect: {new_effect.label}')
75
- self.effect_collection.add_effect(new_effect)
76
-
77
- def add_components(self, *args: Component) -> None:
78
- # Komponenten registrieren:
79
- new_components = list(args)
80
- for new_component in new_components:
81
- logger.info(f'Registered new Component: {new_component.label}')
82
- self._check_if_element_is_unique(new_component) # check if already exists:
83
- new_component.register_component_in_flows() # Komponente in Flow registrieren
84
- new_component.register_flows_in_bus() # Flows in Bus registrieren:
85
- self.components[new_component.label] = new_component # Add to existing components
86
-
87
- def add_elements(self, *args: Element) -> None:
88
- """
89
- add all modeling elements, like storages, boilers, heatpumps, buses, ...
90
-
91
- Parameters
92
- ----------
93
- *args : childs of Element like Boiler, HeatPump, Bus,...
94
- modeling Elements
95
-
96
- """
97
- for new_element in list(args):
98
- if isinstance(new_element, Component):
99
- self.add_components(new_element)
100
- elif isinstance(new_element, Effect):
101
- self.add_effects(new_element)
102
- else:
103
- raise Exception('argument is not instance of a modeling Element (Element)')
104
-
105
- def transform_data(self):
106
- for element in self.all_elements.values():
107
- element.transform_data()
108
-
109
- def network_infos(self) -> Tuple[Dict[str, Dict[str, str]], Dict[str, Dict[str, str]]]:
110
- nodes = {
111
- node.label_full: {
112
- 'label': node.label,
113
- 'class': 'Bus' if isinstance(node, Bus) else 'Component',
114
- 'infos': node.__str__(),
115
- }
116
- for node in list(self.components.values()) + list(self.buses.values())
117
- }
118
-
119
- edges = {
120
- flow.label_full: {
121
- 'label': flow.label,
122
- 'start': flow.bus.label_full if flow.is_input_in_comp else flow.comp.label_full,
123
- 'end': flow.comp.label_full if flow.is_input_in_comp else flow.bus.label_full,
124
- 'infos': flow.__str__(),
125
- }
126
- for flow in self.flows.values()
127
- }
128
-
129
- return nodes, edges
130
-
131
- def infos(self, use_numpy=True, use_element_label=False) -> Dict:
132
- infos = {
133
- 'Components': {
134
- comp.label: comp.infos(use_numpy, use_element_label)
135
- for comp in sorted(self.components.values(), key=lambda component: component.label.upper())
136
- },
137
- 'Buses': {
138
- bus.label: bus.infos(use_numpy, use_element_label)
139
- for bus in sorted(self.buses.values(), key=lambda bus: bus.label.upper())
140
- },
141
- 'Effects': {
142
- effect.label: effect.infos(use_numpy, use_element_label)
143
- for effect in sorted(self.effect_collection.effects.values(), key=lambda effect: effect.label.upper())
144
- },
145
- }
146
- return infos
147
-
148
- def to_json(self, path: Union[str, pathlib.Path]):
149
- """
150
- Saves the flow system to a json file.
151
- This not meant to be reloaded and recreate the object, but rather used to document or compare the object.
152
-
153
- Parameters:
154
- -----------
155
- path : Union[str, pathlib.Path]
156
- The path to the json file.
157
- """
158
- data = get_compact_representation(self.infos(use_numpy=True, use_element_label=True))
159
- with open(path, 'w', encoding='utf-8') as f:
160
- json.dump(data, f, indent=4, ensure_ascii=False)
161
-
162
- def visualize_network(
163
- self,
164
- path: Union[bool, str, pathlib.Path] = 'flow_system.html',
165
- controls: Union[
166
- bool,
167
- List[
168
- Literal['nodes', 'edges', 'layout', 'interaction', 'manipulation', 'physics', 'selection', 'renderer']
169
- ],
170
- ] = True,
171
- show: bool = True,
172
- ) -> Optional['pyvis.network.Network']:
173
- """
174
- Visualizes the network structure of a FlowSystem using PyVis, saving it as an interactive HTML file.
175
-
176
- Parameters:
177
- - path (Union[bool, str, pathlib.Path], default='flow_system.html'):
178
- Path to save the HTML visualization.
179
- - `False`: Visualization is created but not saved.
180
- - `str` or `Path`: Specifies file path (default: 'flow_system.html').
181
-
182
- - controls (Union[bool, List[str]], default=True):
183
- UI controls to add to the visualization.
184
- - `True`: Enables all available controls.
185
- - `List`: Specify controls, e.g., ['nodes', 'layout'].
186
- - Options: 'nodes', 'edges', 'layout', 'interaction', 'manipulation', 'physics', 'selection', 'renderer'.
187
-
188
- - show (bool, default=True):
189
- Whether to open the visualization in the web browser.
190
-
191
- Returns:
192
- - Optional[pyvis.network.Network]: The `Network` instance representing the visualization, or `None` if `pyvis` is not installed.
193
-
194
- Usage:
195
- - Visualize and open the network with default options:
196
- >>> self.visualize_network()
197
-
198
- - Save the visualization without opening:
199
- >>> self.visualize_network(show=False)
200
-
201
- - Visualize with custom controls and path:
202
- >>> self.visualize_network(path='output/custom_network.html', controls=['nodes', 'layout'])
203
-
204
- Notes:
205
- - This function requires `pyvis`. If not installed, the function prints a warning and returns `None`.
206
- - Nodes are styled based on type (e.g., circles for buses, boxes for components) and annotated with node information.
207
- """
208
- from . import plotting
209
-
210
- node_infos, edge_infos = self.network_infos()
211
- return plotting.visualize_network(node_infos, edge_infos, path, controls, show)
212
-
213
- def _check_if_element_is_unique(self, element: Element) -> None:
214
- """
215
- checks if element or label of element already exists in list
216
-
217
- Parameters
218
- ----------
219
- element : Element
220
- new element to check
221
- """
222
- if element in self.all_elements:
223
- raise Exception(f'Element {element.label} already added to FlowSystem!')
224
- # check if name is already used:
225
- if element.label_full in self.all_elements:
226
- raise Exception(f'Label of Element {element.label} already used in another element!')
227
-
228
- def get_time_data_from_indices(
229
- self, time_indices: Optional[Union[List[int], range]] = None
230
- ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.float64]:
231
- """
232
- Computes time series data based on the provided time indices.
233
-
234
- Args:
235
- time_indices: A list of indices or a range object indicating which time steps to extract.
236
- If None, the entire time series is used.
237
-
238
- Returns:
239
- A tuple containing:
240
- - Extracted time series
241
- - Time series with the "end time" appended
242
- - Differences between consecutive timestamps in hours
243
- - Total time in hours
244
- """
245
- # If time_indices is None, use the full time series range
246
- if time_indices is None:
247
- time_indices = range(len(self.time_series))
248
-
249
- # Extract the time series for the provided indices
250
- time_series = self.time_series[time_indices]
251
-
252
- # Ensure the next timestamp for end time is within bounds
253
- last_index = time_indices[-1]
254
- if last_index + 1 < len(self.time_series_with_end):
255
- end_time = self.time_series_with_end[last_index + 1]
256
- else:
257
- raise IndexError(f"Index {last_index + 1} out of bounds for 'self.time_series_with_end'.")
258
-
259
- # Append end time to the time series
260
- time_series_with_end = np.append(time_series, end_time)
261
-
262
- # Calculate time differences (time deltas) in hours
263
- time_deltas = time_series_with_end[1:] - time_series_with_end[:-1]
264
- dt_in_hours = time_deltas / np.timedelta64(1, 'h')
265
-
266
- # Calculate the total time in hours
267
- dt_in_hours_total = np.sum(dt_in_hours)
268
-
269
- return time_series, time_series_with_end, dt_in_hours, dt_in_hours_total
270
-
271
- def __repr__(self):
272
- return f'<{self.__class__.__name__} with {len(self.components)} components and {len(self.effect_collection.effects)} effects>'
273
-
274
- def __str__(self):
275
- return get_str_representation(self.infos(use_numpy=True, use_element_label=True))
276
-
277
- @property
278
- def flows(self) -> Dict[str, Flow]:
279
- set_of_flows = {flow for comp in self.components.values() for flow in comp.inputs + comp.outputs}
280
- return {flow.label_full: flow for flow in set_of_flows}
281
-
282
- @property
283
- def buses(self) -> Dict[str, Bus]:
284
- return {flow.bus.label: flow.bus for flow in self.flows.values()}
285
-
286
- @property
287
- def all_elements(self) -> Dict[str, Element]:
288
- return {**self.components, **self.effect_collection.effects, **self.flows, **self.buses}
289
-
290
- @property
291
- def all_time_series(self) -> List[TimeSeries]:
292
- return [ts for element in self.all_elements.values() for ts in element.used_time_series]
293
-
294
-
295
- def create_datetime_array(
296
- start: str, steps: Optional[int] = None, freq: str = '1h', end: Optional[str] = None
297
- ) -> np.ndarray[np.datetime64]:
298
- """
299
- Create a NumPy array with datetime64 values.
300
-
301
- Parameters
302
- ----------
303
- start : str
304
- Start date in 'YYYY-MM-DD' format or a full timestamp (e.g., 'YYYY-MM-DD HH:MM').
305
- steps : int, optional
306
- Number of steps in the datetime array. If `end` is provided, `steps` is ignored.
307
- freq : str, optional
308
- Frequency for the datetime64 array. Supports flexible intervals:
309
- - 'Y', 'M', 'W', 'D', 'h', 'm', 's' (e.g., '1h', '15m', '2h').
310
- Defaults to 'h' (hourly).
311
- end : str, optional
312
- End date in 'YYYY-MM-DD' format or a full timestamp (e.g., 'YYYY-MM-DD HH:MM').
313
- If provided, the function generates an array from `start` to `end` using `freq`.
314
-
315
- Returns
316
- -------
317
- np.ndarray
318
- NumPy array of datetime64 values.
319
-
320
- Examples
321
- --------
322
- Create an array with 15-minute intervals:
323
- >>> create_datetime_array('2023-01-01', steps=5, freq='15m')
324
- array(['2023-01-01T00:00', '2023-01-01T00:15', '2023-01-01T00:30', ...], dtype='datetime64[m]')
325
-
326
- Create 2-hour intervals:
327
- >>> create_datetime_array('2023-01-01T00', steps=4, freq='2h')
328
- array(['2023-01-01T00', '2023-01-01T02', '2023-01-01T04', ...], dtype='datetime64[h]')
329
-
330
- Generate minute intervals until a specified end time:
331
- >>> create_datetime_array('2023-01-01T00:00', end='2023-01-01T01:00', freq='m')
332
- array(['2023-01-01T00:00', '2023-01-01T00:01', ..., '2023-01-01T00:59'], dtype='datetime64[m]')
333
- """
334
- # Parse the frequency and interval
335
- unit = freq[-1] # Get the time unit (e.g., 'h', 'm', 's')
336
- interval = int(freq[:-1]) if freq[:-1].isdigit() else 1 # Default to interval=1 if not specified
337
- step_size = np.timedelta64(interval, unit) # Create the timedelta step size
338
-
339
- # Convert the start time to a datetime64 object
340
- start_dt = np.datetime64(start)
341
-
342
- # Generate the array based on the parameters
343
- if end: # If `end` is specified, create a range from start to end
344
- end_dt = np.datetime64(end)
345
- return np.arange(start_dt, end_dt, step_size)
346
-
347
- elif steps: # If `steps` is specified, create a range with the given number of steps
348
- return np.array([start_dt + i * step_size for i in range(steps)], dtype='datetime64')
349
-
350
- else: # If neither `steps` nor `end` is provided, raise an error
351
- raise ValueError('Either `steps` or `end` must be provided.')
flixOpt/interface.py DELETED
@@ -1,203 +0,0 @@
1
- """
2
- This module contains classes to collect Parameters for the Investment and OnOff decisions.
3
- These are tightly connected to features.py
4
- """
5
-
6
- import logging
7
- from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Union
8
-
9
- from .config import CONFIG
10
- from .core import Numeric, Numeric_TS, Skalar
11
- from .structure import Element, Interface
12
-
13
- if TYPE_CHECKING:
14
- from .effects import Effect, EffectTimeSeries, EffectValues, EffectValuesInvest
15
-
16
- logger = logging.getLogger('flixOpt')
17
-
18
-
19
- class InvestParameters(Interface):
20
- """
21
- collects arguments for invest-stuff
22
- """
23
-
24
- def __init__(
25
- self,
26
- fixed_size: Optional[Union[int, float]] = None,
27
- minimum_size: Union[int, float] = 0, # TODO: Use EPSILON?
28
- maximum_size: Optional[Union[int, float]] = None,
29
- optional: bool = True, # Investition ist weglassbar
30
- fix_effects: Union[Dict, int, float] = None,
31
- specific_effects: Union[Dict, int, float] = None, # costs per Flow-Unit/Storage-Size/...
32
- effects_in_segments: Optional[
33
- Tuple[List[Tuple[Skalar, Skalar]], Dict['Effect', List[Tuple[Skalar, Skalar]]]]
34
- ] = None,
35
- divest_effects: Union[Dict, int, float] = None,
36
- ):
37
- """
38
- Parameters
39
- ----------
40
- fix_effects : None or scalar, optional
41
- Fixed investment costs if invested.
42
- (Attention: Annualize costs to chosen period!)
43
- divest_effects : None or scalar, optional
44
- Fixed divestment costs (if not invested, e.g., demolition costs or contractual penalty).
45
- fixed_size : int, float, optional
46
- Determines if the investment size is fixed.
47
- optional : bool, optional
48
- If True, investment is not forced.
49
- specific_effects : scalar or Dict[Effect: Union[int, float, np.ndarray], optional
50
- Specific costs, e.g., in €/kW_nominal or €/m²_nominal.
51
- Example: {costs: 3, CO2: 0.3} with costs and CO2 representing an Object of class Effect
52
- (Attention: Annualize costs to chosen period!)
53
- effects_in_segments : list or List[ List[Union[int,float]], Dict[cEffecType: Union[List[Union[int,float]], optional
54
- Linear relation in segments [invest_segments, cost_segments].
55
- Example 1:
56
- [ [5, 25, 25, 100], # size in kW
57
- {costs: [50,250,250,800], # €
58
- PE: [5, 25, 25, 100] # kWh_PrimaryEnergy
59
- }
60
- ]
61
- Example 2 (if only standard-effect):
62
- [ [5, 25, 25, 100], # kW # size in kW
63
- [50,250,250,800] # value for standart effect, typically €
64
- ] # €
65
- (Attention: Annualize costs to chosen period!)
66
- (Args 'specific_effects' and 'fix_effects' can be used in parallel to InvestsizeSegments)
67
- minimum_size : scalar
68
- Min nominal value (only if: size_is_fixed = False).
69
- maximum_size : scalar, Optional
70
- Max nominal value (only if: size_is_fixed = False).
71
- """
72
- self.fix_effects: EffectValuesInvest = fix_effects or {}
73
- self.divest_effects: EffectValuesInvest = divest_effects or {}
74
- self.fixed_size = fixed_size
75
- self.optional = optional
76
- self.specific_effects: EffectValuesInvest = specific_effects or {}
77
- self.effects_in_segments = effects_in_segments
78
- self._minimum_size = minimum_size
79
- self._maximum_size = maximum_size or CONFIG.modeling.BIG # default maximum
80
-
81
- def transform_data(self):
82
- from .effects import as_effect_dict
83
-
84
- self.fix_effects = as_effect_dict(self.fix_effects)
85
- self.divest_effects = as_effect_dict(self.divest_effects)
86
- self.specific_effects = as_effect_dict(self.specific_effects)
87
-
88
- @property
89
- def minimum_size(self):
90
- return self.fixed_size or self._minimum_size
91
-
92
- @property
93
- def maximum_size(self):
94
- return self.fixed_size or self._maximum_size
95
-
96
-
97
- class OnOffParameters(Interface):
98
- def __init__(
99
- self,
100
- effects_per_switch_on: Union[Dict, Numeric] = None,
101
- effects_per_running_hour: Union[Dict, Numeric] = None,
102
- on_hours_total_min: Optional[int] = None,
103
- on_hours_total_max: Optional[int] = None,
104
- consecutive_on_hours_min: Optional[Numeric] = None,
105
- consecutive_on_hours_max: Optional[Numeric] = None,
106
- consecutive_off_hours_min: Optional[Numeric] = None,
107
- consecutive_off_hours_max: Optional[Numeric] = None,
108
- switch_on_total_max: Optional[int] = None,
109
- force_switch_on: bool = False,
110
- ):
111
- """
112
- on_off_parameters class for modeling on and off state of an Element.
113
- If no parameters are given, the default is to create a binary variable for the on state
114
- without further constraints or effects and a variable for the total on hours.
115
-
116
- Parameters
117
- ----------
118
- effects_per_switch_on : scalar, array, TimeSeriesData, optional
119
- cost of one switch from off (var_on=0) to on (var_on=1),
120
- unit i.g. in Euro
121
- effects_per_running_hour : scalar or TS, optional
122
- costs for operating, i.g. in € per hour
123
- on_hours_total_min : scalar, optional
124
- min. overall sum of operating hours.
125
- on_hours_total_max : scalar, optional
126
- max. overall sum of operating hours.
127
- consecutive_on_hours_min : scalar, optional
128
- min sum of operating hours in one piece
129
- (last on-time period of timeseries is not checked and can be shorter)
130
- consecutive_on_hours_max : scalar, optional
131
- max sum of operating hours in one piece
132
- consecutive_off_hours_min : scalar, optional
133
- min sum of non-operating hours in one piece
134
- (last off-time period of timeseries is not checked and can be shorter)
135
- consecutive_off_hours_max : scalar, optional
136
- max sum of non-operating hours in one piece
137
- switch_on_total_max : integer, optional
138
- max nr of switchOn operations
139
- force_switch_on : bool
140
- force creation of switch on variable, even if there is no switch_on_total_max
141
- """
142
- self.effects_per_switch_on: Union[EffectValues, EffectTimeSeries] = effects_per_switch_on or {}
143
- self.effects_per_running_hour: Union[EffectValues, EffectTimeSeries] = effects_per_running_hour or {}
144
- self.on_hours_total_min: Skalar = on_hours_total_min
145
- self.on_hours_total_max: Skalar = on_hours_total_max
146
- self.consecutive_on_hours_min: Numeric_TS = consecutive_on_hours_min
147
- self.consecutive_on_hours_max: Numeric_TS = consecutive_on_hours_max
148
- self.consecutive_off_hours_min: Numeric_TS = consecutive_off_hours_min
149
- self.consecutive_off_hours_max: Numeric_TS = consecutive_off_hours_max
150
- self.switch_on_total_max: Skalar = switch_on_total_max
151
- self.force_switch_on: bool = force_switch_on
152
-
153
- def transform_data(self, owner: 'Element'):
154
- from .effects import effect_values_to_time_series
155
- from .structure import _create_time_series
156
-
157
- self.effects_per_switch_on = effect_values_to_time_series('per_switch_on', self.effects_per_switch_on, owner)
158
- self.effects_per_running_hour = effect_values_to_time_series(
159
- 'per_running_hour', self.effects_per_running_hour, owner
160
- )
161
- self.consecutive_on_hours_min = _create_time_series(
162
- 'consecutive_on_hours_min', self.consecutive_on_hours_min, owner
163
- )
164
- self.consecutive_on_hours_max = _create_time_series(
165
- 'consecutive_on_hours_max', self.consecutive_on_hours_max, owner
166
- )
167
- self.consecutive_off_hours_min = _create_time_series(
168
- 'consecutive_off_hours_min', self.consecutive_off_hours_min, owner
169
- )
170
- self.consecutive_off_hours_max = _create_time_series(
171
- 'consecutive_off_hours_max', self.consecutive_off_hours_max, owner
172
- )
173
-
174
- @property
175
- def use_off(self) -> bool:
176
- """Determines wether the OFF Variable is needed or not"""
177
- return self.use_consecutive_off_hours
178
-
179
- @property
180
- def use_consecutive_on_hours(self) -> bool:
181
- """Determines wether a Variable for consecutive off hours is needed or not"""
182
- return any(param is not None for param in [self.consecutive_on_hours_min, self.consecutive_on_hours_max])
183
-
184
- @property
185
- def use_consecutive_off_hours(self) -> bool:
186
- """Determines wether a Variable for consecutive off hours is needed or not"""
187
- return any(param is not None for param in [self.consecutive_off_hours_min, self.consecutive_off_hours_max])
188
-
189
- @property
190
- def use_switch_on(self) -> bool:
191
- """Determines wether a Variable for SWITCH-ON is needed or not"""
192
- return (
193
- any(
194
- param not in (None, {})
195
- for param in [
196
- self.effects_per_switch_on,
197
- self.switch_on_total_max,
198
- self.on_hours_total_min,
199
- self.on_hours_total_max,
200
- ]
201
- )
202
- or self.force_switch_on
203
- )