flixopt 2.2.0b0__py3-none-any.whl → 2.2.0rc2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of flixopt might be problematic. Click here for more details.
- docs/examples/00-Minimal Example.md +1 -1
- docs/examples/01-Basic Example.md +1 -1
- docs/examples/02-Complex Example.md +1 -1
- docs/examples/index.md +1 -1
- docs/faq/contribute.md +26 -14
- docs/faq/index.md +1 -1
- docs/javascripts/mathjax.js +1 -1
- docs/user-guide/Mathematical Notation/Bus.md +1 -1
- docs/user-guide/Mathematical Notation/Effects, Penalty & Objective.md +13 -13
- docs/user-guide/Mathematical Notation/Flow.md +1 -1
- docs/user-guide/Mathematical Notation/LinearConverter.md +2 -2
- docs/user-guide/Mathematical Notation/Piecewise.md +1 -1
- docs/user-guide/Mathematical Notation/Storage.md +1 -1
- docs/user-guide/Mathematical Notation/index.md +1 -1
- docs/user-guide/Mathematical Notation/others.md +1 -1
- docs/user-guide/index.md +2 -2
- flixopt/__init__.py +5 -0
- flixopt/aggregation.py +0 -1
- flixopt/calculation.py +40 -72
- flixopt/commons.py +10 -1
- flixopt/components.py +326 -154
- flixopt/core.py +459 -966
- flixopt/effects.py +67 -270
- flixopt/elements.py +76 -84
- flixopt/features.py +172 -154
- flixopt/flow_system.py +70 -99
- flixopt/interface.py +315 -147
- flixopt/io.py +27 -56
- flixopt/linear_converters.py +3 -3
- flixopt/network_app.py +755 -0
- flixopt/plotting.py +16 -34
- flixopt/results.py +108 -806
- flixopt/structure.py +11 -67
- flixopt/utils.py +9 -6
- {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/METADATA +63 -42
- flixopt-2.2.0rc2.dist-info/RECORD +54 -0
- {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/WHEEL +1 -1
- scripts/extract_release_notes.py +45 -0
- docs/release-notes/_template.txt +0 -32
- docs/release-notes/index.md +0 -7
- docs/release-notes/v2.0.0.md +0 -93
- docs/release-notes/v2.0.1.md +0 -12
- docs/release-notes/v2.1.0.md +0 -31
- docs/release-notes/v2.2.0.md +0 -55
- docs/user-guide/Mathematical Notation/Investment.md +0 -115
- flixopt-2.2.0b0.dist-info/RECORD +0 -59
- {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/licenses/LICENSE +0 -0
- {flixopt-2.2.0b0.dist-info → flixopt-2.2.0rc2.dist-info}/top_level.txt +0 -0
docs/examples/index.md
CHANGED
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 -
|
|
8
|
-
3.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
- `
|
|
16
|
-
- `ruff
|
|
17
|
-
|
|
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
docs/javascripts/mathjax.js
CHANGED
|
@@ -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
|
|
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
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
|
-
|
|
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.
|
|
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.
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
119
|
-
'output': bus.model.excess_output.solution.
|
|
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.
|
|
126
|
-
or bus.model.excess_output.solution.
|
|
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.
|
|
211
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
302
|
-
|
|
303
|
-
)
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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.
|
|
359
|
-
self.all_timesteps_extra = self.flow_system.time_series_collection.
|
|
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.
|
|
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.
|
|
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,
|
|
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.
|
|
403
|
+
timesteps_of_prior_segment = self.active_timesteps_per_segment[segment_index - 1]
|
|
436
404
|
|
|
437
|
-
start = self.
|
|
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
|
-
|
|
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
|
-
|
|
471
|
-
return
|
|
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
|
|
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
|
]
|