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/results.py DELETED
@@ -1,563 +0,0 @@
1
- """
2
- This module contains the Results functionality of the flixOpt framework.
3
- It provides high level functions to analyze the results of a calculation.
4
- It leverages the plotting.py module to plot the results.
5
- The results can also be analyzed without this module, as the results are stored in a widely supported format.
6
- """
7
-
8
- import datetime
9
- import json
10
- import logging
11
- import pathlib
12
- import timeit
13
- from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union
14
-
15
- import numpy as np
16
- import pandas as pd
17
- import plotly
18
- import yaml
19
-
20
- from flixOpt import plotting, utils
21
-
22
- if TYPE_CHECKING:
23
- import matplotlib.pyplot as plt
24
- import plotly.graph_objects as go
25
- import pyvis
26
-
27
- logger = logging.getLogger('flixOpt')
28
-
29
-
30
- class ElementResults:
31
- def __init__(self, infos: Dict, results: Dict):
32
- self.all_infos = infos
33
- self.all_results = results
34
- self.label = self.all_infos['label']
35
-
36
- def __repr__(self):
37
- return f'{self.__class__.__name__}({self.label})'
38
-
39
- @property
40
- def variables_flat(self) -> Dict[str, Union[int, float, np.ndarray]]:
41
- return flatten_dict(self.all_results)
42
-
43
-
44
- class CalculationResults:
45
- def __init__(self, calculation_name: str, folder: str) -> None:
46
- self.name = calculation_name
47
- self.folder = pathlib.Path(folder)
48
- self._path_infos = self.folder / f'{calculation_name}_infos.yaml'
49
- self._path_data = self.folder / f'{calculation_name}_data.json'
50
- self._path_results = self.folder / f'{calculation_name}_results.json'
51
-
52
- start_time = timeit.default_timer()
53
- with open(self._path_infos, 'rb') as f:
54
- self.calculation_infos: Dict = yaml.safe_load(f)
55
- logger.info(f'Loading Calculation Infos from .yaml took {(timeit.default_timer() - start_time):>8.2f} seconds')
56
-
57
- start_time = timeit.default_timer()
58
- with open(self._path_results, 'rb') as f:
59
- self.all_results: Dict = json.load(f)
60
- self.all_results = utils.convert_numeric_lists_to_arrays(self.all_results)
61
- logger.info(f'Loading results from .json took {(timeit.default_timer() - start_time):>8.2f} seconds')
62
-
63
- start_time = timeit.default_timer()
64
- with open(self._path_data, 'rb') as f:
65
- self.all_data: Dict = json.load(f)
66
- self.all_data = utils.convert_numeric_lists_to_arrays(self.all_data)
67
- logger.info(f'Loading data from .json took {(timeit.default_timer() - start_time):>8.2f} seconds')
68
-
69
- self.component_results: Dict[str, ComponentResults] = {}
70
- self.effect_results: Dict[str, EffectResults] = {}
71
- self.bus_results: Dict[str, BusResults] = {}
72
-
73
- self.time_with_end = np.array(
74
- [datetime.datetime.fromisoformat(date) for date in self.all_results['Time']]
75
- ).astype('datetime64')
76
- self.time = self.time_with_end[:-1]
77
- self.time_intervals_in_hours = np.array(self.all_results['Time intervals in hours'])
78
-
79
- self._construct_component_results()
80
- self._construct_bus_results()
81
- self._construct_effect_results()
82
-
83
- def _construct_component_results(self):
84
- comp_results = self.all_results['Components']
85
- comp_infos = self.all_data['Components']
86
- if not comp_results.keys() == comp_infos.keys():
87
- logger.warning(f'Missing Component or mismatched keys: {comp_results.keys() ^ comp_infos.keys()}')
88
-
89
- for key in comp_results.keys():
90
- infos, results = comp_infos.get(key, {}), comp_results.get(key, {})
91
- res = ComponentResults(infos, results)
92
- self.component_results[res.label] = res
93
-
94
- def _construct_effect_results(self):
95
- effect_results = self.all_results['Effects']
96
- effect_infos = self.all_data['Effects']
97
- effect_infos['penalty'] = {'label': 'Penalty'}
98
- if not effect_results.keys() == effect_infos.keys():
99
- logger.warning(f'Missing Effect or mismatched keys: {effect_results.keys() ^ effect_infos.keys()}')
100
-
101
- for key in effect_results.keys():
102
- infos, results = effect_infos.get(key, {}), effect_results.get(key, {})
103
- res = EffectResults(infos, results)
104
- self.effect_results[res.label] = res
105
-
106
- def _construct_bus_results(self):
107
- """This has to be called after _construct_component_results(), as its using the Flows from the Components"""
108
- bus_results = self.all_results['Buses']
109
- bus_infos = self.all_data['Buses']
110
- if not bus_results.keys() == bus_infos.keys():
111
- logger.warning(f'Missing Bus or mismatched keys: {bus_results.keys() ^ bus_infos.keys()}')
112
-
113
- for bus_label in bus_results.keys():
114
- infos, results = bus_infos.get(bus_label, {}), bus_results.get(bus_label, {})
115
- inputs = [
116
- flow
117
- for flow in self.flow_results().values()
118
- if bus_label == flow.bus_label and not flow.is_input_in_component
119
- ]
120
- outputs = [
121
- flow
122
- for flow in self.flow_results().values()
123
- if bus_label == flow.bus_label and flow.is_input_in_component
124
- ]
125
- res = BusResults(infos, results, inputs, outputs)
126
- self.bus_results[res.label] = res
127
-
128
- def flow_results(self) -> Dict[str, 'FlowResults']:
129
- return {
130
- flow.label_full: flow for comp in self.component_results.values() for flow in comp.inputs + comp.outputs
131
- }
132
-
133
- def to_dataframe(
134
- self,
135
- label: str,
136
- variable_name: str = 'flow_rate',
137
- input_factor: Optional[Literal[1, -1]] = -1,
138
- output_factor: Optional[Literal[1, -1]] = 1,
139
- threshold: Optional[float] = 1e-5,
140
- with_last_time_step: bool = True,
141
- ) -> pd.DataFrame:
142
- """
143
- Convert results of a specified element to a DataFrame.
144
-
145
- Parameters
146
- ----------
147
- label : str
148
- The label of the element (Component, Bus, or Flow) to retrieve data for.
149
- variable_name : str, default='flow_rate'
150
- The name of the variable to extract from the element's data.
151
- input_factor : Optional[Literal[1, -1]], default=-1
152
- Factor to apply to input values.
153
- output_factor : Optional[Literal[1, -1]], default=1
154
- Factor to apply to output values.
155
- threshold : Optional[float], default=1e-5
156
- Minimum absolute value for data inclusion in the DataFrame.
157
- with_last_time_step : bool, default=True
158
- Whether to include the last time step in the DataFrame index.
159
-
160
- Returns
161
- -------
162
- pd.DataFrame
163
- A DataFrame containing the specified variable's data with a datetime index.
164
- Dataframe is empty (no index), if no values are left after filtering.
165
-
166
- Raises
167
- ------
168
- ValueError
169
- If no data is found for the specified variable.
170
- """
171
-
172
- comp_or_bus = {**self.component_results, **self.bus_results}.get(label, None)
173
- flow = self.flow_results().get(label, None)
174
-
175
- if comp_or_bus is not None and flow is not None:
176
- raise Exception(f'{label=} matches both a Flow and a Component/Bus. That is an internal Error!')
177
- elif comp_or_bus is not None:
178
- df = comp_or_bus.to_dataframe(variable_name, input_factor, output_factor)
179
- elif flow is not None:
180
- df = flow.to_dataframe(variable_name)
181
- else:
182
- raise ValueError(f'No Element found with {label=}')
183
-
184
- if threshold is not None:
185
- df = df.loc[:, ((df > threshold) | (df < -1 * threshold)).any()] # Check if any value exceeds the threshold
186
- if df.empty: # If no values are left, return an empty DataFrame
187
- return df
188
-
189
- if with_last_time_step:
190
- if len(df) == len(self.time):
191
- df.loc[len(df)] = df.iloc[-1]
192
- df.index = self.time_with_end
193
- elif len(df) == len(self.time_with_end):
194
- df.index = self.time_with_end
195
- else:
196
- df.index = self.time
197
-
198
- return df
199
-
200
- def plot_operation(
201
- self,
202
- label: str,
203
- mode: Literal['bar', 'line', 'area', 'heatmap'] = 'area',
204
- variable_name: str = 'flow_rate',
205
- heatmap_periods: Literal['YS', 'MS', 'W', 'D', 'h', '15min', 'min'] = 'D',
206
- heatmap_steps_per_period: Literal['W', 'D', 'h', '15min', 'min'] = 'h',
207
- colors: Union[str, List[str]] = 'viridis',
208
- engine: Literal['plotly', 'matplotlib'] = 'plotly',
209
- invert: bool = True,
210
- show: bool = True,
211
- save: bool = False,
212
- path: Union[str, pathlib.Path, Literal['auto']] = 'auto',
213
- ) -> Union['go.Figure', Tuple['plt.Figure', 'plt.Axes']]:
214
- """
215
- Plots the operation results for a specified Element using the chosen plotting engine and mode.
216
-
217
- Parameters
218
- ----------
219
- label : str
220
- The label of the element to plot (e.g., a component or bus).
221
- mode : {'bar', 'line', 'area', 'heatmap'}, default='area'
222
- The type of plot to generate.
223
- variable_name : str, default='flow_rate'
224
- The variable to plot from the element's data.
225
- heatmap_periods : {'YS', 'MS', 'W', 'D', 'h', '15min', 'min'}, default='D'
226
- The period for heatmap plotting.
227
- heatmap_steps_per_period : {'W', 'D', 'h', '15min', 'min'}, default='h'
228
- The steps per period for heatmap plotting.
229
- colors : str or List[str], default='viridis'
230
- The colors or colorscale to use for the plot.
231
- engine : {'plotly', 'matplotlib'}, default='plotly'
232
- The plotting engine to use.
233
- invert : bool, default=False
234
- Whether to invert the input and output factors.
235
- show : bool, default=True
236
- Whether to display the plot immediately. (This includes saving the plot to file when engine='plotly')
237
- save : bool, default=False
238
- Whether to save the plot to a file.
239
- path : Union[str, pathlib.Path, Literal['auto']], default='auto'
240
- The path to save the plot to. If 'auto', the plot is saved to an automatically named file.
241
-
242
- Returns
243
- -------
244
- Union[go.Figure, Tuple[plt.Figure, plt.Axes]]
245
- The generated plot object, either a Plotly figure or a Matplotlib figure and axes.
246
-
247
- Raises
248
- ------
249
- ValueError
250
- If an invalid engine or color configuration is provided for heatmap mode.
251
- """
252
-
253
- if mode == 'heatmap' and not np.all(self.time_intervals_in_hours == self.time_intervals_in_hours[0]):
254
- logger.warning(
255
- 'Heat map plotting with irregular time intervals in time series can lead to unwanted effects'
256
- )
257
- if mode == 'heatmap' and not isinstance(colors, str):
258
- raise ValueError(
259
- f'For a heatmap, you need to pass the colors as a valid name of a colormap, not {colors=}.'
260
- f'Try "Turbo", "Hot", or "Viridis" instead.'
261
- )
262
-
263
- title = f'{variable_name.replace("_", " ").title()} of {label}'
264
- if path == 'auto':
265
- file_suffix = 'html' if engine == 'plotly' else 'png'
266
- if mode == 'heatmap':
267
- path = self.folder / f'{title} ({mode} {heatmap_periods}-{heatmap_steps_per_period}).{file_suffix}'
268
- else:
269
- path = self.folder / f'{title} ({mode}).{file_suffix}'
270
-
271
- data = self.to_dataframe(
272
- label, variable_name, input_factor=-1 if not invert else 1, output_factor=1 if not invert else -1
273
- )
274
- if mode == 'heatmap':
275
- heatmap_data = plotting.heat_map_data_from_df(data, heatmap_periods, heatmap_steps_per_period, 'ffill')
276
-
277
- if engine == 'plotly':
278
- if mode == 'heatmap':
279
- return plotting.heat_map_plotly(
280
- heatmap_data, title=title, color_map=colors, show=show, save=save, path=path
281
- )
282
- else:
283
- return plotting.with_plotly(
284
- data=data, mode=mode, show=show, title=title, colors=colors, save=save, path=path
285
- )
286
-
287
- elif engine == 'matplotlib':
288
- if mode == 'heatmap':
289
- return plotting.heat_map_matplotlib(
290
- heatmap_data, color_map=colors, show=show, path=path if save else None
291
- )
292
- else:
293
- return plotting.with_matplotlib(
294
- data=data, mode=mode, colors=colors, show=show, path=path if save else None
295
- )
296
- else:
297
- raise ValueError(f'Unknown Engine: {engine=}')
298
-
299
- def plot_storage(
300
- self,
301
- label: str,
302
- variable_name: str = 'flow_rate',
303
- mode: Literal['bar', 'line', 'area'] = 'area',
304
- colors: Union[str, List[str]] = 'viridis',
305
- invert: bool = True,
306
- show: bool = True,
307
- save: bool = False,
308
- path: Union[str, pathlib.Path, Literal['auto']] = 'auto',
309
- ):
310
- """
311
- Plots the storage operation results for a specified Storage Element, including its charge state.
312
-
313
- Parameters
314
- ----------
315
- label : str
316
- The label of the Storage to plot
317
- variable_name : str, default='flow_rate'
318
- The variable to plot from the element's data.
319
- mode : {'bar', 'line', 'area'}, default='area'
320
- The type of plot to generate.
321
- colors : str or List[str], default='viridis'
322
- The colors or colorscale to use for the plot.
323
- invert : bool, default=True
324
- Whether to invert the input and output factors.
325
- show : bool, default=True
326
- Whether to display the plot immediately. (This includes saving the plot to file when engine='plotly')
327
- save : bool, default=False
328
- Whether to save the plot to a file.
329
- path : Union[str, pathlib.Path, Literal['auto']], default='auto'
330
- The path to save the plot to. If 'auto', the plot is saved to an automatically named file.
331
-
332
- Returns
333
- -------
334
- plotly.graph_objs.Figure
335
- The generated Plotly figure object with the storage operation plot.
336
- """
337
- fig = self.plot_operation(
338
- label, mode, variable_name, invert=invert, engine='plotly', show=False, colors=colors, save=False
339
- )
340
- fig.add_trace(
341
- plotly.graph_objs.Scatter(
342
- x=self.time_with_end,
343
- y={**self.component_results, **self.bus_results}[label].variables['charge_state'],
344
- mode='lines',
345
- name='Charge State',
346
- )
347
- )
348
-
349
- title = f'{variable_name.replace("_", " ").title()} and Charge State of {label}'
350
- fig.update_layout(title=title)
351
-
352
- if path == 'auto':
353
- path = self.folder / f'{title} ({mode}).html'
354
- path = path.as_posix()
355
- if show:
356
- plotly.offline.plot(fig, filename=path)
357
- elif save: # If show, the file is saved anyway
358
- fig.write_html(path)
359
-
360
- return fig
361
-
362
- def visualize_network(
363
- self,
364
- path: Union[bool, str, pathlib.Path] = 'results/network.html',
365
- controls: Union[
366
- bool,
367
- List[
368
- Literal['nodes', 'edges', 'layout', 'interaction', 'manipulation', 'physics', 'selection', 'renderer']
369
- ],
370
- ] = True,
371
- show: bool = True,
372
- ) -> Optional['pyvis.network.Network']:
373
- """
374
- Visualizes the network structure of a FlowSystem using PyVis, saving it as an interactive HTML file.
375
-
376
- Parameters
377
- ----------
378
- path : Union[bool, str, pathlib.Path], default='results/network.html'
379
- Path to save the HTML visualization. If False, the visualization is created but not saved.
380
- controls : Union[bool, List[str]], default=True
381
- UI controls to add to the visualization. True enables all available controls, or specify a list of controls.
382
- show : bool, default=True
383
- Whether to open the visualization in the web browser.
384
-
385
- Returns
386
- -------
387
- Optional[pyvis.network.Network]
388
- The Network instance representing the visualization, or None if pyvis is not installed.
389
-
390
- Notes
391
- -----
392
- This function requires pyvis. If not installed, the function prints a warning and returns None.
393
- Nodes are styled based on type (e.g., circles for buses, boxes for components) and annotated with node information.
394
- """
395
- from . import plotting
396
-
397
- return plotting.visualize_network(
398
- self.calculation_infos['Network']['Nodes'], self.calculation_infos['Network']['Edges'], path, controls, show
399
- )
400
-
401
-
402
- class FlowResults(ElementResults):
403
- def __init__(self, infos: Dict, results: Dict, label_of_component: str) -> None:
404
- super().__init__(infos, results)
405
- self.is_input_in_component = self.all_infos['is_input_in_component']
406
- self.component_label = label_of_component
407
- self.bus_label = self.all_infos['bus']['label']
408
- self.label_full = f'{label_of_component}__{self.label}'
409
- self.variables = self.all_results
410
-
411
- def to_dataframe(self, variable_name: str = 'flow_rate') -> pd.DataFrame:
412
- return pd.DataFrame({variable_name: self.variables[variable_name]})
413
-
414
-
415
- class ComponentResults(ElementResults):
416
- def __init__(self, infos: Dict, results: Dict):
417
- super().__init__(infos, results)
418
- inputs, outputs = self._create_flow_results()
419
- self.inputs: List[FlowResults] = inputs
420
- self.outputs: List[FlowResults] = outputs
421
- self.variables = {key: val for key, val in self.all_results.items() if key not in self.inputs + self.outputs}
422
-
423
- def _create_flow_results(self) -> Tuple[List[FlowResults], List[FlowResults]]:
424
- flow_infos = {flow['label']: flow for flow in self.all_infos['inputs'] + self.all_infos['outputs']}
425
- flow_results = {flow_info['label']: self.all_results[flow_info['label']] for flow_info in flow_infos.values()}
426
- flows = [
427
- FlowResults(flow_info, flow_result, self.label)
428
- for flow_info, flow_result in zip(flow_infos.values(), flow_results.values(), strict=False)
429
- ]
430
- inputs = [flow for flow in flows if flow.is_input_in_component]
431
- outputs = [flow for flow in flows if not flow.is_input_in_component]
432
- return inputs, outputs
433
-
434
- def to_dataframe(
435
- self,
436
- variable_name: str = 'flow_rate',
437
- input_factor: Optional[Literal[1, -1]] = -1,
438
- output_factor: Optional[Literal[1, -1]] = 1,
439
- ) -> pd.DataFrame:
440
- inputs, outputs = {}, {}
441
- if input_factor is not None:
442
- inputs = {flow.label_full: (flow.variables[variable_name] * input_factor) for flow in self.inputs}
443
- if output_factor is not None:
444
- outputs = {flow.label_full: flow.variables[variable_name] * output_factor for flow in self.outputs}
445
-
446
- return pd.DataFrame(data={**inputs, **outputs})
447
-
448
-
449
- class BusResults(ElementResults):
450
- def __init__(self, infos: Dict, results: Dict, inputs: List[FlowResults], outputs: List[FlowResults]):
451
- super().__init__(infos, results)
452
- self.inputs = inputs
453
- self.outputs = outputs
454
- self.variables = {key: val for key, val in self.all_results.items() if key not in self.inputs + self.outputs}
455
-
456
- def to_dataframe(
457
- self,
458
- variable_name: str = 'flow_rate',
459
- input_factor: Optional[Literal[1, -1]] = -1,
460
- output_factor: Optional[Literal[1, -1]] = 1,
461
- ) -> pd.DataFrame:
462
- inputs, outputs = {}, {}
463
- if input_factor is not None:
464
- inputs = {flow.label_full: (flow.variables[variable_name] * input_factor) for flow in self.inputs}
465
- if 'excess_input' in self.variables:
466
- inputs['Excess Input'] = self.variables['excess_input'] * input_factor
467
- if output_factor is not None:
468
- outputs = {flow.label_full: flow.variables[variable_name] * output_factor for flow in self.outputs}
469
- if 'excess_output' in self.variables:
470
- outputs['Excess Output'] = self.variables['excess_output'] * output_factor
471
-
472
- return pd.DataFrame(data={**inputs, **outputs})
473
-
474
-
475
- class EffectResults(ElementResults):
476
- pass
477
-
478
-
479
- def extract_single_result(
480
- results_data: dict[str, Dict[str, Union[int, float, np.ndarray, dict]]], keys: List[str]
481
- ) -> Optional[Union[int, float, np.ndarray]]:
482
- """Goes through a nested dictionary with the given keys. Returns the value if found. Else returns None"""
483
- for key in keys:
484
- if isinstance(results_data, dict):
485
- results_data = results_data.get(key, None)
486
- else:
487
- return None
488
- return results_data
489
-
490
-
491
- def extract_results(
492
- results_data: dict[str, Dict[str, Union[int, float, np.ndarray, dict]]], keys: List[str], keep_none: bool = False
493
- ) -> Dict[str, Union[int, float, np.ndarray]]:
494
- """For each item in a dictionary, goes through its sub dictionaries.
495
- Returns the value if found. Else returns None. If specified, removes all None values
496
- """
497
- data = {kind: extract_single_result(results_data.get(kind, {}), keys) for kind in results_data.keys()}
498
- if keep_none:
499
- return data
500
- else:
501
- return {key: value for key, value in data.items() if value is not None}
502
-
503
-
504
- def flatten_dict(d, parent_key='', sep='__'):
505
- """
506
- Recursively flattens a nested dictionary.
507
-
508
- Parameters:
509
- d (dict): The dictionary to flatten.
510
- parent_key (str): The base key for the current recursion level.
511
- sep (str): The separator to use when concatenating keys.
512
-
513
- Returns:
514
- dict: A flattened dictionary.
515
- """
516
- items = []
517
- for k, v in d.items():
518
- new_key = f'{parent_key}{sep}{k}' if parent_key else k # Combine parent key and current key
519
- if isinstance(v, dict): # If the value is a nested dictionary, recurse
520
- items.extend(flatten_dict(v, new_key, sep=sep).items())
521
- else: # Otherwise, just add the key-value pair
522
- if new_key not in items:
523
- items.append((new_key, v))
524
- else:
525
- for i in range(100000):
526
- new_key = f'{new_key}_#{i}'
527
- if new_key not in items:
528
- items.append((new_key, v))
529
- break
530
- return dict(items)
531
-
532
-
533
- if __name__ == '__main__':
534
- results = CalculationResults(
535
- 'Sim1', '/Users/felix/Documents/Dokumente - eigene/Neuer Ordner/flixOpt-Fork/examples/Ex02_complex/results'
536
- )
537
-
538
- results.to_dataframe('Kessel')
539
- results.plot_flow_rate('Kessel__Q_fu', 'heatmap')
540
- plotting.heat_map_plotly(
541
- plotting.heat_map_data_from_df(
542
- pd.DataFrame(results.component_results['Speicher'].variables['charge_state'], index=results.time_with_end),
543
- periods='D',
544
- steps_per_period='15min',
545
- )
546
- )
547
-
548
- results.plot_operation('Fernwärme', 'area', engine='plotly')
549
- fig = results.plot_operation('Fernwärme', 'area', engine='plotly')
550
- fig = plotting.with_plotly(results.to_dataframe('Wärmelast'), 'line', fig=fig)
551
- import plotly.offline
552
-
553
- plotly.offline.plot(fig)
554
-
555
- extract_results(results.all_results['Components'], ['Q_th', 'flow_rate'])
556
- extract_single_result(results.all_results['Components'], ['Kessel', 'Q_th', 'flow_rate'])
557
-
558
- fig = plotting.with_plotly(
559
- pd.DataFrame(extract_results(results.all_results['Components'], ['OnOff', 'on']), index=results.time),
560
- mode='bar',
561
- )
562
- fig.update_layout(barmode='group', bargap=0.2, bargroupgap=0.1)
563
- plotly.offline.plot(fig)
flixOpt/solvers.py DELETED
@@ -1,21 +0,0 @@
1
- """
2
- This module contains the solvers of the flixOpt framework, making them available to the end user in a compact way.
3
- """
4
-
5
- from .math_modeling import (
6
- CbcSolver,
7
- CplexSolver,
8
- GlpkSolver,
9
- GurobiSolver,
10
- HighsSolver,
11
- Solver,
12
- )
13
-
14
- __all__ = [
15
- 'Solver',
16
- 'HighsSolver',
17
- 'GurobiSolver',
18
- 'CbcSolver',
19
- 'CplexSolver',
20
- 'GlpkSolver',
21
- ]