flixopt 2.1.7__tar.gz → 2.1.9__tar.gz
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.
- {flixopt-2.1.7 → flixopt-2.1.9}/.github/workflows/python-app.yaml +90 -59
- {flixopt-2.1.7 → flixopt-2.1.9}/CHANGELOG.md +46 -5
- {flixopt-2.1.7/flixopt.egg-info → flixopt-2.1.9}/PKG-INFO +46 -42
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/SUMMARY.md +1 -1
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/getting-started.md +2 -2
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/user-guide/Mathematical Notation/Effects, Penalty & Objective.md +8 -8
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/user-guide/Mathematical Notation/Flow.md +3 -3
- flixopt-2.1.9/docs/user-guide/Mathematical Notation/InvestParameters.md +3 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/user-guide/Mathematical Notation/LinearConverter.md +3 -3
- flixopt-2.1.9/docs/user-guide/Mathematical Notation/OnOffParameters.md +3 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/user-guide/Mathematical Notation/Storage.md +1 -1
- {flixopt-2.1.7 → flixopt-2.1.9}/examples/01_Simple/simple_example.py +0 -1
- {flixopt-2.1.7 → flixopt-2.1.9}/examples/02_Complex/complex_example_results.py +0 -3
- {flixopt-2.1.7 → flixopt-2.1.9}/examples/03_Calculation_types/example_calculation_types.py +5 -8
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/aggregation.py +33 -32
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/calculation.py +158 -58
- flixopt-2.1.9/flixopt/components.py +1279 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/config.py +17 -8
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/core.py +59 -54
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/effects.py +144 -63
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/elements.py +292 -107
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/features.py +61 -58
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/flow_system.py +69 -48
- flixopt-2.1.9/flixopt/interface.py +1104 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/io.py +15 -10
- flixopt-2.1.9/flixopt/linear_converters.py +623 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/network_app.py +73 -39
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/plotting.py +215 -87
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/results.py +382 -209
- flixopt-2.1.9/flixopt/solvers.py +81 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/structure.py +41 -37
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/utils.py +10 -7
- {flixopt-2.1.7 → flixopt-2.1.9/flixopt.egg-info}/PKG-INFO +46 -42
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt.egg-info/SOURCES.txt +2 -0
- flixopt-2.1.9/flixopt.egg-info/requires.txt +60 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/mkdocs.yml +0 -1
- {flixopt-2.1.7 → flixopt-2.1.9}/pyproject.toml +71 -46
- {flixopt-2.1.7 → flixopt-2.1.9}/renovate.json +8 -1
- {flixopt-2.1.7 → flixopt-2.1.9}/scripts/gen_ref_pages.py +1 -1
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_bus.py +0 -5
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_component.py +0 -2
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_effect.py +0 -5
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_examples.py +1 -2
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_flow.py +0 -1
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_integration.py +0 -3
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_io.py +0 -2
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_linear_converter.py +0 -2
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_network_app.py +0 -5
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_on_hours_computation.py +2 -2
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_plots.py +4 -6
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_storage.py +0 -3
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_timeseries.py +1 -3
- flixopt-2.1.7/flixopt/components.py +0 -756
- flixopt-2.1.7/flixopt/interface.py +0 -265
- flixopt-2.1.7/flixopt/linear_converters.py +0 -331
- flixopt-2.1.7/flixopt/solvers.py +0 -77
- flixopt-2.1.7/flixopt.egg-info/requires.txt +0 -56
- {flixopt-2.1.7 → flixopt-2.1.9}/.github/CONTRIBUTING.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/.github/ISSUE_TEMPLATE/general_issue.yml +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/.github/pull_request_template.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/.gitignore +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/.pre-commit-config.yaml +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/LICENSE +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/README.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/contribute.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/examples/00-Minimal Example.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/examples/01-Basic Example.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/examples/02-Complex Example.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/examples/03-Calculation Modes.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/examples/index.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/faq/contribute.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/faq/index.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/images/architecture_flixOpt-pre2.0.0.png +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/images/architecture_flixOpt.png +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/images/flixopt-icon.svg +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/index.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/javascripts/mathjax.js +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/user-guide/Mathematical Notation/Bus.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/user-guide/Mathematical Notation/Piecewise.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/user-guide/Mathematical Notation/index.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/user-guide/Mathematical Notation/others.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/docs/user-guide/index.md +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/examples/00_Minmal/minimal_example.py +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/examples/02_Complex/complex_example.py +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/examples/03_Calculation_types/Zeitreihen2020.csv +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/__init__.py +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/commons.py +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt/config.yaml +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt.egg-info/dependency_links.txt +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/flixopt.egg-info/top_level.txt +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/pics/architecture_flixOpt-pre2.0.0.png +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/pics/architecture_flixOpt.png +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/pics/flixOpt_plotting.jpg +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/pics/flixopt-icon.svg +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/pics/pics.pptx +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/scripts/extract_release_notes.py +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/setup.cfg +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/__init__.py +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/conftest.py +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/ressources/Zeitreihen2020.csv +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/run_all_tests.py +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_dataconverter.py +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_functional.py +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/test_results_plots.py +0 -0
- {flixopt-2.1.7 → flixopt-2.1.9}/tests/todos.txt +0 -0
|
@@ -2,12 +2,15 @@ name: Python Package CI/CD
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
branches: [main
|
|
6
|
-
tags:
|
|
7
|
-
- 'v*.*.*' # Trigger on semantic version tags
|
|
5
|
+
branches: [main] # Only main branch
|
|
6
|
+
tags: ['v*.*.*']
|
|
8
7
|
pull_request:
|
|
9
|
-
branches: [main, dev
|
|
8
|
+
branches: [main, dev]
|
|
10
9
|
types: [opened, synchronize, reopened]
|
|
10
|
+
paths-ignore:
|
|
11
|
+
- 'docs/**'
|
|
12
|
+
- '*.md'
|
|
13
|
+
- 'README*'
|
|
11
14
|
workflow_dispatch: # Allow manual triggering
|
|
12
15
|
|
|
13
16
|
# Set permissions for security
|
|
@@ -24,39 +27,40 @@ env:
|
|
|
24
27
|
|
|
25
28
|
jobs:
|
|
26
29
|
lint:
|
|
27
|
-
runs-on: ubuntu-
|
|
30
|
+
runs-on: ubuntu-24.04
|
|
28
31
|
steps:
|
|
29
32
|
- name: Check out code
|
|
30
33
|
uses: actions/checkout@v5
|
|
31
34
|
|
|
35
|
+
- name: Set up uv
|
|
36
|
+
uses: astral-sh/setup-uv@v6
|
|
37
|
+
with:
|
|
38
|
+
version: "0.8.19"
|
|
39
|
+
enable-cache: true
|
|
40
|
+
|
|
32
41
|
- name: Set up Python
|
|
33
42
|
uses: actions/setup-python@v6
|
|
34
43
|
with:
|
|
35
44
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
36
|
-
cache: 'pip'
|
|
37
|
-
cache-dependency-path: |
|
|
38
|
-
pyproject.toml
|
|
39
45
|
|
|
40
46
|
- name: Install Ruff
|
|
41
47
|
run: |
|
|
42
|
-
|
|
43
|
-
pip install "ruff==0.9.*"
|
|
44
|
-
ruff --version
|
|
48
|
+
uvx ruff --version
|
|
45
49
|
|
|
46
50
|
- name: Run Ruff Linting
|
|
47
51
|
run: |
|
|
48
52
|
echo "::group::Ruff Linting"
|
|
49
|
-
ruff check . --output-format=github
|
|
53
|
+
uvx ruff check . --output-format=github
|
|
50
54
|
echo "::endgroup::"
|
|
51
55
|
|
|
52
56
|
- name: Run Ruff Formatting Check
|
|
53
57
|
run: |
|
|
54
58
|
echo "::group::Ruff Formatting"
|
|
55
|
-
ruff format --check --diff .
|
|
59
|
+
uvx ruff format --check --diff .
|
|
56
60
|
echo "::endgroup::"
|
|
57
61
|
|
|
58
62
|
test:
|
|
59
|
-
runs-on: ubuntu-
|
|
63
|
+
runs-on: ubuntu-24.04
|
|
60
64
|
timeout-minutes: 30
|
|
61
65
|
needs: lint # Run tests only after linting passes
|
|
62
66
|
strategy:
|
|
@@ -68,49 +72,49 @@ jobs:
|
|
|
68
72
|
- name: Check out code
|
|
69
73
|
uses: actions/checkout@v5
|
|
70
74
|
|
|
75
|
+
- name: Set up uv
|
|
76
|
+
uses: astral-sh/setup-uv@v6
|
|
77
|
+
with:
|
|
78
|
+
version: "0.8.19"
|
|
79
|
+
enable-cache: true
|
|
80
|
+
|
|
71
81
|
- name: Set up Python ${{ matrix.python-version }}
|
|
72
82
|
uses: actions/setup-python@v6
|
|
73
83
|
with:
|
|
74
84
|
python-version: ${{ matrix.python-version }}
|
|
75
|
-
cache: 'pip'
|
|
76
|
-
cache-dependency-path: |
|
|
77
|
-
pyproject.toml
|
|
78
85
|
|
|
79
86
|
- name: Install dependencies
|
|
80
87
|
run: |
|
|
81
|
-
|
|
82
|
-
pip install .[dev] pytest-xdist
|
|
88
|
+
uv pip install --system .[dev] "pytest-xdist==3.*"
|
|
83
89
|
|
|
84
90
|
- name: Run tests
|
|
85
91
|
run: pytest -v -p no:warnings --numprocesses=auto
|
|
86
92
|
|
|
87
93
|
security:
|
|
88
94
|
name: Security Scan
|
|
89
|
-
runs-on: ubuntu-
|
|
95
|
+
runs-on: ubuntu-24.04
|
|
90
96
|
needs: lint
|
|
91
97
|
steps:
|
|
92
98
|
- name: Check out code
|
|
93
99
|
uses: actions/checkout@v5
|
|
94
100
|
|
|
101
|
+
- name: Set up uv
|
|
102
|
+
uses: astral-sh/setup-uv@v6
|
|
103
|
+
with:
|
|
104
|
+
version: "0.8.19"
|
|
105
|
+
enable-cache: true
|
|
106
|
+
|
|
95
107
|
- name: Set up Python
|
|
96
108
|
uses: actions/setup-python@v6
|
|
97
109
|
with:
|
|
98
110
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
99
|
-
cache: 'pip'
|
|
100
|
-
cache-dependency-path: |
|
|
101
|
-
pyproject.toml
|
|
102
|
-
|
|
103
|
-
- name: Install security tools
|
|
104
|
-
run: |
|
|
105
|
-
python -m pip install --upgrade pip
|
|
106
|
-
pip install bandit[toml]
|
|
107
111
|
|
|
108
112
|
- name: Run Bandit security scan
|
|
109
113
|
run: |
|
|
110
114
|
# Gate on HIGH severity & MEDIUM confidence; produce JSON artifact
|
|
111
|
-
bandit -r flixopt/ -c pyproject.toml -f json -o bandit-report.json -q -
|
|
115
|
+
uvx bandit -r flixopt/ -c pyproject.toml -f json -o bandit-report.json -q --severity-level high --confidence-level medium
|
|
112
116
|
# Human-readable output without affecting job status
|
|
113
|
-
bandit -r flixopt/ -c pyproject.toml -q --exit-zero
|
|
117
|
+
uvx bandit -r flixopt/ -c pyproject.toml -q --exit-zero
|
|
114
118
|
|
|
115
119
|
- name: Upload security reports
|
|
116
120
|
uses: actions/upload-artifact@v4
|
|
@@ -122,7 +126,7 @@ jobs:
|
|
|
122
126
|
|
|
123
127
|
create-release:
|
|
124
128
|
name: Create GitHub Release
|
|
125
|
-
runs-on: ubuntu-
|
|
129
|
+
runs-on: ubuntu-24.04
|
|
126
130
|
permissions:
|
|
127
131
|
contents: write
|
|
128
132
|
needs: [lint, test, security]
|
|
@@ -134,6 +138,12 @@ jobs:
|
|
|
134
138
|
with:
|
|
135
139
|
fetch-depth: 0
|
|
136
140
|
|
|
141
|
+
- name: Set up uv
|
|
142
|
+
uses: astral-sh/setup-uv@v6
|
|
143
|
+
with:
|
|
144
|
+
version: "0.8.19"
|
|
145
|
+
enable-cache: true
|
|
146
|
+
|
|
137
147
|
- name: Set up Python
|
|
138
148
|
uses: actions/setup-python@v6
|
|
139
149
|
with:
|
|
@@ -157,46 +167,54 @@ jobs:
|
|
|
157
167
|
|
|
158
168
|
publish-testpypi:
|
|
159
169
|
name: Publish to TestPyPI
|
|
160
|
-
runs-on: ubuntu-
|
|
170
|
+
runs-on: ubuntu-24.04
|
|
161
171
|
needs: [test, create-release] # Run after tests and release creation
|
|
162
172
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') # Only on tag push
|
|
163
173
|
environment:
|
|
164
174
|
name: testpypi
|
|
165
175
|
url: https://test.pypi.org/p/flixopt
|
|
176
|
+
env:
|
|
177
|
+
SKIP_TESTPYPI_UPLOAD: "false"
|
|
166
178
|
|
|
167
179
|
steps:
|
|
168
180
|
- name: Checkout repository
|
|
169
181
|
uses: actions/checkout@v5
|
|
170
182
|
|
|
183
|
+
- name: Set up uv
|
|
184
|
+
uses: astral-sh/setup-uv@v6
|
|
185
|
+
with:
|
|
186
|
+
version: "0.8.19"
|
|
187
|
+
enable-cache: true
|
|
188
|
+
|
|
171
189
|
- name: Set up Python
|
|
172
190
|
uses: actions/setup-python@v6
|
|
173
191
|
with:
|
|
174
192
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
175
|
-
cache: 'pip'
|
|
176
|
-
cache-dependency-path: |
|
|
177
|
-
pyproject.toml
|
|
178
193
|
|
|
179
194
|
- name: Install dependencies
|
|
180
195
|
run: |
|
|
181
|
-
|
|
182
|
-
pip install build setuptools wheel twine
|
|
196
|
+
uv pip install --system twine
|
|
183
197
|
|
|
184
198
|
- name: Build the distribution
|
|
185
199
|
run: |
|
|
186
|
-
|
|
200
|
+
uv build
|
|
187
201
|
|
|
188
202
|
- name: Upload to TestPyPI
|
|
189
203
|
run: |
|
|
190
|
-
twine upload --repository-url https://test.pypi.org/legacy/ dist/* --verbose
|
|
204
|
+
twine upload --repository-url https://test.pypi.org/legacy/ dist/* --verbose --skip-existing
|
|
191
205
|
env:
|
|
192
206
|
TWINE_USERNAME: __token__
|
|
193
207
|
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
|
|
208
|
+
TWINE_NON_INTERACTIVE: "1"
|
|
194
209
|
|
|
195
210
|
- name: Test install from TestPyPI
|
|
211
|
+
if: env.SKIP_TESTPYPI_UPLOAD != 'true'
|
|
196
212
|
run: |
|
|
213
|
+
set -Eeuo pipefail
|
|
197
214
|
# Create a temporary environment to test installation
|
|
198
|
-
|
|
215
|
+
uv venv test_env
|
|
199
216
|
source test_env/bin/activate
|
|
217
|
+
|
|
200
218
|
# Get project name from pyproject.toml (PEP 621)
|
|
201
219
|
PACKAGE_NAME=$(python - <<'PY'
|
|
202
220
|
import sys, tomllib, pathlib
|
|
@@ -204,15 +222,18 @@ jobs:
|
|
|
204
222
|
print(data["project"]["name"])
|
|
205
223
|
PY
|
|
206
224
|
)
|
|
225
|
+
|
|
207
226
|
# Extract version from git tag
|
|
208
227
|
VERSION=${GITHUB_REF#refs/tags/v}
|
|
228
|
+
|
|
209
229
|
# Wait and retry while TestPyPI indexes the package
|
|
210
230
|
INSTALL_SUCCESS=false
|
|
211
|
-
for d in
|
|
231
|
+
for d in 10 20 40 80 120; do
|
|
212
232
|
sleep "$d"
|
|
213
233
|
echo "Attempting to install $PACKAGE_NAME==$VERSION from TestPyPI (retry after ${d}s)..."
|
|
234
|
+
|
|
214
235
|
# Install specific version and verify it matches
|
|
215
|
-
if pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "$PACKAGE_NAME==$VERSION" && \
|
|
236
|
+
if uv pip install --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "$PACKAGE_NAME==$VERSION" && \
|
|
216
237
|
python -c "from importlib.metadata import version; installed = version('$PACKAGE_NAME'); print(f'Installed: {installed}'); assert '$VERSION' == installed"; then
|
|
217
238
|
INSTALL_SUCCESS=true
|
|
218
239
|
break
|
|
@@ -234,7 +255,7 @@ jobs:
|
|
|
234
255
|
|
|
235
256
|
publish-pypi:
|
|
236
257
|
name: Publish to PyPI
|
|
237
|
-
runs-on: ubuntu-
|
|
258
|
+
runs-on: ubuntu-24.04
|
|
238
259
|
needs: [publish-testpypi] # Only run after TestPyPI publish succeeds
|
|
239
260
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') # Only on tag push
|
|
240
261
|
environment:
|
|
@@ -245,35 +266,40 @@ jobs:
|
|
|
245
266
|
- name: Checkout repository
|
|
246
267
|
uses: actions/checkout@v5
|
|
247
268
|
|
|
269
|
+
- name: Set up uv
|
|
270
|
+
uses: astral-sh/setup-uv@v6
|
|
271
|
+
with:
|
|
272
|
+
version: "0.8.19"
|
|
273
|
+
enable-cache: true
|
|
274
|
+
|
|
248
275
|
- name: Set up Python
|
|
249
276
|
uses: actions/setup-python@v6
|
|
250
277
|
with:
|
|
251
278
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
252
|
-
cache: 'pip'
|
|
253
|
-
cache-dependency-path: |
|
|
254
|
-
pyproject.toml
|
|
255
279
|
|
|
256
280
|
- name: Install dependencies
|
|
257
281
|
run: |
|
|
258
|
-
|
|
259
|
-
pip install build setuptools wheel twine
|
|
282
|
+
uv pip install --system twine
|
|
260
283
|
|
|
261
284
|
- name: Build the distribution
|
|
262
285
|
run: |
|
|
263
|
-
|
|
286
|
+
uv build
|
|
264
287
|
|
|
265
288
|
- name: Upload to PyPI
|
|
266
289
|
run: |
|
|
267
|
-
twine upload dist/* --verbose
|
|
290
|
+
twine upload dist/* --verbose --skip-existing
|
|
268
291
|
env:
|
|
269
292
|
TWINE_USERNAME: __token__
|
|
270
293
|
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
294
|
+
TWINE_NON_INTERACTIVE: "1"
|
|
271
295
|
|
|
272
296
|
- name: Verify PyPI installation
|
|
273
297
|
run: |
|
|
298
|
+
set -Eeuo pipefail
|
|
274
299
|
# Create a temporary environment to test installation
|
|
275
|
-
|
|
300
|
+
uv venv prod_test_env
|
|
276
301
|
source prod_test_env/bin/activate
|
|
302
|
+
|
|
277
303
|
# Get project name from pyproject.toml (PEP 621)
|
|
278
304
|
PACKAGE_NAME=$(python - <<'PY'
|
|
279
305
|
import sys, tomllib, pathlib
|
|
@@ -281,15 +307,18 @@ jobs:
|
|
|
281
307
|
print(data["project"]["name"])
|
|
282
308
|
PY
|
|
283
309
|
)
|
|
310
|
+
|
|
284
311
|
# Extract version from git tag
|
|
285
312
|
VERSION=${GITHUB_REF#refs/tags/v}
|
|
313
|
+
|
|
286
314
|
# Wait and retry while PyPI indexes the package
|
|
287
315
|
INSTALL_SUCCESS=false
|
|
288
|
-
for d in
|
|
316
|
+
for d in 10 20 40 80 120; do
|
|
289
317
|
sleep "$d"
|
|
290
318
|
echo "Attempting to install $PACKAGE_NAME==$VERSION from PyPI (retry after ${d}s)..."
|
|
319
|
+
|
|
291
320
|
# Install specific version and verify it matches
|
|
292
|
-
if pip install "$PACKAGE_NAME==$VERSION" && \
|
|
321
|
+
if uv pip install "$PACKAGE_NAME==$VERSION" && \
|
|
293
322
|
python -c "from importlib.metadata import version; installed = version('$PACKAGE_NAME'); print(f'Installed: {installed}'); assert '$VERSION' == installed"; then
|
|
294
323
|
INSTALL_SUCCESS=true
|
|
295
324
|
break
|
|
@@ -311,7 +340,7 @@ jobs:
|
|
|
311
340
|
|
|
312
341
|
deploy-docs:
|
|
313
342
|
name: Deploy Documentation
|
|
314
|
-
runs-on: ubuntu-
|
|
343
|
+
runs-on: ubuntu-24.04
|
|
315
344
|
permissions:
|
|
316
345
|
contents: write
|
|
317
346
|
needs: [publish-pypi] # Deploy docs after successful PyPI publishing
|
|
@@ -323,13 +352,16 @@ jobs:
|
|
|
323
352
|
with:
|
|
324
353
|
fetch-depth: 0 # Fetch all history for proper versioning
|
|
325
354
|
|
|
355
|
+
- name: Set up uv
|
|
356
|
+
uses: astral-sh/setup-uv@v6
|
|
357
|
+
with:
|
|
358
|
+
version: "0.8.19"
|
|
359
|
+
enable-cache: true
|
|
360
|
+
|
|
326
361
|
- name: Set up Python
|
|
327
362
|
uses: actions/setup-python@v6
|
|
328
363
|
with:
|
|
329
364
|
python-version: ${{ env.PYTHON_VERSION }}
|
|
330
|
-
cache: 'pip'
|
|
331
|
-
cache-dependency-path: |
|
|
332
|
-
pyproject.toml
|
|
333
365
|
|
|
334
366
|
- name: Sync changelog to docs
|
|
335
367
|
run: |
|
|
@@ -338,8 +370,7 @@ jobs:
|
|
|
338
370
|
|
|
339
371
|
- name: Install documentation dependencies
|
|
340
372
|
run: |
|
|
341
|
-
|
|
342
|
-
pip install -e ".[docs]"
|
|
373
|
+
uv pip install --system ".[docs]"
|
|
343
374
|
|
|
344
375
|
- name: Configure Git Credentials
|
|
345
376
|
run: |
|
|
@@ -6,23 +6,64 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
8
|
<!-- This text won't be rendered
|
|
9
|
+
Note: The CI will automatically append a "What's Changed" section to the changelog.
|
|
10
|
+
This contains all commits, PRs, and contributors.
|
|
11
|
+
Therefore, the Changelog should focus on the user-facing changes.
|
|
12
|
+
Please remove all irrelevant sections before releasing.
|
|
9
13
|
|
|
10
|
-
## [Unreleased]
|
|
11
|
-
|
|
12
|
-
### Changed
|
|
14
|
+
## [Unreleased] - ????-??-??
|
|
13
15
|
|
|
14
16
|
### Added
|
|
15
17
|
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
16
20
|
### Deprecated
|
|
17
21
|
|
|
22
|
+
### Removed
|
|
23
|
+
|
|
18
24
|
### Fixed
|
|
19
25
|
|
|
20
26
|
### Known issues
|
|
21
27
|
|
|
22
|
-
### Development
|
|
28
|
+
### *Development*
|
|
23
29
|
|
|
24
30
|
Until here -->
|
|
25
31
|
|
|
32
|
+
## [2.1.9] - 2025-09-23
|
|
33
|
+
Small Bugfix which was supposed to be fixed in 2.1.8
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
- Fix error handling in network visualization if networkx is not installed.
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## [2.1.8] - 2025-09-22
|
|
40
|
+
This release focuses on code quality improvements, enhanced documentation, and bug fixes for heat pump components and visualization features.
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
- Extra Check for HeatPumpWithSource.COP to be strictly > 1 to avoid division by zero
|
|
44
|
+
- Apply deterministic color assignment by using sorted() in `plotting.py`
|
|
45
|
+
- Add missing args in docstrings in `plotting.py`, `solvers.py`, and `core.py`.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
- Greatly improved docstrings and documentation of all public classes
|
|
49
|
+
- Make path handling to be gentle about missing .html suffix in `plotting.py`
|
|
50
|
+
- Default for `relative_losses` in `Transmission` is now 0 instead of None
|
|
51
|
+
- Setter of COP in `HeatPumpWithSource` now completely overwrites the conversion factors, which is safer.
|
|
52
|
+
- Fix some docstrings in plotting.py
|
|
53
|
+
- Change assertions to raise Exceptions in `plotting.py`
|
|
54
|
+
|
|
55
|
+
### Fixed
|
|
56
|
+
- Fix color scheme selection in network_app; color pickers now update when a scheme is selected.
|
|
57
|
+
- Fix error handling in network visualization if networkx is not installed.
|
|
58
|
+
- Fix broken links in docs.
|
|
59
|
+
- Fix COP getter and setter of `HeatPumpWithSource` returning and setting wrong conversion factors.
|
|
60
|
+
- Fix custom compression levels in `io.save_dataset_to_netcdf`
|
|
61
|
+
- Fix `total_max` did not work when total min was not used.
|
|
62
|
+
|
|
63
|
+
### *Development*
|
|
64
|
+
- Pin dev dependencies to specific versions
|
|
65
|
+
- Improve CI workflows to run faster and smarter
|
|
66
|
+
|
|
26
67
|
## [2.1.7] - 2025-09-13
|
|
27
68
|
|
|
28
69
|
This update is a maintenance release to improve Code Quality, CI and update the dependencies.
|
|
@@ -31,7 +72,7 @@ There are no changes or new features.
|
|
|
31
72
|
### Added
|
|
32
73
|
- Added __version__ to flixopt
|
|
33
74
|
|
|
34
|
-
### Development
|
|
75
|
+
### *Development*
|
|
35
76
|
- ruff format the whole Codebase
|
|
36
77
|
- Added renovate config
|
|
37
78
|
- Added pre-commit
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flixopt
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.9
|
|
4
4
|
Summary: Vector based energy and material flow optimization framework in Python.
|
|
5
5
|
Author-email: "Chair of Building Energy Systems and Heat Supply, TU Dresden" <peter.stange@tu-dresden.de>, Felix Bumann <felixbumann387@gmail.com>, Felix Panitz <baumbude@googlemail.com>, Peter Stange <peter.stange@tu-dresden.de>
|
|
6
6
|
Maintainer-email: Felix Bumann <felixbumann387@gmail.com>, Peter Stange <peter.stange@tu-dresden.de>
|
|
@@ -24,54 +24,58 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
Requires-Dist: numpy<3,>=1.21.5
|
|
26
26
|
Requires-Dist: pandas<3,>=2.0.0
|
|
27
|
-
Requires-Dist:
|
|
27
|
+
Requires-Dist: xarray<2026.0,>=2024.2.0
|
|
28
|
+
Requires-Dist: linopy<0.6,>=0.5.1
|
|
28
29
|
Requires-Dist: netcdf4<2,>=1.6.1
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist: rich
|
|
31
|
-
Requires-Dist: tomli
|
|
32
|
-
Requires-Dist: highspy
|
|
33
|
-
Requires-Dist: matplotlib<4
|
|
34
|
-
Requires-Dist: plotly<
|
|
30
|
+
Requires-Dist: pyyaml<7,>=6.0.0
|
|
31
|
+
Requires-Dist: rich<15,>=13.0.0
|
|
32
|
+
Requires-Dist: tomli<3,>=2.0.1; python_version < "3.11"
|
|
33
|
+
Requires-Dist: highspy<2,>=1.5.3
|
|
34
|
+
Requires-Dist: matplotlib<4,>=3.5.2
|
|
35
|
+
Requires-Dist: plotly<7,>=5.15.0
|
|
35
36
|
Provides-Extra: network-viz
|
|
36
|
-
Requires-Dist: dash
|
|
37
|
-
Requires-Dist: dash-cytoscape
|
|
38
|
-
Requires-Dist: dash-daq
|
|
39
|
-
Requires-Dist: networkx
|
|
40
|
-
Requires-Dist: werkzeug
|
|
37
|
+
Requires-Dist: dash<4,>=3.0.0; extra == "network-viz"
|
|
38
|
+
Requires-Dist: dash-cytoscape<2,>=1.0.0; extra == "network-viz"
|
|
39
|
+
Requires-Dist: dash-daq<1,>=0.6.0; extra == "network-viz"
|
|
40
|
+
Requires-Dist: networkx<4,>=3.0.0; extra == "network-viz"
|
|
41
|
+
Requires-Dist: werkzeug<4,>=3.0.0; extra == "network-viz"
|
|
42
|
+
Requires-Dist: flask<4,>=3.0.0; extra == "network-viz"
|
|
41
43
|
Provides-Extra: full
|
|
42
44
|
Requires-Dist: pyvis==0.3.2; extra == "full"
|
|
43
|
-
Requires-Dist: tsam<3
|
|
44
|
-
Requires-Dist: scipy<2
|
|
45
|
-
Requires-Dist: gurobipy
|
|
46
|
-
Requires-Dist: dash
|
|
47
|
-
Requires-Dist: dash-cytoscape
|
|
48
|
-
Requires-Dist: dash-daq
|
|
49
|
-
Requires-Dist: networkx
|
|
50
|
-
Requires-Dist: werkzeug
|
|
45
|
+
Requires-Dist: tsam<3,>=2.3.1; extra == "full"
|
|
46
|
+
Requires-Dist: scipy<2,>=1.15.1; extra == "full"
|
|
47
|
+
Requires-Dist: gurobipy<13,>=10.0.0; extra == "full"
|
|
48
|
+
Requires-Dist: dash<4,>=3.0.0; extra == "full"
|
|
49
|
+
Requires-Dist: dash-cytoscape<2,>=1.0.0; extra == "full"
|
|
50
|
+
Requires-Dist: dash-daq<1,>=0.6.0; extra == "full"
|
|
51
|
+
Requires-Dist: networkx<4,>=3.0.0; extra == "full"
|
|
52
|
+
Requires-Dist: werkzeug<4,>=3.0.0; extra == "full"
|
|
53
|
+
Requires-Dist: flask<4,>=3.0.0; extra == "full"
|
|
51
54
|
Provides-Extra: dev
|
|
52
|
-
Requires-Dist: pytest
|
|
53
|
-
Requires-Dist:
|
|
54
|
-
Requires-Dist:
|
|
55
|
+
Requires-Dist: pytest==8.4.2; extra == "dev"
|
|
56
|
+
Requires-Dist: nbformat==5.10.4; extra == "dev"
|
|
57
|
+
Requires-Dist: ruff==0.13.0; extra == "dev"
|
|
58
|
+
Requires-Dist: pre-commit==4.3.0; extra == "dev"
|
|
55
59
|
Requires-Dist: pyvis==0.3.2; extra == "dev"
|
|
56
|
-
Requires-Dist: tsam
|
|
57
|
-
Requires-Dist: scipy
|
|
58
|
-
Requires-Dist: gurobipy
|
|
59
|
-
Requires-Dist: dash
|
|
60
|
-
Requires-Dist: dash-cytoscape
|
|
61
|
-
Requires-Dist: dash-daq
|
|
62
|
-
Requires-Dist: networkx
|
|
63
|
-
Requires-Dist: werkzeug
|
|
60
|
+
Requires-Dist: tsam==2.3.1; extra == "dev"
|
|
61
|
+
Requires-Dist: scipy==1.15.1; extra == "dev"
|
|
62
|
+
Requires-Dist: gurobipy==12.0.3; extra == "dev"
|
|
63
|
+
Requires-Dist: dash==3.0.0; extra == "dev"
|
|
64
|
+
Requires-Dist: dash-cytoscape==1.0.2; extra == "dev"
|
|
65
|
+
Requires-Dist: dash-daq==0.6.0; extra == "dev"
|
|
66
|
+
Requires-Dist: networkx==3.0.0; extra == "dev"
|
|
67
|
+
Requires-Dist: werkzeug==3.0.0; extra == "dev"
|
|
64
68
|
Provides-Extra: docs
|
|
65
|
-
Requires-Dist: mkdocs-material
|
|
66
|
-
Requires-Dist: mkdocstrings-python
|
|
67
|
-
Requires-Dist: mkdocs-table-reader-plugin
|
|
68
|
-
Requires-Dist: mkdocs-gen-files
|
|
69
|
-
Requires-Dist: mkdocs-include-markdown-plugin
|
|
70
|
-
Requires-Dist: mkdocs-literate-nav
|
|
71
|
-
Requires-Dist: markdown-include
|
|
72
|
-
Requires-Dist: pymdown-extensions
|
|
73
|
-
Requires-Dist: pygments
|
|
74
|
-
Requires-Dist: mike
|
|
69
|
+
Requires-Dist: mkdocs-material==9.6.19; extra == "docs"
|
|
70
|
+
Requires-Dist: mkdocstrings-python==1.18.2; extra == "docs"
|
|
71
|
+
Requires-Dist: mkdocs-table-reader-plugin==3.1.0; extra == "docs"
|
|
72
|
+
Requires-Dist: mkdocs-gen-files==0.5.0; extra == "docs"
|
|
73
|
+
Requires-Dist: mkdocs-include-markdown-plugin==7.1.7; extra == "docs"
|
|
74
|
+
Requires-Dist: mkdocs-literate-nav==0.6.2; extra == "docs"
|
|
75
|
+
Requires-Dist: markdown-include==0.8.1; extra == "docs"
|
|
76
|
+
Requires-Dist: pymdown-extensions==10.16.1; extra == "docs"
|
|
77
|
+
Requires-Dist: pygments==2.19.2; extra == "docs"
|
|
78
|
+
Requires-Dist: mike==2.1.3; extra == "docs"
|
|
75
79
|
Dynamic: license-file
|
|
76
80
|
|
|
77
81
|
# FlixOpt: Energy and Material Flow Optimization Framework
|
|
@@ -19,7 +19,7 @@ This provides the core functionality with the HiGHS solver included.
|
|
|
19
19
|
For all features including interactive network visualizations and time series aggregation:
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
-
pip install "flixopt[full]"
|
|
22
|
+
pip install "flixopt[full]"
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
## Basic Workflow
|
|
@@ -38,5 +38,5 @@ Working with FlixOpt follows a general pattern:
|
|
|
38
38
|
Now that you've installed FlixOpt and understand the basic workflow, you can:
|
|
39
39
|
|
|
40
40
|
- Learn about the [core concepts of FlixOpt](user-guide/index.md)
|
|
41
|
-
- Explore some [examples](examples/)
|
|
41
|
+
- Explore some [examples](examples/index.md)
|
|
42
42
|
- Check the [API reference](api-reference/index.md) for detailed documentation
|
|
@@ -1,15 +1,15 @@
|
|
|
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
11
|
-
|
|
12
|
-
Effects are allocated
|
|
12
|
+
Effects are allocated separately for investments and operation.
|
|
13
13
|
|
|
14
14
|
### Shares to Effects
|
|
15
15
|
|
|
@@ -43,7 +43,7 @@ For example, the Effect "CO$_2$ emissions" (unit: kg)
|
|
|
43
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
49
|
E_{e, \text{inv}} =
|
|
@@ -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$
|
|
@@ -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 **
|
|
131
|
+
- ... the **Weighted Sum** method, as the chosen **Objective Effect** can incorporate other Effects.
|
|
132
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)
|
|
@@ -16,6 +16,6 @@ $$ \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
|
|
19
|
+
where $\text a$ can be interpreted as the conversion efficiency of the **LinearConverter**.
|
|
20
|
+
#### Piecewise Conversion factors
|
|
21
21
|
The conversion efficiency can be defined as a piecewise linear approximation. See [Piecewise](Piecewise.md) for more details.
|
|
@@ -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}
|