flixopt 2.1.6__py3-none-any.whl → 2.1.8__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 +21 -21
- docs/user-guide/Mathematical Notation/Flow.md +3 -3
- docs/user-guide/Mathematical Notation/InvestParameters.md +3 -0
- docs/user-guide/Mathematical Notation/LinearConverter.md +5 -5
- docs/user-guide/Mathematical Notation/OnOffParameters.md +3 -0
- docs/user-guide/Mathematical Notation/Piecewise.md +1 -1
- docs/user-guide/Mathematical Notation/Storage.md +2 -2
- 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 +4 -0
- flixopt/aggregation.py +33 -32
- flixopt/calculation.py +161 -65
- flixopt/components.py +687 -154
- flixopt/config.py +17 -8
- flixopt/core.py +69 -60
- flixopt/effects.py +146 -64
- flixopt/elements.py +297 -110
- flixopt/features.py +78 -71
- flixopt/flow_system.py +72 -50
- flixopt/interface.py +952 -113
- flixopt/io.py +15 -10
- flixopt/linear_converters.py +373 -81
- flixopt/network_app.py +445 -266
- flixopt/plotting.py +215 -87
- flixopt/results.py +382 -209
- flixopt/solvers.py +25 -21
- flixopt/structure.py +41 -39
- flixopt/utils.py +10 -7
- {flixopt-2.1.6.dist-info → flixopt-2.1.8.dist-info}/METADATA +64 -53
- flixopt-2.1.8.dist-info/RECORD +56 -0
- scripts/extract_release_notes.py +5 -5
- scripts/gen_ref_pages.py +1 -1
- flixopt-2.1.6.dist-info/RECORD +0 -54
- {flixopt-2.1.6.dist-info → flixopt-2.1.8.dist-info}/WHEEL +0 -0
- {flixopt-2.1.6.dist-info → flixopt-2.1.8.dist-info}/licenses/LICENSE +0 -0
- {flixopt-2.1.6.dist-info → flixopt-2.1.8.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`)
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
## Effects
|
|
2
|
-
[`Effects`][flixopt.effects.Effect] are used to allocate things like costs, emissions, or other "effects"
|
|
2
|
+
[`Effects`][flixopt.effects.Effect] are used to allocate things like costs, emissions, or other "effects" occurring in the system.
|
|
3
3
|
These arise from so called **Shares**, which originate from **Elements** like [Flows](Flow.md).
|
|
4
4
|
|
|
5
5
|
**Example:**
|
|
6
6
|
|
|
7
7
|
[`Flows`][flixopt.elements.Flow] have an attribute called `effects_per_flow_hour`, defining the effect amount of per flow hour.
|
|
8
|
-
|
|
8
|
+
Associated effects could be:
|
|
9
9
|
- costs - given in [€/kWh]...
|
|
10
10
|
- ...or emissions - given in [kg/kWh].
|
|
11
|
-
-
|
|
12
|
-
Effects are allocated
|
|
11
|
+
-
|
|
12
|
+
Effects are allocated separately 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
|
-
The overall sum of investment shares of an Effect $e$ is given by $\eqref{Effect_invest}$
|
|
46
|
+
The overall sum of investment shares of an Effect $e$ is given by $\eqref{eq: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
|
|
|
@@ -68,8 +68,8 @@ With:
|
|
|
68
68
|
|
|
69
69
|
- $\mathcal{L}$ being the set of all elements in the FlowSystem
|
|
70
70
|
- $\mathcal{E}$ being the set of all effects in the FlowSystem
|
|
71
|
-
- $\text r_{x \rightarrow e, \text{inv}}$ being the factor between the
|
|
72
|
-
- $\text r_{x \rightarrow e, \text{op}}(\text{t}_i)$ being the factor between the
|
|
71
|
+
- $\text r_{x \rightarrow e, \text{inv}}$ being the factor between the invest part of Effect $x$ and Effect $e$
|
|
72
|
+
- $\text r_{x \rightarrow e, \text{op}}(\text{t}_i)$ being the factor between the operation part of Effect $x$ and Effect $e$
|
|
73
73
|
|
|
74
74
|
- $\text{t}_i$ being the time step
|
|
75
75
|
- $s_{l \rightarrow e, \text{inv}}$ being the share of element $l$ to the investment part of effect $e$
|
|
@@ -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}
|
|
@@ -113,7 +113,7 @@ With:
|
|
|
113
113
|
- $\mathcal{T}$ being the set of all timesteps
|
|
114
114
|
- $s_{l \rightarrow \Phi}$ being the share of element $l$ to the penalty
|
|
115
115
|
|
|
116
|
-
At the moment, penalties only occur in [Buses](
|
|
116
|
+
At the moment, penalties only occur in [Buses](Bus.md)
|
|
117
117
|
|
|
118
118
|
## Objective
|
|
119
119
|
|
|
@@ -128,5 +128,5 @@ With:
|
|
|
128
128
|
- $\Phi$ being the [Penalty](#penalty)
|
|
129
129
|
|
|
130
130
|
This approach allows for a multi-criteria optimization using both...
|
|
131
|
-
- ... the **
|
|
132
|
-
- ... the ($\epsilon$-constraint method) by constraining effects.
|
|
131
|
+
- ... the **Weighted Sum** method, as the chosen **Objective Effect** can incorporate other Effects.
|
|
132
|
+
- ... the ($\epsilon$-constraint method) by constraining effects.
|
|
@@ -21,6 +21,6 @@ $$
|
|
|
21
21
|
$$
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
This mathematical
|
|
25
|
-
to define the
|
|
26
|
-
|
|
24
|
+
This mathematical formulation can be extended by using [OnOffParameters](./OnOffParameters.md)
|
|
25
|
+
to define the on/off state of the Flow, or by using [InvestParameters](./InvestParameters.md)
|
|
26
|
+
to change the size of the Flow from a constant to an optimization variable.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[`LinearConverters`][flixopt.components.LinearConverter] define a ratio between incoming and outgoing [Flows](
|
|
1
|
+
[`LinearConverters`][flixopt.components.LinearConverter] define a ratio between incoming and outgoing [Flows](Flow.md).
|
|
2
2
|
|
|
3
3
|
$$ \label{eq:Linear-Transformer-Ratio}
|
|
4
4
|
\sum_{f_{\text{in}} \in \mathcal F_{in}} \text a_{f_{\text{in}}}(\text{t}_i) \cdot p_{f_\text{in}}(\text{t}_i) = \sum_{f_{\text{out}} \in \mathcal F_{out}} \text b_{f_\text{out}}(\text{t}_i) \cdot p_{f_\text{out}}(\text{t}_i)
|
|
@@ -10,12 +10,12 @@ 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)
|
|
17
17
|
$$
|
|
18
18
|
|
|
19
|
-
where $\text a$ can be interpreted as the conversion efficiency of the **
|
|
20
|
-
#### Piecewise
|
|
21
|
-
The conversion efficiency can be defined as a piecewise linear approximation. See [Piecewise](Piecewise.md) for more details.
|
|
19
|
+
where $\text a$ can be interpreted as the conversion efficiency of the **LinearConverter**.
|
|
20
|
+
#### Piecewise Conversion factors
|
|
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$.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Storages
|
|
2
|
-
**Storages** have one incoming and one outgoing **[Flow](
|
|
2
|
+
**Storages** have one incoming and one outgoing **[Flow](Flow.md)** with a charging and discharging efficiency.
|
|
3
3
|
A storage has a state of charge $c(\text{t}_i)$ which is limited by its `size` $\text C$ and relative bounds $\eqref{eq:Storage_Bounds}$.
|
|
4
4
|
|
|
5
5
|
$$ \label{eq:Storage_Bounds}
|
|
@@ -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
flixopt/aggregation.py
CHANGED
|
@@ -3,16 +3,15 @@ This module contains the Aggregation functionality for the flixopt framework.
|
|
|
3
3
|
Through this, aggregating TimeSeriesData is possible.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
6
8
|
import copy
|
|
7
9
|
import logging
|
|
8
10
|
import pathlib
|
|
9
11
|
import timeit
|
|
10
|
-
import
|
|
11
|
-
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Union
|
|
12
|
+
from typing import TYPE_CHECKING
|
|
12
13
|
|
|
13
|
-
import linopy
|
|
14
14
|
import numpy as np
|
|
15
|
-
import pandas as pd
|
|
16
15
|
|
|
17
16
|
try:
|
|
18
17
|
import tsam.timeseriesaggregation as tsam
|
|
@@ -22,18 +21,20 @@ except ImportError:
|
|
|
22
21
|
TSAM_AVAILABLE = False
|
|
23
22
|
|
|
24
23
|
from .components import Storage
|
|
25
|
-
from .core import Scalar, TimeSeriesData
|
|
26
|
-
from .elements import Component
|
|
27
|
-
from .flow_system import FlowSystem
|
|
28
24
|
from .structure import (
|
|
29
|
-
Element,
|
|
30
25
|
Model,
|
|
31
26
|
SystemModel,
|
|
32
27
|
)
|
|
33
28
|
|
|
34
29
|
if TYPE_CHECKING:
|
|
30
|
+
import linopy
|
|
31
|
+
import pandas as pd
|
|
35
32
|
import plotly.graph_objects as go
|
|
36
33
|
|
|
34
|
+
from .core import Scalar, TimeSeriesData
|
|
35
|
+
from .elements import Component
|
|
36
|
+
from .flow_system import FlowSystem
|
|
37
|
+
|
|
37
38
|
logger = logging.getLogger('flixopt')
|
|
38
39
|
|
|
39
40
|
|
|
@@ -48,9 +49,9 @@ class Aggregation:
|
|
|
48
49
|
hours_per_time_step: Scalar,
|
|
49
50
|
hours_per_period: Scalar,
|
|
50
51
|
nr_of_periods: int = 8,
|
|
51
|
-
weights:
|
|
52
|
-
time_series_for_high_peaks:
|
|
53
|
-
time_series_for_low_peaks:
|
|
52
|
+
weights: dict[str, float] | None = None,
|
|
53
|
+
time_series_for_high_peaks: list[str] | None = None,
|
|
54
|
+
time_series_for_low_peaks: list[str] | None = None,
|
|
54
55
|
):
|
|
55
56
|
"""
|
|
56
57
|
Args:
|
|
@@ -75,9 +76,9 @@ class Aggregation:
|
|
|
75
76
|
self.time_series_for_high_peaks = time_series_for_high_peaks or []
|
|
76
77
|
self.time_series_for_low_peaks = time_series_for_low_peaks or []
|
|
77
78
|
|
|
78
|
-
self.aggregated_data:
|
|
79
|
+
self.aggregated_data: pd.DataFrame | None = None
|
|
79
80
|
self.clustering_duration_seconds = None
|
|
80
|
-
self.tsam:
|
|
81
|
+
self.tsam: tsam.TimeSeriesAggregation | None = None
|
|
81
82
|
|
|
82
83
|
def cluster(self) -> None:
|
|
83
84
|
"""
|
|
@@ -140,7 +141,7 @@ class Aggregation:
|
|
|
140
141
|
def use_extreme_periods(self):
|
|
141
142
|
return self.time_series_for_high_peaks or self.time_series_for_low_peaks
|
|
142
143
|
|
|
143
|
-
def plot(self, colormap: str = 'viridis', show: bool = True, save:
|
|
144
|
+
def plot(self, colormap: str = 'viridis', show: bool = True, save: pathlib.Path | None = None) -> go.Figure:
|
|
144
145
|
from . import plotting
|
|
145
146
|
|
|
146
147
|
df_org = self.original_data.copy().rename(
|
|
@@ -162,14 +163,14 @@ class Aggregation:
|
|
|
162
163
|
figure_like=fig,
|
|
163
164
|
default_path=pathlib.Path('aggregated data.html'),
|
|
164
165
|
default_filetype='.html',
|
|
165
|
-
user_path=
|
|
166
|
+
user_path=save,
|
|
166
167
|
show=show,
|
|
167
|
-
save=
|
|
168
|
+
save=save is not None,
|
|
168
169
|
)
|
|
169
170
|
|
|
170
171
|
return fig
|
|
171
172
|
|
|
172
|
-
def get_cluster_indices(self) ->
|
|
173
|
+
def get_cluster_indices(self) -> dict[str, list[np.ndarray]]:
|
|
173
174
|
"""
|
|
174
175
|
Generates a dictionary that maps each cluster to a list of index vectors representing the time steps
|
|
175
176
|
assigned to that cluster for each period.
|
|
@@ -192,7 +193,7 @@ class Aggregation:
|
|
|
192
193
|
|
|
193
194
|
return index_vectors
|
|
194
195
|
|
|
195
|
-
def get_equation_indices(self, skip_first_index_of_period: bool = True) ->
|
|
196
|
+
def get_equation_indices(self, skip_first_index_of_period: bool = True) -> tuple[np.ndarray, np.ndarray]:
|
|
196
197
|
"""
|
|
197
198
|
Generates pairs of indices for the equations by comparing index vectors of the same cluster.
|
|
198
199
|
If `skip_first_index_of_period` is True, the first index of each period is skipped.
|
|
@@ -201,7 +202,7 @@ class Aggregation:
|
|
|
201
202
|
skip_first_index_of_period (bool): Whether to include or skip the first index of each period.
|
|
202
203
|
|
|
203
204
|
Returns:
|
|
204
|
-
|
|
205
|
+
tuple[np.ndarray, np.ndarray]: Two arrays of indices.
|
|
205
206
|
"""
|
|
206
207
|
idx_var1 = []
|
|
207
208
|
idx_var2 = []
|
|
@@ -237,8 +238,8 @@ class AggregationParameters:
|
|
|
237
238
|
aggregate_data_and_fix_non_binary_vars: bool,
|
|
238
239
|
percentage_of_period_freedom: float = 0,
|
|
239
240
|
penalty_of_period_freedom: float = 0,
|
|
240
|
-
time_series_for_high_peaks:
|
|
241
|
-
time_series_for_low_peaks:
|
|
241
|
+
time_series_for_high_peaks: list[TimeSeriesData] | None = None,
|
|
242
|
+
time_series_for_low_peaks: list[TimeSeriesData] | None = None,
|
|
242
243
|
):
|
|
243
244
|
"""
|
|
244
245
|
Initializes aggregation parameters for time series data
|
|
@@ -264,24 +265,24 @@ class AggregationParameters:
|
|
|
264
265
|
self.aggregate_data_and_fix_non_binary_vars = aggregate_data_and_fix_non_binary_vars
|
|
265
266
|
self.percentage_of_period_freedom = percentage_of_period_freedom
|
|
266
267
|
self.penalty_of_period_freedom = penalty_of_period_freedom
|
|
267
|
-
self.time_series_for_high_peaks:
|
|
268
|
-
self.time_series_for_low_peaks:
|
|
268
|
+
self.time_series_for_high_peaks: list[TimeSeriesData] = time_series_for_high_peaks or []
|
|
269
|
+
self.time_series_for_low_peaks: list[TimeSeriesData] = time_series_for_low_peaks or []
|
|
269
270
|
|
|
270
271
|
@property
|
|
271
272
|
def use_extreme_periods(self):
|
|
272
273
|
return self.time_series_for_high_peaks or self.time_series_for_low_peaks
|
|
273
274
|
|
|
274
275
|
@property
|
|
275
|
-
def labels_for_high_peaks(self) ->
|
|
276
|
+
def labels_for_high_peaks(self) -> list[str]:
|
|
276
277
|
return [ts.label for ts in self.time_series_for_high_peaks]
|
|
277
278
|
|
|
278
279
|
@property
|
|
279
|
-
def labels_for_low_peaks(self) ->
|
|
280
|
+
def labels_for_low_peaks(self) -> list[str]:
|
|
280
281
|
return [ts.label for ts in self.time_series_for_low_peaks]
|
|
281
282
|
|
|
282
283
|
@property
|
|
283
|
-
def use_low_peaks(self):
|
|
284
|
-
return self.time_series_for_low_peaks
|
|
284
|
+
def use_low_peaks(self) -> bool:
|
|
285
|
+
return bool(self.time_series_for_low_peaks)
|
|
285
286
|
|
|
286
287
|
|
|
287
288
|
class AggregationModel(Model):
|
|
@@ -295,7 +296,7 @@ class AggregationModel(Model):
|
|
|
295
296
|
aggregation_parameters: AggregationParameters,
|
|
296
297
|
flow_system: FlowSystem,
|
|
297
298
|
aggregation_data: Aggregation,
|
|
298
|
-
components_to_clusterize:
|
|
299
|
+
components_to_clusterize: list[Component] | None,
|
|
299
300
|
):
|
|
300
301
|
"""
|
|
301
302
|
Modeling-Element for "index-equating"-equations
|
|
@@ -314,9 +315,9 @@ class AggregationModel(Model):
|
|
|
314
315
|
|
|
315
316
|
indices = self.aggregation_data.get_equation_indices(skip_first_index_of_period=True)
|
|
316
317
|
|
|
317
|
-
time_variables:
|
|
318
|
-
binary_variables:
|
|
319
|
-
binary_time_variables:
|
|
318
|
+
time_variables: set[str] = {k for k, v in self._model.variables.data.items() if 'time' in v.indexes}
|
|
319
|
+
binary_variables: set[str] = {k for k, v in self._model.variables.data.items() if k in self._model.binaries}
|
|
320
|
+
binary_time_variables: set[str] = time_variables & binary_variables
|
|
320
321
|
|
|
321
322
|
for component in components:
|
|
322
323
|
if isinstance(component, Storage) and not self.aggregation_parameters.fix_storage_flows:
|
|
@@ -336,7 +337,7 @@ class AggregationModel(Model):
|
|
|
336
337
|
for variable in self.variables_direct.values():
|
|
337
338
|
self._model.effects.add_share_to_penalty('Aggregation', variable * penalty)
|
|
338
339
|
|
|
339
|
-
def _equate_indices(self, variable: linopy.Variable, indices:
|
|
340
|
+
def _equate_indices(self, variable: linopy.Variable, indices: tuple[np.ndarray, np.ndarray]) -> None:
|
|
340
341
|
assert len(indices[0]) == len(indices[1]), 'The length of the indices must match!!'
|
|
341
342
|
length = len(indices[0])
|
|
342
343
|
|