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.

Files changed (48) hide show
  1. docs/examples/00-Minimal Example.md +1 -1
  2. docs/examples/01-Basic Example.md +1 -1
  3. docs/examples/02-Complex Example.md +1 -1
  4. docs/examples/index.md +1 -1
  5. docs/faq/contribute.md +26 -14
  6. docs/faq/index.md +1 -1
  7. docs/javascripts/mathjax.js +1 -1
  8. docs/user-guide/Mathematical Notation/Bus.md +1 -1
  9. docs/user-guide/Mathematical Notation/Effects, Penalty & Objective.md +13 -13
  10. docs/user-guide/Mathematical Notation/Flow.md +1 -1
  11. docs/user-guide/Mathematical Notation/LinearConverter.md +2 -2
  12. docs/user-guide/Mathematical Notation/Piecewise.md +1 -1
  13. docs/user-guide/Mathematical Notation/Storage.md +1 -1
  14. docs/user-guide/Mathematical Notation/index.md +1 -1
  15. docs/user-guide/Mathematical Notation/others.md +1 -1
  16. docs/user-guide/index.md +2 -2
  17. flixopt/__init__.py +5 -0
  18. flixopt/aggregation.py +0 -1
  19. flixopt/calculation.py +40 -72
  20. flixopt/commons.py +10 -1
  21. flixopt/components.py +326 -154
  22. flixopt/core.py +459 -966
  23. flixopt/effects.py +67 -270
  24. flixopt/elements.py +76 -84
  25. flixopt/features.py +172 -154
  26. flixopt/flow_system.py +70 -99
  27. flixopt/interface.py +315 -147
  28. flixopt/io.py +27 -56
  29. flixopt/linear_converters.py +3 -3
  30. flixopt/network_app.py +755 -0
  31. flixopt/plotting.py +16 -34
  32. flixopt/results.py +108 -806
  33. flixopt/structure.py +11 -67
  34. flixopt/utils.py +9 -6
  35. {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/METADATA +63 -42
  36. flixopt-2.2.0rc2.dist-info/RECORD +54 -0
  37. {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/WHEEL +1 -1
  38. scripts/extract_release_notes.py +45 -0
  39. docs/release-notes/_template.txt +0 -32
  40. docs/release-notes/index.md +0 -7
  41. docs/release-notes/v2.0.0.md +0 -93
  42. docs/release-notes/v2.0.1.md +0 -12
  43. docs/release-notes/v2.1.0.md +0 -31
  44. docs/release-notes/v2.2.0.md +0 -55
  45. docs/user-guide/Mathematical Notation/Investment.md +0 -115
  46. flixopt-2.2.0b0.dist-info/RECORD +0 -59
  47. {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/licenses/LICENSE +0 -0
  48. {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/top_level.txt +0 -0
@@ -2,4 +2,4 @@
2
2
 
3
3
  ```python
4
4
  {! ../examples/00_Minmal/minimal_example.py !}
5
- ```
5
+ ```
@@ -2,4 +2,4 @@
2
2
 
3
3
  ```python
4
4
  {! ../examples/01_Simple/simple_example.py !}
5
- ```
5
+ ```
@@ -7,4 +7,4 @@ This saves the results of a calculation to file and reloads them to analyze the
7
7
  ## Load the Results from file
8
8
  ```python
9
9
  {! ../examples/02_Complex/complex_example_results.py !}
10
- ```
10
+ ```
docs/examples/index.md CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  Here you can find a collection of examples that demonstrate how to use FlixOpt.
4
4
 
5
- We work on improving this gallery. If you have something to share, please contact us!
5
+ We work on improving this gallery. If you have something to share, please contact us!
docs/faq/contribute.md CHANGED
@@ -4,17 +4,29 @@ We warmly welcome contributions from the community! This guide will help you get
4
4
 
5
5
  ## Development Setup
6
6
  1. Clone the repository `git clone https://github.com/flixOpt/flixopt.git`
7
- 2. Install the development dependencies `pip install -editable .[dev, docs]`
8
- 3. Run `pytest` and `ruff check .` to ensure your code passes all tests
9
-
10
- ## Documentation
11
- FlixOpt uses [mkdocs](https://www.mkdocs.org/) to generate documentation. To preview the documentation locally, run `mkdocs serve` in the root directory.
12
-
13
- ## Helpful Commands
14
- - `mkdocs serve` to preview the documentation locally. Navigate to `http://127.0.0.1:8000/` to view the documentation.
15
- - `pytest` to run the test suite (You can also run the provided python script `run_all_test.py`)
16
- - `ruff check .` to run the linter
17
- - `ruff check . --fix` to automatically fix linting issues
7
+ 2. Install the development dependencies `pip install -e ".[dev]"`
8
+ 3. Install pre-commit hooks `pre-commit install` (one-time setup)
9
+ 4. Run `pytest` to ensure your code passes all tests
10
+
11
+ ## Code Quality
12
+ We use [Ruff](https://github.com/astral-sh/ruff) for linting and formatting. After the one-time setup above, **code quality checks run automatically on every commit**.
13
+
14
+ To run manually:
15
+ - `ruff check --fix .` to check and fix linting issues
16
+ - `ruff format .` to format code
17
+
18
+ ## Documentation (Optional)
19
+ FlixOpt uses [mkdocs](https://www.mkdocs.org/) to generate documentation.
20
+ To work on documentation:
21
+ ```bash
22
+ pip install -e ".[docs]"
23
+ mkdocs serve
24
+ ```
25
+ Then navigate to http://127.0.0.1:8000/
26
+
27
+ ## Testing
28
+ - `pytest` to run the test suite
29
+ - You can also run the provided python script `run_all_test.py`
18
30
 
19
31
  ---
20
32
  # Best practices
@@ -30,8 +42,8 @@ FlixOpt uses [mkdocs](https://www.mkdocs.org/) to generate documentation. To pre
30
42
  ## Branches
31
43
  As we start to think FlixOpt in **Releases**, we decided to introduce multiple **dev**-branches instead of only one:
32
44
  Following the **Semantic Versioning** guidelines, we introduced:
33
- - `next/patch`: This is where all pull requests for the next patch release (1.0.x) go.
34
- - `next/minor`: This is where all pull requests for the next minor release (1.x.0) go.
45
+ - `next/patch`: This is where all pull requests for the next patch release (1.0.x) go.
46
+ - `next/minor`: This is where all pull requests for the next minor release (1.x.0) go.
35
47
  - `next/major`: This is where all pull requests for the next major release (x.0.0) go.
36
48
 
37
49
  Everything else remains in `feature/...`-branches.
@@ -44,6 +56,6 @@ At some point, `next/minor` or `next/major` will get merged into `main` using a
44
56
  ## Releases
45
57
  As stated, we follow **Semantic Versioning**.
46
58
  Right after one of the 3 [release branches](#branches) is merged into main, a **Tag** should be added to the merge commit and pushed to the main branch. The tag has the form `v1.2.3`.
47
- With this tag, a release with **Release Notes** must be created.
59
+ With this tag, a release with **Release Notes** must be created.
48
60
 
49
61
  *This is our current best practice*
docs/faq/index.md CHANGED
@@ -1,3 +1,3 @@
1
1
  # Frequently Asked Questions
2
2
 
3
- ## Work in progress
3
+ ## Work in progress
@@ -15,4 +15,4 @@ document$.subscribe(() => {
15
15
  MathJax.typesetClear()
16
16
  MathJax.texReset()
17
17
  MathJax.typesetPromise()
18
- })
18
+ })
@@ -30,4 +30,4 @@ With:
30
30
  - $\phi_\text{in}(\text{t}_i)$ and $\phi_\text{out}(\text{t}_i)$ being the missing or excess flow-rate at time $\text{t}_i$, respectively
31
31
  - $\text{t}_i$ being the time step
32
32
  - $s_{b \rightarrow \Phi}(\text{t}_i)$ being the penalty term
33
- - $\text a_{b \rightarrow \Phi}(\text{t}_i)$ being the penalty coefficient (`excess_penalty_per_flow_hour`)
33
+ - $\text a_{b \rightarrow \Phi}(\text{t}_i)$ being the penalty coefficient (`excess_penalty_per_flow_hour`)
@@ -8,17 +8,17 @@ These arise from so called **Shares**, which originate from **Elements** like [F
8
8
  Assiziated effects could be:
9
9
  - costs - given in [€/kWh]...
10
10
  - ...or emissions - given in [kg/kWh].
11
- -
11
+ -
12
12
  Effects are allocated seperatly for investments and operation.
13
13
 
14
14
  ### Shares to Effects
15
15
 
16
16
  $$ \label{eq:Share_invest}
17
- s_{l \rightarrow e, \text{inv}} = \sum_{v \in \mathcal{V}_{l, \text{inv}}} v \cdot \text a_{v \rightarrow e}
17
+ s_{l \rightarrow e, \text{inv}} = \sum_{v \in \mathcal{V}_{l, \text{inv}}} v \cdot \text a_{v \rightarrow e}
18
18
  $$
19
19
 
20
20
  $$ \label{eq:Share_operation}
21
- s_{l \rightarrow e, \text{op}}(\text{t}_i) = \sum_{v \in \mathcal{V}_{l,\text{op}}} v(\text{t}_i) \cdot \text a_{v \rightarrow e}(\text{t}_i)
21
+ s_{l \rightarrow e, \text{op}}(\text{t}_i) = \sum_{v \in \mathcal{V}_{l,\text{op}}} v(\text{t}_i) \cdot \text a_{v \rightarrow e}(\text{t}_i)
22
22
  $$
23
23
 
24
24
  With:
@@ -36,26 +36,26 @@ With:
36
36
 
37
37
  ### Shares between different Effects
38
38
 
39
- Furthermore, the Effect $x$ can contribute a share to another Effect ${e} \in \mathcal{E}\backslash x$.
40
- This share is defined by the factor $\text r_{x \rightarrow e}$.
39
+ Furthermore, the Effect $x$ can contribute a share to another Effect ${e} \in \mathcal{E}\backslash x$.
40
+ This share is defined by the factor $\text r_{x \rightarrow e}$.
41
41
 
42
- For example, the Effect "CO$_2$ emissions" (unit: kg)
43
- can cause an additional share to Effect "monetary costs" (unit: €).
42
+ For example, the Effect "CO$_2$ emissions" (unit: kg)
43
+ can cause an additional share to Effect "monetary costs" (unit: €).
44
44
  In this case, the factor $\text a_{x \rightarrow e}$ is the specific CO$_2$ price in €/kg. However, circular references have to be avoided.
45
45
 
46
46
  The overall sum of investment shares of an Effect $e$ is given by $\eqref{Effect_invest}$
47
47
 
48
48
  $$ \label{eq:Effect_invest}
49
- E_{e, \text{inv}} =
50
- \sum_{l \in \mathcal{L}} s_{l \rightarrow e,\text{inv}} +
49
+ E_{e, \text{inv}} =
50
+ \sum_{l \in \mathcal{L}} s_{l \rightarrow e,\text{inv}} +
51
51
  \sum_{x \in \mathcal{E}\backslash e} E_{x, \text{inv}} \cdot \text{r}_{x \rightarrow e,\text{inv}}
52
52
  $$
53
53
 
54
54
  The overall sum of operation shares is given by $\eqref{eq:Effect_Operation}$
55
55
 
56
56
  $$ \label{eq:Effect_Operation}
57
- E_{e, \text{op}}(\text{t}_{i}) =
58
- \sum_{l \in \mathcal{L}} s_{l \rightarrow e, \text{op}}(\text{t}_i) +
57
+ E_{e, \text{op}}(\text{t}_{i}) =
58
+ \sum_{l \in \mathcal{L}} s_{l \rightarrow e, \text{op}}(\text{t}_i) +
59
59
  \sum_{x \in \mathcal{E}\backslash e} E_{x, \text{op}}(\text{t}_i) \cdot \text{r}_{x \rightarrow {e},\text{op}}(\text{t}_i)
60
60
  $$
61
61
 
@@ -100,7 +100,7 @@ $$
100
100
 
101
101
  Additionally to the user defined [Effects](#effects), a Penalty $\Phi$ is part of every FlixOpt Model.
102
102
  Its used to prevent unsolvable problems and simplify troubleshooting.
103
- Shares to the penalty can originate from every Element and are constructed similarly to
103
+ Shares to the penalty can originate from every Element and are constructed similarly to
104
104
  $\eqref{Share_invest}$ and $\eqref{Share_operation}$.
105
105
 
106
106
  $$ \label{eq:Penalty}
@@ -129,4 +129,4 @@ With:
129
129
 
130
130
  This approach allows for a multi-criteria optimization using both...
131
131
  - ... the **Weigted Sum**Method, as the chosen **Objective Effect** can incorporate other Effects.
132
- - ... the ($\epsilon$-constraint method) by constraining effects.
132
+ - ... the ($\epsilon$-constraint method) by constraining effects.
@@ -23,4 +23,4 @@ $$
23
23
 
24
24
  This mathematical Formulation can be extended or changed when using [OnOffParameters](#onoffparameters)
25
25
  to define the On/Off state of the Flow, or [InvestParameters](#investments),
26
- which changes the size of the Flow from a constant to an optimization variable.
26
+ which changes the size of the Flow from a constant to an optimization variable.
@@ -10,7 +10,7 @@ With:
10
10
  - $p_{f_\text{in}}(\text{t}_i)$ and $p_{f_\text{out}}(\text{t}_i)$ being the flow-rate at time $\text{t}_i$ for flow $f_\text{in}$ and $f_\text{out}$, respectively
11
11
  - $\text a_{f_\text{in}}(\text{t}_i)$ and $\text b_{f_\text{out}}(\text{t}_i)$ being the ratio of the flow-rate at time $\text{t}_i$ for flow $f_\text{in}$ and $f_\text{out}$, respectively
12
12
 
13
- With one incoming **Flow** and one outgoing **Flow**, this can be simplified to:
13
+ With one incoming **Flow** and one outgoing **Flow**, this can be simplified to:
14
14
 
15
15
  $$ \label{eq:Linear-Transformer-Ratio-simple}
16
16
  \text a(\text{t}_i) \cdot p_{f_\text{in}}(\text{t}_i) = p_{f_\text{out}}(\text{t}_i)
@@ -18,4 +18,4 @@ $$
18
18
 
19
19
  where $\text a$ can be interpreted as the conversion efficiency of the **LinearTransformer**.
20
20
  #### Piecewise Concersion factors
21
- The conversion efficiency can be defined as a piecewise linear approximation. See [Piecewise](Piecewise.md) for more details.
21
+ The conversion efficiency can be defined as a piecewise linear approximation. See [Piecewise](Piecewise.md) for more details.
@@ -40,7 +40,7 @@ Which can also be described as $v \in \{0\} \cup [\text{v}_{\text{start_k}}, \te
40
40
 
41
41
  ## Combining multiple Piecewises
42
42
 
43
- Piecewise allows representing non-linear relationships.
43
+ Piecewise allows representing non-linear relationships.
44
44
  This is a powerful technique in linear optimization to model non-linear behaviors while maintaining the problem's linearity.
45
45
 
46
46
  Therefore, each Piecewise must have the same number of Pieces $k$.
@@ -41,4 +41,4 @@ Where:
41
41
  - $p_{f_\text{in}}(\text{t}_i)$ is the input flow rate at time $\text{t}_i$
42
42
  - $\eta_\text{in}(\text{t}_i)$ is the charging efficiency at time $\text{t}_i$
43
43
  - $p_{f_\text{out}}(\text{t}_i)$ is the output flow rate at time $\text{t}_i$
44
- - $\eta_\text{out}(\text{t}_i)$ is the discharging efficiency at time $\text{t}_i$
44
+ - $\eta_\text{out}(\text{t}_i)$ is the discharging efficiency at time $\text{t}_i$
@@ -14,7 +14,7 @@ FlixOpt uses the following naming conventions:
14
14
 
15
15
  ## Timesteps
16
16
  Time steps are defined as a sequence of discrete time steps $\text{t}_i \in \mathcal{T} \quad \text{for} \quad i \in \{1, 2, \dots, \text{n}\}$ (left-aligned in its timespan).
17
- From this sequence, the corresponding time intervals $\Delta \text{t}_i \in \Delta \mathcal{T}$ are derived as
17
+ From this sequence, the corresponding time intervals $\Delta \text{t}_i \in \Delta \mathcal{T}$ are derived as
18
18
 
19
19
  $$\Delta \text{t}_i = \text{t}_{i+1} - \text{t}_i \quad \text{for} \quad i \in \{1, 2, \dots, \text{n}-1\}$$
20
20
 
@@ -1,3 +1,3 @@
1
1
  # Work in Progress
2
2
 
3
- This is a work in progress.
3
+ This is a work in progress.
docs/user-guide/index.md CHANGED
@@ -6,7 +6,7 @@ FlixOpt is built around a set of core concepts that work together to represent a
6
6
 
7
7
  ### FlowSystem
8
8
 
9
- The [`FlowSystem`][flixopt.flow_system.FlowSystem] is the central organizing unit in FlixOpt.
9
+ The [`FlowSystem`][flixopt.flow_system.FlowSystem] is the central organizing unit in FlixOpt.
10
10
  Every FlixOpt model starts with creating a FlowSystem. It:
11
11
 
12
12
  - Defines the timesteps for the optimization
@@ -40,7 +40,7 @@ Examples:
40
40
  [`Bus`][flixopt.elements.Bus] objects represent nodes or connection points in a FlowSystem. They:
41
41
 
42
42
  - Balance incoming and outgoing flows
43
- - Can represent physical networks like heat, electricity, or gas
43
+ - Can represent physical networks like heat, electricity, or gas
44
44
  - Handle infeasible balances gently by allowing the balance to be closed in return for a big Penalty (optional)
45
45
 
46
46
  ### Components
flixopt/__init__.py CHANGED
@@ -2,6 +2,10 @@
2
2
  This module bundles all common functionality of flixopt and sets up the logging
3
3
  """
4
4
 
5
+ from importlib.metadata import version
6
+
7
+ __version__ = version('flixopt')
8
+
5
9
  from .commons import (
6
10
  CONFIG,
7
11
  AggregatedCalculation,
@@ -18,6 +22,7 @@ from .commons import (
18
22
  Piecewise,
19
23
  PiecewiseConversion,
20
24
  PiecewiseEffects,
25
+ PiecewiseEffectsPerFlowHour,
21
26
  SegmentedCalculation,
22
27
  Sink,
23
28
  Source,
flixopt/aggregation.py CHANGED
@@ -34,7 +34,6 @@ from .structure import (
34
34
  if TYPE_CHECKING:
35
35
  import plotly.graph_objects as go
36
36
 
37
- warnings.filterwarnings('ignore', category=DeprecationWarning)
38
37
  logger = logging.getLogger('flixopt')
39
38
 
40
39
 
flixopt/calculation.py CHANGED
@@ -12,7 +12,6 @@ import logging
12
12
  import math
13
13
  import pathlib
14
14
  import timeit
15
- import warnings
16
15
  from typing import Any, Dict, List, Optional, Union
17
16
 
18
17
  import numpy as np
@@ -44,92 +43,74 @@ class Calculation:
44
43
  self,
45
44
  name: str,
46
45
  flow_system: FlowSystem,
47
- selected_timesteps: Optional[pd.DatetimeIndex] = None,
48
- selected_scenarios: Optional[pd.Index] = None,
49
- folder: Optional[pathlib.Path] = None,
50
46
  active_timesteps: Optional[pd.DatetimeIndex] = None,
47
+ folder: Optional[pathlib.Path] = None,
51
48
  ):
52
49
  """
53
50
  Args:
54
51
  name: name of calculation
55
52
  flow_system: flow_system which should be calculated
56
- selected_timesteps: timesteps which should be used for calculation. If None, then all timesteps are used.
57
- selected_scenarios: scenarios which should be used for calculation. If None, then all scenarios are used.
53
+ active_timesteps: list with indices, which should be used for calculation. If None, then all timesteps are used.
58
54
  folder: folder where results should be saved. If None, then the current working directory is used.
59
- active_timesteps: Deprecated. Use selected_timesteps instead.
60
55
  """
61
- if active_timesteps is not None:
62
- warnings.warn(
63
- 'active_timesteps is deprecated. Use selected_timesteps instead.',
64
- DeprecationWarning,
65
- stacklevel=2,
66
- )
67
- selected_timesteps = active_timesteps
68
56
  self.name = name
69
57
  self.flow_system = flow_system
70
58
  self.model: Optional[SystemModel] = None
71
- self.selected_timesteps = selected_timesteps
72
- self.selected_scenarios = selected_scenarios
59
+ self.active_timesteps = active_timesteps
73
60
 
74
61
  self.durations = {'modeling': 0.0, 'solving': 0.0, 'saving': 0.0}
75
62
  self.folder = pathlib.Path.cwd() / 'results' if folder is None else pathlib.Path(folder)
76
63
  self.results: Optional[CalculationResults] = None
77
64
 
78
- if not self.folder.exists():
79
- try:
80
- self.folder.mkdir(parents=False)
81
- except FileNotFoundError as e:
82
- raise FileNotFoundError(
83
- f'Folder {self.folder} and its parent do not exist. Please create them first.'
84
- ) from e
65
+ if self.folder.exists() and not self.folder.is_dir():
66
+ raise NotADirectoryError(f'Path {self.folder} exists and is not a directory.')
67
+ self.folder.mkdir(parents=False, exist_ok=True)
85
68
 
86
69
  @property
87
70
  def main_results(self) -> Dict[str, Union[Scalar, Dict]]:
88
71
  from flixopt.features import InvestmentModel
89
72
 
90
- main_results = {
73
+ return {
91
74
  'Objective': self.model.objective.value,
92
- 'Penalty': self.model.effects.penalty.total.solution.values,
75
+ 'Penalty': float(self.model.effects.penalty.total.solution.values),
93
76
  'Effects': {
94
77
  f'{effect.label} [{effect.unit}]': {
95
- 'operation': effect.model.operation.total.solution.values,
96
- 'invest': effect.model.invest.total.solution.values,
97
- 'total': effect.model.total.solution.values,
78
+ 'operation': float(effect.model.operation.total.solution.values),
79
+ 'invest': float(effect.model.invest.total.solution.values),
80
+ 'total': float(effect.model.total.solution.values),
98
81
  }
99
82
  for effect in self.flow_system.effects
100
83
  },
101
84
  'Invest-Decisions': {
102
85
  'Invested': {
103
- model.label_of_element: model.size.solution
86
+ model.label_of_element: float(model.size.solution)
104
87
  for component in self.flow_system.components.values()
105
88
  for model in component.model.all_sub_models
106
- if isinstance(model, InvestmentModel) and model.size.solution.max() >= CONFIG.modeling.EPSILON
89
+ if isinstance(model, InvestmentModel) and float(model.size.solution) >= CONFIG.modeling.EPSILON
107
90
  },
108
91
  'Not invested': {
109
- model.label_of_element: model.size.solution
92
+ model.label_of_element: float(model.size.solution)
110
93
  for component in self.flow_system.components.values()
111
94
  for model in component.model.all_sub_models
112
- if isinstance(model, InvestmentModel) and model.size.solution.max() < CONFIG.modeling.EPSILON
95
+ if isinstance(model, InvestmentModel) and float(model.size.solution) < CONFIG.modeling.EPSILON
113
96
  },
114
97
  },
115
98
  'Buses with excess': [
116
99
  {
117
100
  bus.label_full: {
118
- 'input': bus.model.excess_input.solution.sum('time'),
119
- 'output': bus.model.excess_output.solution.sum('time'),
101
+ 'input': float(np.sum(bus.model.excess_input.solution.values)),
102
+ 'output': float(np.sum(bus.model.excess_output.solution.values)),
120
103
  }
121
104
  }
122
105
  for bus in self.flow_system.buses.values()
123
106
  if bus.with_excess
124
107
  and (
125
- bus.model.excess_input.solution.sum() > 1e-3
126
- or bus.model.excess_output.solution.sum() > 1e-3
108
+ float(np.sum(bus.model.excess_input.solution.values)) > 1e-3
109
+ or float(np.sum(bus.model.excess_output.solution.values)) > 1e-3
127
110
  )
128
111
  ],
129
112
  }
130
113
 
131
- return utils.round_floats(main_results)
132
-
133
114
  @property
134
115
  def summary(self):
135
116
  return {
@@ -143,15 +124,6 @@ class Calculation:
143
124
  'Config': CONFIG.to_dict(),
144
125
  }
145
126
 
146
- @property
147
- def active_timesteps(self) -> pd.DatetimeIndex:
148
- warnings.warn(
149
- 'active_timesteps is deprecated. Use selected_timesteps instead.',
150
- DeprecationWarning,
151
- stacklevel=2,
152
- )
153
- return self.selected_timesteps
154
-
155
127
 
156
128
  class FullCalculation(Calculation):
157
129
  """
@@ -207,8 +179,8 @@ class FullCalculation(Calculation):
207
179
 
208
180
  def _activate_time_series(self):
209
181
  self.flow_system.transform_data()
210
- self.flow_system.time_series_collection.set_selection(
211
- timesteps=self.selected_timesteps, scenarios=self.selected_scenarios
182
+ self.flow_system.time_series_collection.activate_timesteps(
183
+ active_timesteps=self.active_timesteps,
212
184
  )
213
185
 
214
186
 
@@ -223,7 +195,7 @@ class AggregatedCalculation(FullCalculation):
223
195
  flow_system: FlowSystem,
224
196
  aggregation_parameters: AggregationParameters,
225
197
  components_to_clusterize: Optional[List[Component]] = None,
226
- selected_timesteps: Optional[pd.DatetimeIndex] = None,
198
+ active_timesteps: Optional[pd.DatetimeIndex] = None,
227
199
  folder: Optional[pathlib.Path] = None,
228
200
  ):
229
201
  """
@@ -237,13 +209,11 @@ class AggregatedCalculation(FullCalculation):
237
209
  components_to_clusterize: List of Components to perform aggregation on. If None, then all components are aggregated.
238
210
  This means, teh variables in the components are equalized to each other, according to the typical periods
239
211
  computed in the DataAggregation
240
- selected_timesteps: pd.DatetimeIndex or None
212
+ active_timesteps: pd.DatetimeIndex or None
241
213
  list with indices, which should be used for calculation. If None, then all timesteps are used.
242
214
  folder: folder where results should be saved. If None, then the current working directory is used.
243
215
  """
244
- if flow_system.time_series_collection.scenarios is not None:
245
- raise ValueError('Aggregation is not supported for scenarios yet. Please use FullCalculation instead.')
246
- super().__init__(name, flow_system, selected_timesteps, folder=folder)
216
+ super().__init__(name, flow_system, active_timesteps, folder=folder)
247
217
  self.aggregation_parameters = aggregation_parameters
248
218
  self.components_to_clusterize = components_to_clusterize
249
219
  self.aggregation = None
@@ -298,9 +268,9 @@ class AggregatedCalculation(FullCalculation):
298
268
 
299
269
  # Aggregation - creation of aggregated timeseries:
300
270
  self.aggregation = Aggregation(
301
- original_data=self.flow_system.time_series_collection.as_dataset(
302
- with_extra_timestep=False, with_constants=False
303
- ).to_dataframe(),
271
+ original_data=self.flow_system.time_series_collection.to_dataframe(
272
+ include_extra_timestep=False
273
+ ), # Exclude last row (NaN)
304
274
  hours_per_time_step=float(dt_min),
305
275
  hours_per_period=self.aggregation_parameters.hours_per_period,
306
276
  nr_of_periods=self.aggregation_parameters.nr_of_periods,
@@ -312,11 +282,9 @@ class AggregatedCalculation(FullCalculation):
312
282
  self.aggregation.cluster()
313
283
  self.aggregation.plot(show=True, save=self.folder / 'aggregation.html')
314
284
  if self.aggregation_parameters.aggregate_data_and_fix_non_binary_vars:
315
- for col in self.aggregation.aggregated_data.columns:
316
- data = self.aggregation.aggregated_data[col].values
317
- if col in self.flow_system.time_series_collection._has_extra_timestep:
318
- data = np.append(data, data[-1])
319
- self.flow_system.time_series_collection.update_time_series(col, data)
285
+ self.flow_system.time_series_collection.insert_new_data(
286
+ self.aggregation.aggregated_data, include_extra_timestep=False
287
+ )
320
288
  self.durations['aggregation'] = round(timeit.default_timer() - t_start_agg, 2)
321
289
 
322
290
 
@@ -355,13 +323,13 @@ class SegmentedCalculation(Calculation):
355
323
  self.nr_of_previous_values = nr_of_previous_values
356
324
  self.sub_calculations: List[FullCalculation] = []
357
325
 
358
- self.all_timesteps = self.flow_system.time_series_collection._full_timesteps
359
- self.all_timesteps_extra = self.flow_system.time_series_collection._full_timesteps_extra
326
+ self.all_timesteps = self.flow_system.time_series_collection.all_timesteps
327
+ self.all_timesteps_extra = self.flow_system.time_series_collection.all_timesteps_extra
360
328
 
361
329
  self.segment_names = [
362
330
  f'Segment_{i + 1}' for i in range(math.ceil(len(self.all_timesteps) / self.timesteps_per_segment))
363
331
  ]
364
- self.selected_timesteps_per_segment = self._calculate_timesteps_of_segment()
332
+ self.active_timesteps_per_segment = self._calculate_timesteps_of_segment()
365
333
 
366
334
  assert timesteps_per_segment > 2, 'The Segment length must be greater 2, due to unwanted internal side effects'
367
335
  assert self.timesteps_per_segment_with_overlap <= len(self.all_timesteps), (
@@ -387,7 +355,7 @@ class SegmentedCalculation(Calculation):
387
355
  logger.info(f'{" Segmented Solving ":#^80}')
388
356
 
389
357
  for i, (segment_name, timesteps_of_segment) in enumerate(
390
- zip(self.segment_names, self.selected_timesteps_per_segment, strict=False)
358
+ zip(self.segment_names, self.active_timesteps_per_segment, strict=False)
391
359
  ):
392
360
  if self.sub_calculations:
393
361
  self._transfer_start_values(i)
@@ -398,7 +366,7 @@ class SegmentedCalculation(Calculation):
398
366
  )
399
367
 
400
368
  calculation = FullCalculation(
401
- f'{self.name}-{segment_name}', self.flow_system, selected_timesteps=timesteps_of_segment
369
+ f'{self.name}-{segment_name}', self.flow_system, active_timesteps=timesteps_of_segment
402
370
  )
403
371
  self.sub_calculations.append(calculation)
404
372
  calculation.do_modeling()
@@ -432,9 +400,9 @@ class SegmentedCalculation(Calculation):
432
400
  This function gets the last values of the previous solved segment and
433
401
  inserts them as start values for the next segment
434
402
  """
435
- timesteps_of_prior_segment = self.selected_timesteps_per_segment[segment_index - 1]
403
+ timesteps_of_prior_segment = self.active_timesteps_per_segment[segment_index - 1]
436
404
 
437
- start = self.selected_timesteps_per_segment[segment_index][0]
405
+ start = self.active_timesteps_per_segment[segment_index][0]
438
406
  start_previous_values = timesteps_of_prior_segment[self.timesteps_per_segment - self.nr_of_previous_values]
439
407
  end_previous_values = timesteps_of_prior_segment[self.timesteps_per_segment - 1]
440
408
 
@@ -463,12 +431,12 @@ class SegmentedCalculation(Calculation):
463
431
  comp.initial_charge_state = self._original_start_values[comp.label_full]
464
432
 
465
433
  def _calculate_timesteps_of_segment(self) -> List[pd.DatetimeIndex]:
466
- selected_timesteps_per_segment = []
434
+ active_timesteps_per_segment = []
467
435
  for i, _ in enumerate(self.segment_names):
468
436
  start = self.timesteps_per_segment * i
469
437
  end = min(start + self.timesteps_per_segment_with_overlap, len(self.all_timesteps))
470
- selected_timesteps_per_segment.append(self.all_timesteps[start:end])
471
- return selected_timesteps_per_segment
438
+ active_timesteps_per_segment.append(self.all_timesteps[start:end])
439
+ return active_timesteps_per_segment
472
440
 
473
441
  @property
474
442
  def timesteps_per_segment_with_overlap(self):
flixopt/commons.py CHANGED
@@ -18,7 +18,15 @@ from .core import TimeSeriesData
18
18
  from .effects import Effect
19
19
  from .elements import Bus, Flow
20
20
  from .flow_system import FlowSystem
21
- from .interface import InvestParameters, OnOffParameters, Piece, Piecewise, PiecewiseConversion, PiecewiseEffects
21
+ from .interface import (
22
+ InvestParameters,
23
+ OnOffParameters,
24
+ Piece,
25
+ Piecewise,
26
+ PiecewiseConversion,
27
+ PiecewiseEffects,
28
+ PiecewiseEffectsPerFlowHour,
29
+ )
22
30
 
23
31
  __all__ = [
24
32
  'TimeSeriesData',
@@ -48,4 +56,5 @@ __all__ = [
48
56
  'results',
49
57
  'linear_converters',
50
58
  'solvers',
59
+ 'PiecewiseEffectsPerFlowHour',
51
60
  ]