36 Commits

Author SHA1 Message Date
Normann
447f7d05be Update dependabot.yml to also include the feature branch 2025-03-23 22:48:02 +01:00
dependabot[bot]
c72051a08e Bump sphinx from 8.1.3 to 8.2.3 (#476)
Bumps [sphinx](https://github.com/sphinx-doc/sphinx) from 8.1.3 to 8.2.3.
- [Release notes](https://github.com/sphinx-doc/sphinx/releases)
- [Changelog](https://github.com/sphinx-doc/sphinx/blob/master/CHANGES.rst)
- [Commits](https://github.com/sphinx-doc/sphinx/compare/v8.1.3...v8.2.3)

---
updated-dependencies:
- dependency-name: sphinx
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-23 22:14:51 +01:00
Normann
60bd320fbc Python req. to 3.11 for sphinx update (#487)
* Python req. to 3.11 for sphinx update

* Update pyproject.toml
2025-03-23 22:09:02 +01:00
dependabot[bot]
600e332aae Bump platformdirs from 4.3.6 to 4.3.7 (#485)
Bumps [platformdirs](https://github.com/tox-dev/platformdirs) from 4.3.6 to 4.3.7.
- [Release notes](https://github.com/tox-dev/platformdirs/releases)
- [Changelog](https://github.com/tox-dev/platformdirs/blob/main/CHANGES.rst)
- [Commits](https://github.com/tox-dev/platformdirs/compare/4.3.6...4.3.7)

---
updated-dependencies:
- dependency-name: platformdirs
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-23 21:14:34 +01:00
dependabot[bot]
6a51de04da Bump pvlib from 0.11.2 to 0.12.0 (#486)
Bumps [pvlib](https://github.com/pvlib/pvlib-python) from 0.11.2 to 0.12.0.
- [Release notes](https://github.com/pvlib/pvlib-python/releases)
- [Commits](https://github.com/pvlib/pvlib-python/compare/v0.11.2...v0.12.0)

---
updated-dependencies:
- dependency-name: pvlib
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-23 21:08:51 +01:00
dependabot[bot]
64c8415714 Bump pandas-stubs from 2.2.3.241126 to 2.2.3.250308 (#479)
Bumps [pandas-stubs](https://github.com/pandas-dev/pandas-stubs) from 2.2.3.241126 to 2.2.3.250308.
- [Changelog](https://github.com/pandas-dev/pandas-stubs/blob/main/docs/release_procedure.md)
- [Commits](https://github.com/pandas-dev/pandas-stubs/compare/v2.2.3.241126...v2.2.3.250308)

---
updated-dependencies:
- dependency-name: pandas-stubs
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-23 21:01:48 +01:00
dependabot[bot]
52f8b015b1 Bump numpy from 2.2.3 to 2.2.4 (#483)
Bumps [numpy](https://github.com/numpy/numpy) from 2.2.3 to 2.2.4.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v2.2.3...v2.2.4)

---
updated-dependencies:
- dependency-name: numpy
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-23 21:01:33 +01:00
thiloms
a4adb07ebf CONTRIBUTING.md fix documentation link (404 error) (#484)
Fix take over the same link as in README.md. The current link will end up in HTTP error 404 (not found).
2025-03-23 13:52:15 +01:00
dependabot[bot]
b69bbe897f Bump numpydantic from 1.6.7 to 1.6.8 (#480)
Bumps [numpydantic](https://github.com/p2p-ld/numpydantic) from 1.6.7 to 1.6.8.
- [Release notes](https://github.com/p2p-ld/numpydantic/releases)
- [Changelog](https://github.com/p2p-ld/numpydantic/blob/main/docs/changelog.md)
- [Commits](https://github.com/p2p-ld/numpydantic/compare/v1.6.7...v1.6.8)

---
updated-dependencies:
- dependency-name: numpydantic
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-23 13:51:06 +01:00
dependabot[bot]
0a7420c42b Bump types-requests from 2.32.0.20250301 to 2.32.0.20250306 (#478)
Bumps [types-requests](https://github.com/python/typeshed) from 2.32.0.20250301 to 2.32.0.20250306.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-23 13:49:47 +01:00
Eric
b22b5ee651 Conceptual documentation (#463)
Added new instruction.md, changed index.md accordingly and deleted the no longer needed about.md of new documentation structure.
Refinement of differences to other solutions and features of EOS.

Co-authored-by: Eric Hirsch <git@familie-hirsch.net>
2025-03-23 13:27:40 +01:00
dependabot[bot]
9b4ec74823 Bump pytest from 8.3.4 to 8.3.5 (#475)
Some checks failed
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.4 to 8.3.5.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.3.4...8.3.5)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-05 10:32:48 +01:00
dependabot[bot]
1f30d4e403 Bump python-fasthtml from 0.12.1 to 0.12.4 (#470)
Some checks failed
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
Bumps [python-fasthtml](https://github.com/AnswerDotAI/fasthtml) from 0.12.1 to 0.12.4.
- [Release notes](https://github.com/AnswerDotAI/fasthtml/releases)
- [Changelog](https://github.com/AnswerDotAI/fasthtml/blob/main/CHANGELOG.md)
- [Commits](https://github.com/AnswerDotAI/fasthtml/compare/0.12.1...0.12.4)

---
updated-dependencies:
- dependency-name: python-fasthtml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-02 11:10:23 +01:00
dependabot[bot]
b563bbbd98 Bump matplotlib from 3.10.0 to 3.10.1 (#471)
Bumps [matplotlib](https://github.com/matplotlib/matplotlib) from 3.10.0 to 3.10.1.
- [Release notes](https://github.com/matplotlib/matplotlib/releases)
- [Commits](https://github.com/matplotlib/matplotlib/compare/v3.10.0...v3.10.1)

---
updated-dependencies:
- dependency-name: matplotlib
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-02 11:04:30 +01:00
dependabot[bot]
8422a5c9d8 Bump types-requests from 2.32.0.20241016 to 2.32.0.20250301 (#473)
Bumps [types-requests](https://github.com/python/typeshed) from 2.32.0.20241016 to 2.32.0.20250301.
- [Commits](https://github.com/python/typeshed/commits)

---
updated-dependencies:
- dependency-name: types-requests
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-02 10:58:46 +01:00
dependabot[bot]
bec5c2cbda Bump fastapi[standard] from 0.115.8 to 0.115.11 (#472)
Bumps [fastapi[standard]](https://github.com/fastapi/fastapi) from 0.115.8 to 0.115.11.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.115.8...0.115.11)

---
updated-dependencies:
- dependency-name: fastapi[standard]
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-02 10:57:57 +01:00
Dominique Lasserre
d2136f1447 Dockerfile: Set default for EOS_SERVER__EOSDASH_SESSKEY Closes #447 (#467)
Some checks failed
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
* This allows to start the container without any extra settings
   (potentially unsafe).
   It is recommended to set EOS_SERVER__EOSDASH_SESSKEY.
2025-02-23 16:17:54 +01:00
Dominique Lasserre
20621aa626 docker-compose: Expose EOSdash port Closes #447
Some checks failed
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
* Fixes direct EOSdash access on Windows localhost:8504 (required for
   redirect).
2025-02-18 07:08:14 +01:00
Dominique Lasserre
76b5ec3638 visualize.py: Support variable remuneration Closes #451 (#459)
Some checks failed
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
2025-02-16 11:06:31 +01:00
dependabot[bot]
d912561bfb Bump numpy from 2.2.2 to 2.2.3 (#456)
Some checks are pending
docker-build / platform-excludes (push) Waiting to run
docker-build / build (push) Blocked by required conditions
docker-build / merge (push) Blocked by required conditions
pre-commit / pre-commit (push) Waiting to run
Run Pytest on Pull Request / test (push) Waiting to run
Bumps [numpy](https://github.com/numpy/numpy) from 2.2.2 to 2.2.3.
- [Release notes](https://github.com/numpy/numpy/releases)
- [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst)
- [Commits](https://github.com/numpy/numpy/compare/v2.2.2...v2.2.3)

---
updated-dependencies:
- dependency-name: numpy
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-15 13:32:48 +01:00
dependabot[bot]
5907c94a2e Bump myst-parser from 4.0.0 to 4.0.1 (#455)
Bumps [myst-parser](https://github.com/executablebooks/MyST-Parser) from 4.0.0 to 4.0.1.
- [Release notes](https://github.com/executablebooks/MyST-Parser/releases)
- [Changelog](https://github.com/executablebooks/MyST-Parser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/executablebooks/MyST-Parser/compare/v4.0.0...v4.0.1)

---
updated-dependencies:
- dependency-name: myst-parser
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-15 13:29:34 +01:00
Bobby Noelte
7b9b58f1e0 Add Markdown linter
Some checks failed
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
Add Markdown linter (pymarkdown) to pre-commit.
Adapt current markdown files to fulfill linter rules.

Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
2025-02-13 12:10:47 +01:00
Dennis
c87bf2e4fc EOF issue in "optimize" documentation 2025-02-13 12:10:47 +01:00
Dennis
7773c4c2c9 Initial "optimize" documentation 2025-02-13 12:10:47 +01:00
Dominique Lasserre
b380624c9f Windows: Fix EOSdash startup Closes #436 #447 (#450)
Some checks failed
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
* On Windows use 127.0.0.1 as default config host (model defaults) and
   addionally redirect 0.0.0.0 to localhost on Windows (because default
   config file still has 0.0.0.0).
   Use 0.0.0.0 as default otherwise (e.g. Linux/Docker) to allow EOS
   being accessible on local network (not just same host).
   Note: Docs generation on Windows is incompatible with the Github
   pipeline tests. Address this in the nested-config feature branch.
 * Update install/startup instructions as package installation is
   required atm and Docker on Windows has to be accessed at localhost or
   127.0.0.1 even though the server log says 0.0.0.0 (which is required
   to be available outside the container).
 * Fix EOSdash startup with read_only: true (support session key via
   EOS_SERVER__EOSDASH_SESSKEY variable). Backport of feature branch.
 * Remove root_path, causing Windows to fail load swagger UI (/docs).
2025-02-10 00:38:35 +01:00
dependabot[bot]
caed880672 Bump mypy from 1.13.0 to 1.15.0 (#449)
Some checks failed
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
Bumps [mypy](https://github.com/python/mypy) from 1.13.0 to 1.15.0.
- [Changelog](https://github.com/python/mypy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/python/mypy/compare/v1.13.0...v1.15.0)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-08 01:08:17 +01:00
celle1234
6cc9a5fd44 visualize: fix timestamps on diagrams (#430) Closes #387
* visualize: fix timestamps on diagrams
* set start time in all graphs to the same beginning hour

---------

Co-authored-by: Normann <github@koldrack.com>
2025-02-08 00:47:21 +01:00
dependabot[bot]
80a4079bbf Bump fastapi[standard] from 0.115.7 to 0.115.8 (#442)
Some checks failed
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
Bumps [fastapi[standard]](https://github.com/fastapi/fastapi) from 0.115.7 to 0.115.8.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.115.7...0.115.8)

---
updated-dependencies:
- dependency-name: fastapi[standard]
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-04 08:30:54 +01:00
dependabot[bot]
cc687b140f Bump python-fasthtml from 0.12.0 to 0.12.1 (#441)
Bumps [python-fasthtml](https://github.com/AnswerDotAI/fasthtml) from 0.12.0 to 0.12.1.
- [Release notes](https://github.com/AnswerDotAI/fasthtml/releases)
- [Changelog](https://github.com/AnswerDotAI/fasthtml/blob/main/CHANGELOG.md)
- [Commits](https://github.com/AnswerDotAI/fasthtml/compare/0.12.0...0.12.1)

---
updated-dependencies:
- dependency-name: python-fasthtml
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-04 08:25:09 +01:00
Dominique Lasserre
29cf3a3174 README.md: Add some system requirements (#438)
Some checks failed
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
2025-02-01 20:46:00 +01:00
Theo Weiss
837595de56 remove excess double quotes in Makefile (#437) 2025-02-01 16:41:24 +01:00
Normann
1a2da7636b Data prefetch for ems (#418)
Some checks failed
docker-build / platform-excludes (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
* Pre-fetch data

* maintanance and extend tests

* comment clean up

* nansum usage (to be save)
2025-01-26 18:29:26 +01:00
dependabot[bot]
774cfd8b65 Bump fastapi[standard] from 0.115.6 to 0.115.7 (#411)
Some checks are pending
docker-build / platform-excludes (push) Waiting to run
docker-build / build (push) Blocked by required conditions
docker-build / merge (push) Blocked by required conditions
pre-commit / pre-commit (push) Waiting to run
Run Pytest on Pull Request / test (push) Waiting to run
Bumps [fastapi[standard]](https://github.com/fastapi/fastapi) from 0.115.6 to 0.115.7.
- [Release notes](https://github.com/fastapi/fastapi/releases)
- [Commits](https://github.com/fastapi/fastapi/compare/0.115.6...0.115.7)

---
updated-dependencies:
- dependency-name: fastapi[standard]
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-25 19:35:42 +01:00
dependabot[bot]
9170b5f5cd Bump pydantic from 2.10.5 to 2.10.6 (#412)
Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.10.5 to 2.10.6.
- [Release notes](https://github.com/pydantic/pydantic/releases)
- [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md)
- [Commits](https://github.com/pydantic/pydantic/compare/v2.10.5...v2.10.6)

---
updated-dependencies:
- dependency-name: pydantic
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-25 19:32:34 +01:00
dependabot[bot]
e42dd5d4b2 Bump timezonefinder from 6.5.7 to 6.5.8 (#414)
Bumps [timezonefinder](https://github.com/jannikmi/timezonefinder) from 6.5.7 to 6.5.8.
- [Release notes](https://github.com/jannikmi/timezonefinder/releases)
- [Changelog](https://github.com/jannikmi/timezonefinder/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jannikmi/timezonefinder/compare/6.5.7...6.5.8)

---
updated-dependencies:
- dependency-name: timezonefinder
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-25 19:29:00 +01:00
dependabot[bot]
58f077b4ae Bump numpydantic from 1.6.4 to 1.6.7 (#413)
Bumps [numpydantic](https://github.com/p2p-ld/numpydantic) from 1.6.4 to 1.6.7.
- [Release notes](https://github.com/p2p-ld/numpydantic/releases)
- [Changelog](https://github.com/p2p-ld/numpydantic/blob/main/docs/changelog.md)
- [Commits](https://github.com/p2p-ld/numpydantic/compare/v1.6.4...v1.6.7)

---
updated-dependencies:
- dependency-name: numpydantic
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-25 19:24:44 +01:00
35 changed files with 863 additions and 232 deletions

1
.env
View File

@@ -1,4 +1,5 @@
EOS_VERSION=main EOS_VERSION=main
EOS_PORT=8503 EOS_PORT=8503
EOSDASH_PORT=8504
PYTHON_VERSION=3.12.6 PYTHON_VERSION=3.12.6

View File

@@ -5,7 +5,16 @@
version: 2 version: 2
updates: updates:
# Update dependencies on the main branch
- package-ecosystem: "pip" # See documentation for possible values - package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests directory: "/" # Location of package manifests
schedule: schedule:
interval: "weekly" interval: "weekly"
target-branch: "main" # Target the main branch
# Update dependencies on the feature/config-nested branch
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
target-branch: "feature/config-nested" # Target the specific feature branch

View File

@@ -33,3 +33,12 @@ repos:
- "pandas-stubs==2.2.3.241009" - "pandas-stubs==2.2.3.241009"
- "numpy==2.1.3" - "numpy==2.1.3"
pass_filenames: false pass_filenames: false
- repo: https://github.com/jackdewinter/pymarkdown
rev: main
hooks:
- id: pymarkdown
files: ^docs/
exclude: ^docs/_generated
args:
- --config=docs/pymarkdown.json
- scan

View File

@@ -6,7 +6,7 @@ The `EOS` project is in early development, therefore we encourage contribution i
## Documentation ## Documentation
Latest development documentation can be found at [Akkudoktor-EOS](https://akkudoktor-eos.readthedocs.io/en/main/). Latest development documentation can be found at [Akkudoktor-EOS](https://akkudoktor-eos.readthedocs.io/en/latest/).
## Bug Reports ## Bug Reports
@@ -33,6 +33,7 @@ See also [README.md](README.md).
python -m venv .venv python -m venv .venv
source .venv/bin/activate source .venv/bin/activate
pip install -r requirements-dev.txt pip install -r requirements-dev.txt
pip install -e .
``` ```
Install make to get access to helpful shortcuts (documentation generation, manual formatting, etc.). Install make to get access to helpful shortcuts (documentation generation, manual formatting, etc.).

View File

@@ -11,6 +11,9 @@ ENV EOS_CACHE_DIR="${EOS_DIR}/cache"
ENV EOS_OUTPUT_DIR="${EOS_DIR}/output" ENV EOS_OUTPUT_DIR="${EOS_DIR}/output"
ENV EOS_CONFIG_DIR="${EOS_DIR}/config" ENV EOS_CONFIG_DIR="${EOS_DIR}/config"
# Overwrite when starting the container in a production environment
ENV EOS_SERVER__EOSDASH_SESSKEY=s3cr3t
WORKDIR ${EOS_DIR} WORKDIR ${EOS_DIR}
RUN adduser --system --group --no-create-home eos \ RUN adduser --system --group --no-create-home eos \
@@ -39,6 +42,7 @@ ENTRYPOINT []
EXPOSE 8503 EXPOSE 8503
EXPOSE 8504 EXPOSE 8504
ENV server_eosdash_host=0.0.0.0
CMD ["python", "src/akkudoktoreos/server/eos.py", "--host", "0.0.0.0"] CMD ["python", "src/akkudoktoreos/server/eos.py", "--host", "0.0.0.0"]
VOLUME ["${MPLCONFIGDIR}", "${EOS_CACHE_DIR}", "${EOS_OUTPUT_DIR}", "${EOS_CONFIG_DIR}"] VOLUME ["${MPLCONFIGDIR}", "${EOS_CACHE_DIR}", "${EOS_OUTPUT_DIR}", "${EOS_CONFIG_DIR}"]

View File

@@ -17,8 +17,8 @@ help:
@echo " docker-build - Rebuild docker image" @echo " docker-build - Rebuild docker image"
@echo " docs - Generate HTML documentation (in build/docs/html/)." @echo " docs - Generate HTML documentation (in build/docs/html/)."
@echo " read-docs - Read HTML documentation in your browser." @echo " read-docs - Read HTML documentation in your browser."
@echo " gen-docs - Generate openapi.json and docs/_generated/*."" @echo " gen-docs - Generate openapi.json and docs/_generated/*."
@echo " clean-docs - Remove generated documentation."" @echo " clean-docs - Remove generated documentation."
@echo " run - Run EOS production server in the virtual environment." @echo " run - Run EOS production server in the virtual environment."
@echo " run-dev - Run EOS development server in the virtual environment (automatically reloads)." @echo " run-dev - Run EOS development server in the virtual environment (automatically reloads)."
@echo " dist - Create distribution (in dist/)." @echo " dist - Create distribution (in dist/)."

View File

@@ -8,9 +8,19 @@ Documentation can be found at [Akkudoktor-EOS](https://akkudoktor-eos.readthedoc
See [CONTRIBUTING.md](CONTRIBUTING.md). See [CONTRIBUTING.md](CONTRIBUTING.md).
## System requirements
- Python >= 3.11, < 3.13
- Architecture: amd64, aarch64 (armv8)
- OS: Linux, Windows, macOS
Note: For Python 3.13 some dependencies (e.g. [Pendulum](https://github.com/python-pendulum/Pendulum)) are not yet available on https://pypi.org and have to be manually compiled (a recent [Rust](https://www.rust-lang.org/tools/install) installation is required).
Other architectures (e.g. armv6, armv7) are unsupported for now, because a multitude of dependencies are not available on https://piwheels.org and have to be built manually (a recent Rust installation and [GCC](https://gcc.gnu.org/) are required, Python 3.11 is recommended).
## Installation ## Installation
The project requires Python 3.10 or newer. Official docker images can be found at [akkudoktor/eos](https://hub.docker.com/r/akkudoktor/eos). Docker images (amd64/aarch64) can be found at [akkudoktor/eos](https://hub.docker.com/r/akkudoktor/eos).
Following sections describe how to locally start the EOS server on `http://localhost:8503`. Following sections describe how to locally start the EOS server on `http://localhost:8503`.
@@ -23,6 +33,7 @@ Linux:
```bash ```bash
python -m venv .venv python -m venv .venv
.venv/bin/pip install -r requirements.txt .venv/bin/pip install -r requirements.txt
.venv/bin/pip install -e .
``` ```
Windows: Windows:
@@ -30,9 +41,10 @@ Windows:
```cmd ```cmd
python -m venv .venv python -m venv .venv
.venv\Scripts\pip install -r requirements.txt .venv\Scripts\pip install -r requirements.txt
.venv\Scripts\pip install -e .
``` ```
Finally, start the EOS server: Finally, start the EOS server to access it at `http://localhost:8503` (API docs at `http://localhost:8503/docs`):
Linux: Linux:
@@ -48,6 +60,8 @@ Windows:
### Docker ### Docker
Start EOS with following command to access it at `http://localhost:8503` (API docs at `http://localhost:8503/docs`):
```bash ```bash
docker compose up docker compose up
``` ```

View File

@@ -17,6 +17,8 @@ services:
- longitude=13.4 - longitude=13.4
- elecprice_provider=ElecPriceAkkudoktor - elecprice_provider=ElecPriceAkkudoktor
- elecprice_charges_kwh=0.21 - elecprice_charges_kwh=0.21
- server_fasthtml_host=none - EOS_SERVER__EOSDASH_SESSKEY=s3cr3t
ports: ports:
- "${EOS_PORT}:${EOS_PORT}" # Configure what ports to expose on host
- "${EOS_PORT}:8503"
- "${EOSDASH_PORT}:8504"

BIN
docs/_static/introduction/integration.png vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
docs/_static/introduction/introduction.png vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/_static/introduction/overview.png vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

File diff suppressed because one or more lines are too long

BIN
docs/_static/optimization_timeframes.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 664 KiB

View File

@@ -1,9 +0,0 @@
% SPDX-License-Identifier: Apache-2.0
# About Akkudoktor EOS
The Energy System Simulation and Optimization System (EOS) provides a comprehensive solution for
simulating and optimizing an energy system based on renewable energy sources. With a focus on
photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements),
heat pumps, electric vehicles, and consideration of electricity price data, this system enables
forecasting and optimization of energy flow and costs over a specified period.

View File

@@ -20,17 +20,22 @@ EOS Architecture
### Configuration ### Configuration
The configuration controls all aspects of EOS: optimization, prediction, measurement, and energy management. The configuration controls all aspects of EOS: optimization, prediction, measurement, and energy
management.
### Energy Management ### Energy Management
Energy management is the overall process to provide planning data for scheduling the different devices in your system in an optimal way. Energy management cares for the update of predictions and the optimization of the planning based on the simulated behavior of the devices. The planning is on the hour. Sub-hour energy management is left Energy management is the overall process to provide planning data for scheduling the different
devices in your system in an optimal way. Energy management cares for the update of predictions and
the optimization of the planning based on the simulated behavior of the devices. The planning is on
the hour. Sub-hour energy management is left
### Optimization ### Optimization
### Device Simulations ### Device Simulations
Device simulations simulate devices' behavior based on internal logic and predicted data. They provide the data needed for optimization. Device simulations simulate devices' behavior based on internal logic and predicted data. They
provide the data needed for optimization.
### Predictions ### Predictions
@@ -38,7 +43,8 @@ Predictions provide predicted future data to be used by the optimization.
### Measurements ### Measurements
Measurements are utilized to refine predictions using real data from your system, thereby enhancing accuracy. Measurements are utilized to refine predictions using real data from your system, thereby enhancing
accuracy.
### EOS Server ### EOS Server

View File

@@ -31,10 +31,10 @@ Use endpoint `POST /v1/config/update` to update the configuration from the `EOS
The configuration sources and their priorities are as follows: The configuration sources and their priorities are as follows:
1. **Settings**: Provided during runtime by the REST interface 1. `Settings`: Provided during runtime by the REST interface
2. **Environment Variables**: Defined at startup of the REST server and during runtime 2. `Environment Variables`: Defined at startup of the REST server and during runtime
3. **EOS Configuration File**: Read at startup of the REST server and on request 3. `EOS Configuration File`: Read at startup of the REST server and on request
4. **Default Values** 4. `Default Values`
### Settings ### Settings

View File

@@ -1,4 +1,5 @@
% SPDX-License-Identifier: Apache-2.0 % SPDX-License-Identifier: Apache-2.0
(integration-page)=
# Integration # Integration
@@ -17,18 +18,19 @@ APIs, and online services in creative and practical ways.
Andreas Schmitz uses [Node-RED](https://nodered.org/) as part of his home automation setup. Andreas Schmitz uses [Node-RED](https://nodered.org/) as part of his home automation setup.
### Resources ### Node-Red Resources
- [Installation Guide (German)](https://meintechblog.de/2024/09/05/andreas-schmitz-joerg-installiert-mein-energieoptimierungssystem/) — A detailed guide on integrating an early version of EOS with - [Installation Guide (German)](https://meintechblog.de/2024/09/05/andreas-schmitz-joerg-installiert-mein-energieoptimierungssystem/)
`Node-RED`. \— A detailed guide on integrating an early version of EOS with `Node-RED`.
## Home Assistant ## Home Assistant
[Home Assistant](https://www.home-assistant.io/) is an open-source home automation platform that [Home Assistant](https://www.home-assistant.io/) is an open-source home automation platform that
emphasizes local control and user privacy. emphasizes local control and user privacy.
### Resources (duetting-solution)=
### Home Assistant Resources
- Duetting's [EOS Home Assistant Addon](https://github.com/Duetting/ha_eos_addon) — Additional - Duetting's [EOS Home Assistant Addon](https://github.com/Duetting/ha_eos_addon) — Additional
details can be found in this details can be found in this [discussion thread](https://github.com/Akkudoktor-EOS/EOS/discussions/294).
[discussion thread](https://github.com/Akkudoktor-EOS/EOS/discussions/294).

View File

@@ -0,0 +1,180 @@
% SPDX-License-Identifier: Apache-2.0
# Introduction
The Energy System Simulation and Optimization System (EOS) provides a comprehensive
solution for simulating and optimizing an energy system based on renewable energy
sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load
management (consumer requirements), heat pumps, electric vehicles, and consideration of
electricity price data, this system enables forecasting and optimization of energy flow
and costs over a specified period.
After successfully installing a PV system with or without battery storage, most owners
first priority is often to charge the electric car with surplus energy in order to use
the electricity generated by the PV system cost-effectively for electromobility.
After initial experiences, the desire to include battery storage and dynamic electricity
prices in the solution soon arises. The market already offers various commercial and
non-commercial solutions for this, such as the popular open source hardware and software
solutions evcc or openWB.
Some solutions take into account the current values of the system such as PV power
output, battery storage charge level or the current electricity price to decide whether
to charge the electric car with PV surplus or from the grid (e.g. openWB), some use
historical consumption values and PV forecast data for their calculations, but leave out
the current electricity prices and charging the battery storage from the power grid
(Predbat). Others are specialiced on working in combination with a specific smart home
solution (e.g. emhass). Still others focus on certain consumers, such as the electric car,
or are currently working on integrating the forecast values (evcc). And some are commercial
devices that require an electrician to install them and expect a certain ecosystem
(e.g. Sunny Home Manager).
The Akkudoktor EOS
- takes into account historical, current and forecast data such as consumption values, PV
forecast data, electricity price forecast, battery storage and electric car charge levels
- the simulation also takes into account the possibility of charging the battery storage
from the grid at low electricity prices
- is not limited to certain consumers, but includes electric cars, heat pumps or more
powerful consumers such as tumble dryers
- is independent of a specific smart home solution and can also be integrated into
self-developed solutions if desired
- is a free and independent open source software solution
![Introdution](../_static/introduction/introduction.png)
The challenge is to charge (electric car) or start the consumers (washing machine, dryer)
at the right time and to do so as cost-efficiently as possible. If PV yield forecast,
battery storage and dynamic electricity price forecasts are included in the calculation,
the possibilities increase, but unfortunately so does the complexity.
The Akkudoktor EOS addresses this challenge by simulating energy flows in the household
based on target values, forecast data and current operating data over a 48-hour
observation period, running through a large number of different scenarios and finally
providing a cost-optimized plan for the current day controlling the relevant consumers.
## Prerequisites
- Technical requirements
- Input data
### Technical requirements
- reasonably fast computer on which EOS is installed
- controllable energy system consisting of photovoltaic system, solar battery storage,
energy intensive consumers that must provide the appropriate interfaces
- integration solution for integrating the energy system and EOS
### Input Data
![Overview](../_static/introduction/overview.png)
The EOS requires various types of data for the simulation:
Forecast data
- PV yield forecast
- Expected household consumption
- Electricity price forecast
- Forecast temperature trend (if heatpump is used)
Basic data and current operating data
- Current charge level of the battery storage
- Value of electricity in the battery storage
- Current charge level of the electric car
- Energy consumption and running time of dishwasher, washing machine and tumble dryer
Target values
- Charge level the electric car should reach in the next few hours
- Consumers to run in the next few hours
There are various service providers available for PV forecasting that calculate forecast
data for a PV system based on the various influencing factors, such as system size,
orientation, location, time of year and weather conditions. EOS also offers a
[PV forecasting service](#prediction-page) which can be used. This service uses
public data in the background.
For the forecast of household consumption EOS provides a standard load curve for an
average day based on annual household consumption that you can fetch via API. This data
was compiled based on data from several households and provides an initial usable basis.
Alternatively your own collected historical data could be used to reflect your personal
consumption behaviour.
## Simulation Results
Based on the input data, the EOS uses a genetic algorithm to create a cost-optimized
schedule for the coming hours from numerous simulations of the overall system.
The plan created contains for each of the coming hours
- Control information
- whether and with what power the battery storage should be charged from the grid
- when the battery storage should be charged via the PV system
- whether discharging the battery storage is permitted or not
- when and with what power the electric car should be charged
- when a household appliance should be activated
- Energy history information
- Total load of the house
- Grid consumption
- Feed-in
- Load of the planned household appliances
- Charge level of the battery storage
- Charge level of the electric car
- Active losses
- Cost information
- Revenue per hour (when fed into the grid)
- Total costs per hour (when drawn from the grid)
- Overall balance (revenue-costs)
- Cost development
If required, the simulation result can also be created and downloaded in graphical
form as a PDF from EOS.
## Integration
The Akkudoktor EOS can be integrated into a wide variety of systems with a variety
of components.
![Integration](../_static/introduction/integration.png)
However, the components are not integrated by the EOS itself, but must be intergrated by
the user using an integration solution and currently requires some effort and technical
know-how.
Any [integration](#integration-page) solution that can act as an intermediary between the
components and the REST API of EOS can be used. One possible solution that enables the
integration of components and EOS is Node-RED. Another solution could be Home Assistant
usings its built in features.
Access to the data and functions of the components can be done in a variety of ways.
Node-RED offers a large number of types of nodes that allow access via the protocols
commonly used in this area, such as Modbus or MQTT. Access to any existing databases,
such as InfluxDB or PostgreSQL, is also possible via nodes provided by Node-RED.
It becomes easier if a smart home solution like Homa Assistant, openHAB or ioBroker or
solutions such as evcc or openWB are already in use. In this case, these smart home
solutions already take over the technical integration and communication with the components
at a technical level and Node-RED offers nodes for accessing these solutions, so that the
corresponding sources can be easily integrated into a flow.
In Home Assistant you could use an automation to prepare the input payload for EOS and
then use the RESTful integration to call EOS. Based on this concept there is already a
home assistand add-on created by [Duetting](#duetting-solution).
The plan created by EOS must also be executed via the chosen integration solution,
with the respective devices receiving their instructions according to the plan.
## Limitations
The plan calculated by EOS is cost-optimized due to the genetic algorithm used, but not
necessarily cost-optimal, since genetic algorithms do not always find the global optimum,
but usually find good local optima very quickly in a large solution space.
## Links
- [German Video explaining the basic concept and installation process for the early version of EOS (YouTube)](https://www.youtube.com/live/ftQULW4-1ts?si=oDdBBifCpUmiCXaY)
- [German Forum of Akkudoktor EOS](https://akkudoktor.net/c/der-akkudoktor/eos)
- [Akkudoktor-EOS GitHub Repository](https://github.com/Akkudoktor-EOS/EOS)
- [Latest EOS Documentation](https://akkudoktor-eos.readthedocs.io/en/latest/)

View File

@@ -5,9 +5,9 @@
Measurements are utilized to refine predictions using real data from your system, thereby enhancing Measurements are utilized to refine predictions using real data from your system, thereby enhancing
accuracy. accuracy.
- **Household Load Measurement** - Household Load Measurement
- **Grid Export Measurement** - Grid Export Measurement
- **Grid Import Measurement** - Grid Import Measurement
## Storing Measurements ## Storing Measurements

View File

@@ -2,7 +2,199 @@
# Optimization # Optimization
:::{admonition} Todo ## Introduction
:class: note
Describe optimization. The `POST /optimize` API endpoint optimizes your energy management system based on various inputs
::: including electricity prices, battery storage capacity, PV forecast, and temperature data.
## Input Payload
### Sample Request
```json
{
"ems": {
"preis_euro_pro_wh_akku": 0.0007,
"einspeiseverguetung_euro_pro_wh": 0.00007,
"gesamtlast": [500, 500, ..., 500, 500],
"pv_prognose_wh": [300, 0, 0, ..., 2160, 1840],
"strompreis_euro_pro_wh": [0.0003784, 0.0003868, ..., 0.00034102, 0.00033709]
},
"pv_akku": {
"capacity_wh": 12000,
"charging_efficiency": 0.92,
"discharging_efficiency": 0.92,
"max_charge_power_w": 5700,
"initial_soc_percentage": 66,
"min_soc_percentage": 5,
"max_soc_percentage": 100
},
"inverter": {
"max_power_wh": 15500
},
"eauto": {
"capacity_wh": 64000,
"charging_efficiency": 0.88,
"discharging_efficiency": 0.88,
"max_charge_power_w": 11040,
"initial_soc_percentage": 98,
"min_soc_percentage": 60,
"max_soc_percentage": 100
},
"temperature_forecast": [18.3, 18, ..., 20.16, 19.84],
"start_solution": null
}
```
## Input Parameters
### Energy Management System (EMS)
#### Battery Cost (`preis_euro_pro_wh_akku`)
- Unit: €/Wh
- Purpose: Represents the residual value of energy stored in the battery
- Impact: Lower values encourage battery depletion, higher values preserve charge at the end of the simulation.
#### Feed-in Tariff (`einspeiseverguetung_euro_pro_wh`)
- Unit: €/Wh
- Purpose: Compensation received for feeding excess energy back to the grid
#### Total Load Forecast (`gesamtlast`)
- Unit: W
- Time Range: 48 hours (00:00 today to 23:00 tomorrow)
- Format: Array of hourly values
- Note: Exclude optimizable loads (EV charging, battery charging, etc.)
##### Data Sources
1. Standard Load Profile: `GET /v1/prediction/list?key=load_mean` for a standard load profile based
on your yearly consumption.
2. Adjusted Load Profile: `GET /v1/prediction/list?key=load_mean_adjusted` for a combination of a
standard load profile based on your yearly consumption incl. data from last 48h.
#### PV Generation Forecast (`pv_prognose_wh`)
- Unit: W
- Time Range: 48 hours (00:00 today to 23:00 tomorrow)
- Format: Array of hourly values
- Data Source: `GET /v1/prediction/series?key=pvforecast_ac_power`
#### Electricity Price Forecast (`strompreis_euro_pro_wh`)
- Unit: €/Wh
- Time Range: 48 hours (00:00 today to 23:00 tomorrow)
- Format: Array of hourly values
- Data Source: `GET /v1/prediction/list?key=elecprice_marketprice_wh`
Verify prices against your local tariffs.
### Battery Storage System
#### Configuration
- `capacity_wh`: Total battery capacity in Wh
- `charging_efficiency`: Charging efficiency (0-1)
- `discharging_efficiency`: Discharging efficiency (0-1)
- `max_charge_power_w`: Maximum charging power in W
#### State of Charge (SoC)
- `initial_soc_percentage`: Current battery level (%)
- `min_soc_percentage`: Minimum allowed SoC (%)
- `max_soc_percentage`: Maximum allowed SoC (%)
### Inverter
- `max_power_wh`: Maximum inverter power in Wh
### Electric Vehicle (EV)
- `capacity_wh`: Battery capacity in Wh
- `charging_efficiency`: Charging efficiency (0-1)
- `discharging_efficiency`: Discharging efficiency (0-1)
- `max_charge_power_w`: Maximum charging power in W
- `initial_soc_percentage`: Current charge level (%)
- `min_soc_percentage`: Minimum allowed SoC (%)
- `max_soc_percentage`: Maximum allowed SoC (%)
### Temperature Forecast
- Unit: °C
- Time Range: 48 hours (00:00 today to 23:00 tomorrow)
- Format: Array of hourly values
- Data Source: `GET /v1/prediction/list?key=weather_temp_air`
## Output Format
### Sample Response
```json
{
"ac_charge": [0.625, 0, ..., 0.75, 0],
"dc_charge": [1, 1, ..., 1, 1],
"discharge_allowed": [0, 0, 1, ..., 0, 0],
"eautocharge_hours_float": [0.625, 0, ..., 0.75, 0],
"result": {
"Last_Wh_pro_Stunde": [...],
"EAuto_SoC_pro_Stunde": [...],
"Einnahmen_Euro_pro_Stunde": [...],
"Gesamt_Verluste": 1514.96,
"Gesamtbilanz_Euro": 2.51,
"Gesamteinnahmen_Euro": 2.88,
"Gesamtkosten_Euro": 5.39,
"akku_soc_pro_stunde": [...]
}
}
```
### Output Parameters
#### Battery Control
- `ac_charge`: Grid charging schedule (0-1)
- `dc_charge`: DC charging schedule (0-1)
- `discharge_allowed`: Discharge permission (0 or 1)
0 (no charge)
1 (charge with full load)
`ac_charge` multiplied by the maximum charge power of the battery results in the planned charging power.
#### EV Charging
- `eautocharge_hours_float`: EV charging schedule (0-1)
#### Results
The `result` object contains detailed information about the optimization outcome.
The length of the array is between 25 and 48 and starts at the current hour and ends at 23:00 tomorrow.
- `Last_Wh_pro_Stunde`: Array of hourly load values in Wh
- Shows the total energy consumption per hour
- Includes household load, battery charging/discharging, and EV charging
- `EAuto_SoC_pro_Stunde`: Array of hourly EV state of charge values (%)
- Shows the projected EV battery level throughout the optimization period
- `Einnahmen_Euro_pro_Stunde`: Array of hourly revenue values in Euro
- `Gesamt_Verluste`: Total energy losses in Wh
- `Gesamtbilanz_Euro`: Overall financial balance in Euro
- `Gesamteinnahmen_Euro`: Total revenue in Euro
- `Gesamtkosten_Euro`: Total costs in Euro
- `akku_soc_pro_stunde`: Array of hourly battery state of charge values (%)
## Timeframe overview
```{figure} ../_static/optimization_timeframes.png
:alt: Timeframe Overview
Timeframe Overview
```

View File

@@ -1,14 +1,15 @@
% SPDX-License-Identifier: Apache-2.0 % SPDX-License-Identifier: Apache-2.0
(prediction-page)=
# Predictions # Predictions
Predictions, along with simulations and measurements, form the foundation upon which energy Predictions, along with simulations and measurements, form the foundation upon which energy
optimization is executed. In EOS, a standard set of predictions is managed, including: optimization is executed. In EOS, a standard set of predictions is managed, including:
- **Household Load Prediction** - Household Load Prediction
- **Electricity Price Prediction** - Electricity Price Prediction
- **PV Power Prediction** - PV Power Prediction
- **Weather Prediction** - Weather Prediction
## Storing Predictions ## Storing Predictions
@@ -56,13 +57,15 @@ A dictionary with the following structure:
#### 2. DateTimeDataFrame #### 2. DateTimeDataFrame
A JSON string created from a [pandas](https://pandas.pydata.org/docs/index.html) dataframe with a A JSON string created from a [pandas](https://pandas.pydata.org/docs/index.html) dataframe with a
`DatetimeIndex`. Use [pandas.DataFrame.to_json(orient="index")](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_json.html#pandas.DataFrame.to_json). `DatetimeIndex`. Use
[pandas.DataFrame.to_json(orient="index")](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_json.html#pandas.DataFrame.to_json).
The column name of the data must be the same as the names of the `prediction key`s. The column name of the data must be the same as the names of the `prediction key`s.
#### 3. DateTimeSeries #### 3. DateTimeSeries
A JSON string created from a [pandas](https://pandas.pydata.org/docs/index.html) series with a A JSON string created from a [pandas](https://pandas.pydata.org/docs/index.html) series with a
`DatetimeIndex`. Use [pandas.Series.to_json(orient="index")](https://pandas.pydata.org/docs/reference/api/pandas.Series.to_json.html#pandas.Series.to_json). `DatetimeIndex`. Use
[pandas.Series.to_json(orient="index")](https://pandas.pydata.org/docs/reference/api/pandas.Series.to_json.html#pandas.Series.to_json).
## Adjusted Predictions ## Adjusted Predictions
@@ -196,13 +199,20 @@ Configuration options:
- `latitude`: Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)" - `latitude`: Latitude in decimal degrees, between -90 and 90, north is positive (ISO 19115) (°)"
- `longitude`: Longitude in decimal degrees, within -180 to 180 (°) - `longitude`: Longitude in decimal degrees, within -180 to 180 (°)
- `pvforecast<0..5>_surface_tilt`: Tilt angle from horizontal plane. Ignored for two-axis tracking. - `pvforecast<0..5>_surface_tilt`: Tilt angle from horizontal plane. Ignored for two-axis tracking.
- `pvforecast<0..5>_surface_azimuth`: Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). - `pvforecast<0..5>_surface_azimuth`: Orientation (azimuth angle) of the (fixed) plane.
Clockwise from north (north=0, east=90, south=180, west=270).
- `pvforecast<0..5>_userhorizon`: Elevation of horizon in degrees, at equally spaced azimuth clockwise from north. - `pvforecast<0..5>_userhorizon`: Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.
- `pvforecast<0..5>_peakpower`: Nominal power of PV system in kW. - `pvforecast<0..5>_peakpower`: Nominal power of PV system in kW.
- `pvforecast<0..5>_pvtechchoice`: PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'. - `pvforecast<0..5>_pvtechchoice`: PV technology. One of 'crystSi', 'CIS', 'CdTe', 'Unknown'.
- `pvforecast<0..5>_mountingplace`: Type of mounting for PV system. Options are 'free' for free-standing and 'building' for building-integrated. - `pvforecast<0..5>_mountingplace`: Type of mounting for PV system. Options are 'free' for free-standing
and 'building' for building-integrated.
- `pvforecast<0..5>_loss`: Sum of PV system losses in percent - `pvforecast<0..5>_loss`: Sum of PV system losses in percent
- `pvforecast<0..5>_trackingtype`: Type of suntracking. 0=fixed, 1=single horizontal axis aligned north-south, 2=two-axis tracking, 3=vertical axis tracking, 4=single horizontal axis aligned east-west, 5=single inclined axis aligned north-south. - `pvforecast<0..5>_trackingtype`: Type of suntracking. 0=fixed,
1=single horizontal axis aligned north-south,
2=two-axis tracking,
3=vertical axis tracking,
4=single horizontal axis aligned east-west,
5=single inclined axis aligned north-south.
- `pvforecast<0..5>_optimal_surface_tilt`: Calculate the optimum tilt angle. Ignored for two-axis tracking. - `pvforecast<0..5>_optimal_surface_tilt`: Calculate the optimum tilt angle. Ignored for two-axis tracking.
- `pvforecast<0..5>_optimalangles`: Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking. - `pvforecast<0..5>_optimalangles`: Calculate the optimum tilt and azimuth angles. Ignored for two-axis tracking.
- `pvforecast<0..5>_albedo`: Proportion of the light hitting the ground that it reflects back. - `pvforecast<0..5>_albedo`: Proportion of the light hitting the ground that it reflects back.
@@ -214,39 +224,76 @@ Configuration options:
- `pvforecastimport_file_path`: Path to the file to import PV forecast data from. - `pvforecastimport_file_path`: Path to the file to import PV forecast data from.
- `pvforecastimport_json`: JSON string, dictionary of PV forecast value lists. - `pvforecastimport_json`: JSON string, dictionary of PV forecast value lists.
------ ---
Some of the configuration options directly follow the [PVGIS](https://joint-research-centre.ec.europa.eu/photovoltaic-geographical-information-system-pvgis/getting-started-pvgis/pvgis-user-manual_en) nomenclature. Some of the configuration options directly follow the
[PVGIS](https://joint-research-centre.ec.europa.eu/photovoltaic-geographical-information-system-pvgis/getting-started-pvgis/pvgis-user-manual_en)
nomenclature.
Detailed definitions taken from **PVGIS**: Detailed definitions taken from **PVGIS**:
- `pvforecast<0..5>_pvtechchoice` - `pvforecast<0..5>_pvtechchoice`
The performance of PV modules depends on the temperature and on the solar irradiance, but the exact dependence varies between different types of PV modules. At the moment we can estimate the losses due to temperature and irradiance effects for the following types of modules: crystalline silicon cells; thin film modules made from CIS or CIGS and thin film modules made from Cadmium Telluride (CdTe). The performance of PV modules depends on the temperature and on the solar irradiance, but the exact
dependence varies between different types of PV modules. At the moment we can estimate the losses
due to temperature and irradiance effects for the following types of modules: crystalline silicon
cells; thin film modules made from CIS or CIGS and thin film modules made from Cadmium Telluride
(CdTe).
For other technologies (especially various amorphous technologies), this correction cannot be calculated here. If you choose one of the first three options here the calculation of performance will take into account the temperature dependence of the performance of the chosen technology. If you choose the other option (other/unknown), the calculation will assume a loss of 8% of power due to temperature effects (a generic value which has found to be reasonable for temperate climates). For other technologies (especially various amorphous technologies), this correction cannot be
calculated here. If you choose one of the first three options here the calculation of performance
will take into account the temperature dependence of the performance of the chosen technology. If
you choose the other option (other/unknown), the calculation will assume a loss of 8% of power due
to temperature effects (a generic value which has found to be reasonable for temperate climates).
PV power output also depends on the spectrum of the solar radiation. PVGIS can calculate how the variations of the spectrum of sunlight affects the overall energy production from a PV system. At the moment this calculation can be done for crystalline silicon and CdTe modules. Note that this calculation is not yet available when using the NSRDB solar radiation database. PV power output also depends on the spectrum of the solar radiation. PVGIS can calculate how the
variations of the spectrum of sunlight affects the overall energy production from a PV system. At
the moment this calculation can be done for crystalline silicon and CdTe modules. Note that this
calculation is not yet available when using the NSRDB solar radiation database.
- `pvforecast<0..5>_peakpower` - `pvforecast<0..5>_peakpower`
This is the power that the manufacturer declares that the PV array can produce under standard test conditions (STC), which are a constant 1000W of solar irradiation per square meter in the plane of the array, at an array temperature of 25°C. The peak power should be entered in kilowatt-peak (kWp). If you do not know the declared peak power of your modules but instead know the area of the modules and the declared conversion efficiency (in percent), you can calculate the peak power as power = area * efficiency / 100. This is the power that the manufacturer declares that the PV array can produce under standard test
conditions (STC), which are a constant 1000W of solar irradiation per square meter in the plane of
the array, at an array temperature of 25°C. The peak power should be entered in kilowatt-peak (kWp).
If you do not know the declared peak power of your modules but instead know the area of the modules
and the declared conversion efficiency (in percent), you can calculate the peak power as
power = area \* efficiency / 100.
Bifacial modules: PVGIS doesn't make specific calculations for bifacial modules at present. Users who wish to explore the possible benefits of this technology can input the power value for Bifacial Nameplate Irradiance. This can also be can also be estimated from the front side peak power P_STC value and the bifaciality factor, φ (if reported in the module data sheet) as: P_BNPI = P_STC * (1 + φ * 0.135). NB this bifacial approach is not appropriate for BAPV or BIPV installations or for modules mounting on a N-S axis i.e. facing E-W. Bifacial modules: PVGIS doesn't make specific calculations for bifacial modules at present. Users
who wish to explore the possible benefits of this technology can input the power value for Bifacial
Nameplate Irradiance. This can also be can also be estimated from the front side peak power P_STC
value and the bifaciality factor, φ (if reported in the module data sheet) as:
P_BNPI = P_STC \* (1 + φ \* 0.135). NB this bifacial approach is not appropriate for BAPV or BIPV
installations or for modules mounting on a N-S axis i.e. facing E-W.
- `pvforecast<0..5>_loss` - `pvforecast<0..5>_loss`
The estimated system losses are all the losses in the system, which cause the power actually delivered to the electricity grid to be lower than the power produced by the PV modules. There are several causes for this loss, such as losses in cables, power inverters, dirt (sometimes snow) on the modules and so on. Over the years the modules also tend to lose a bit of their power, so the average yearly output over the lifetime of the system will be a few percent lower than the output in the first years. The estimated system losses are all the losses in the system, which cause the power actually
delivered to the electricity grid to be lower than the power produced by the PV modules. There are
several causes for this loss, such as losses in cables, power inverters, dirt (sometimes snow) on
the modules and so on. Over the years the modules also tend to lose a bit of their power, so the
average yearly output over the lifetime of the system will be a few percent lower than the output
in the first years.
We have given a default value of 14% for the overall losses. If you have a good idea that your value will be different (maybe due to a really high-efficiency inverter) you may reduce this value a little. We have given a default value of 14% for the overall losses. If you have a good idea that your value
will be different (maybe due to a really high-efficiency inverter) you may reduce this value a little.
- `pvforecast<0..5>_mountingplace` - `pvforecast<0..5>_mountingplace`
For fixed (non-tracking) systems, the way the modules are mounted will have an influence on the temperature of the module, which in turn affects the efficiency. Experiments have shown that if the movement of air behind the modules is restricted, the modules can get considerably hotter (up to 15°C at 1000W/m2 of sunlight). For fixed (non-tracking) systems, the way the modules are mounted will have an influence on the
temperature of the module, which in turn affects the efficiency. Experiments have shown that if the
movement of air behind the modules is restricted, the modules can get considerably hotter
(up to 15°C at 1000W/m2 of sunlight).
In PVGIS there are two possibilities: free-standing, meaning that the modules are mounted on a rack with air flowing freely behind the modules; and building- integrated, which means that the modules are completely built into the structure of the wall or roof of a building, with no air movement behind the modules. In PVGIS there are two possibilities: free-standing, meaning that the modules are mounted on a rack
with air flowing freely behind the modules; and building- integrated, which means that the modules
are completely built into the structure of the wall or roof of a building, with no air movement
behind the modules.
Some types of mounting are in between these two extremes, for instance if the modules are mounted on a roof with curved roof tiles, allowing air to move behind the modules. In such cases, the performance will be somewhere between the results of the two calculations that are possible here. Some types of mounting are in between these two extremes, for instance if the modules are mounted on
a roof with curved roof tiles, allowing air to move behind the modules. In such cases, the
performance will be somewhere between the results of the two calculations that are possible here.
- `pvforecast<0..5>_userhorizon` - `pvforecast<0..5>_userhorizon`
@@ -258,9 +305,10 @@ represent equal angular distance around the horizon. For instance, if you have 3
point is due north, the next is 10 degrees east of north, and so on, until the last point, 10 point is due north, the next is 10 degrees east of north, and so on, until the last point, 10
degrees west of north. degrees west of north.
------ ---
Most of the configuration options are in line with the [PVLib](https://pvlib-python.readthedocs.io/en/stable/_modules/pvlib/iotools/pvgis.html) definition for PVGIS data. Most of the configuration options are in line with the
[PVLib](https://pvlib-python.readthedocs.io/en/stable/_modules/pvlib/iotools/pvgis.html) definition for PVGIS data.
Detailed definitions from **PVLib** for PVGIS data. Detailed definitions from **PVLib** for PVGIS data.
@@ -273,7 +321,7 @@ Tilt angle from horizontal plane.
Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180,
west=270). This is offset 180 degrees from the convention used by PVGIS. west=270). This is offset 180 degrees from the convention used by PVGIS.
------ ---
### PVForecastAkkudoktor Provider ### PVForecastAkkudoktor Provider
@@ -288,7 +336,8 @@ The following general configuration options of the PV system must be set:
For each plane `<0..5>` of the PV system the following configuration options must be set: For each plane `<0..5>` of the PV system the following configuration options must be set:
- `pvforecast<0..5>_surface_tilt`: Tilt angle from horizontal plane. Ignored for two-axis tracking. - `pvforecast<0..5>_surface_tilt`: Tilt angle from horizontal plane. Ignored for two-axis tracking.
- `pvforecast<0..5>_surface_azimuth`: Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). - `pvforecast<0..5>_surface_azimuth`: Orientation (azimuth angle) of the (fixed) plane.
Clockwise from north (north=0, east=90, south=180, west=270).
- `pvforecast<0..5>_userhorizon`: Elevation of horizon in degrees, at equally spaced azimuth clockwise from north. - `pvforecast<0..5>_userhorizon`: Elevation of horizon in degrees, at equally spaced azimuth clockwise from north.
- `pvforecast<0..5>_inverter_paco`: AC power rating of the inverter. [W] - `pvforecast<0..5>_inverter_paco`: AC power rating of the inverter. [W]
- `pvforecast<0..5>_peakpower`: Nominal power of PV system in kW. - `pvforecast<0..5>_peakpower`: Nominal power of PV system in kW.
@@ -370,8 +419,8 @@ Configuration options:
- `weather_provider`: Load provider id of provider to be used. - `weather_provider`: Load provider id of provider to be used.
- `BrightSky`: Retrieves from https://api.brightsky.dev. - `BrightSky`: Retrieves from [BrightSky](https://api.brightsky.dev).
- `ClearOutside`: Retrieves from https://clearoutside.com/forecast. - `ClearOutside`: Retrieves from [ClearOutside](https://clearoutside.com/forecast).
- `LoadImport`: Imports from a file or JSON string. - `LoadImport`: Imports from a file or JSON string.
- `weatherimport_file_path`: Path to the file to import weatherforecast data from. - `weatherimport_file_path`: Path to the file to import weatherforecast data from.

View File

@@ -19,6 +19,7 @@ Install the dependencies in a virtual environment:
python -m venv .venv python -m venv .venv
.venv\Scripts\pip install -r requirements.txt .venv\Scripts\pip install -r requirements.txt
.venv\Scripts\pip install -e .
.. tab:: Linux .. tab:: Linux
@@ -26,6 +27,7 @@ Install the dependencies in a virtual environment:
python -m venv .venv python -m venv .venv
.venv/bin/pip install -r requirements.txt .venv/bin/pip install -r requirements.txt
.venv/bin/pip install -e .
``` ```
@@ -73,37 +75,53 @@ This project uses the `EOS.config.json` file to manage configuration settings.
### Default Configuration ### Default Configuration
A default configuration file `default.config.json` is provided. This file contains all the necessary configuration keys with their default values. A default configuration file `default.config.json` is provided. This file contains all the necessary
configuration keys with their default values.
### Custom Configuration ### Custom Configuration
Users can specify a custom configuration directory by setting the environment variable `EOS_DIR`. Users can specify a custom configuration directory by setting the environment variable `EOS_DIR`.
- If the directory specified by `EOS_DIR` contains an existing `EOS.config.json` file, the application will use this configuration file. - If the directory specified by `EOS_DIR` contains an existing `EOS.config.json` file, the
- If the `EOS.config.json` file does not exist in the specified directory, the `default.config.json` file will be copied to the directory as `EOS.config.json`. application will use this configuration file.
- If the `EOS.config.json` file does not exist in the specified directory, the `default.config.json`
file will be copied to the directory as `EOS.config.json`.
### Configuration Updates ### Configuration Updates
If the configuration keys in the `EOS.config.json` file are missing or different from those in `default.config.json`, they will be automatically updated to match the default settings, ensuring that all required keys are present. If the configuration keys in the `EOS.config.json` file are missing or different from those in
`default.config.json`, they will be automatically updated to match the default settings, ensuring
that all required keys are present.
## Classes and Functionalities ## Classes and Functionalities
This project uses various classes to simulate and optimize the components of an energy system. Each class represents a specific aspect of the system, as described below: This project uses various classes to simulate and optimize the components of an energy system. Each
class represents a specific aspect of the system, as described below:
- `Battery`: Simulates a battery storage system, including capacity, state of charge, and now charge and discharge losses. - `Battery`: Simulates a battery storage system, including capacity, state of charge, and now
charge and discharge losses.
- `PVForecast`: Provides forecast data for photovoltaic generation, based on weather data and historical generation data. - `PVForecast`: Provides forecast data for photovoltaic generation, based on weather data and
historical generation data.
- `Load`: Models the load requirements of a household or business, enabling the prediction of future energy demand. - `Load`: Models the load requirements of a household or business, enabling the prediction of future
energy demand.
- `Heatpump`: Simulates a heat pump, including its energy consumption and efficiency under various operating conditions. - `Heatpump`: Simulates a heat pump, including its energy consumption and efficiency under various
operating conditions.
- `Strompreis`: Provides information on electricity prices, enabling optimization of energy consumption and generation based on tariff information. - `Strompreis`: Provides information on electricity prices, enabling optimization of energy
consumption and generation based on tariff information.
- `EMS`: The Energy Management System (EMS) coordinates the interaction between the various components, performs optimization, and simulates the operation of the entire energy system. - `EMS`: The Energy Management System (EMS) coordinates the interaction between the various
components, performs optimization, and simulates the operation of the entire energy system.
These classes work together to enable a detailed simulation and optimization of the energy system. For each class, specific parameters and settings can be adjusted to test different scenarios and strategies. These classes work together to enable a detailed simulation and optimization of the energy system.
For each class, specific parameters and settings can be adjusted to test different scenarios and
strategies.
### Customization and Extension ### Customization and Extension
Each class is designed to be easily customized and extended to integrate additional functions or improvements. For example, new methods can be added for more accurate modeling of PV system or battery behavior. Developers are invited to modify and extend the system according to their needs. Each class is designed to be easily customized and extended to integrate additional functions or
improvements. For example, new methods can be added for more accurate modeling of PV system or
battery behavior. Developers are invited to modify and extend the system according to their needs.

View File

@@ -8,12 +8,32 @@
```{toctree} ```{toctree}
:maxdepth: 2 :maxdepth: 2
:caption: 'Contents:' :caption: Overview
akkudoktoreos/introduction.md
```
```{toctree}
:maxdepth: 2
:caption: Tutorials
welcome.md
akkudoktoreos/about.md
develop/getting_started.md develop/getting_started.md
```
```{toctree}
:maxdepth: 2
:caption: How-To Guides
develop/CONTRIBUTING.md develop/CONTRIBUTING.md
```
```{toctree}
:maxdepth: 2
:caption: Reference
akkudoktoreos/architecture.md akkudoktoreos/architecture.md
akkudoktoreos/configuration.md akkudoktoreos/configuration.md
akkudoktoreos/optimization.md akkudoktoreos/optimization.md
@@ -22,9 +42,10 @@ akkudoktoreos/measurement.md
akkudoktoreos/integration.md akkudoktoreos/integration.md
akkudoktoreos/serverapi.md akkudoktoreos/serverapi.md
akkudoktoreos/api.rst akkudoktoreos/api.rst
``` ```
# Indices and tables ## Indices and tables
- {ref}`genindex` - {ref}`genindex`
- {ref}`modindex` - {ref}`modindex`

20
docs/pymarkdown.json Normal file
View File

@@ -0,0 +1,20 @@
{
"plugins": {
"md007": {
"enabled": true,
"code_block_line_length" : 160
},
"md013": {
"enabled": true,
"line_length" : 120
},
"md041": {
"enabled": false
}
},
"extensions": {
"front-matter" : {
"enabled" : true
}
}
}

View File

@@ -1,12 +1,12 @@
% SPDX-License-Identifier: Apache-2.0 % SPDX-License-Identifier: Apache-2.0
# Welcome to the EOS documentation! # Welcome to the EOS documentation
This documentation is continuously written. It is edited via text files in the This documentation is continuously written. It is edited via text files in the
[Markdown/ Markedly Structured Text](https://myst-parser.readthedocs.io/en/latest/index.html) [Markdown/ Markedly Structured Text](https://myst-parser.readthedocs.io/en/latest/index.html)
markup language and then compiled into a static website/ offline document using the open source tool markup language and then compiled into a static website/ offline document using the open source tool
[Sphinx](https://www.sphinx-doc.org) and will someday land on [Sphinx](https://www.sphinx-doc.org) and is available on
[Read the Docs](https://akkudoktoreos.readthedocs.io/en/latest/index.html). [Read the Docs](https://akkudoktor-eos.readthedocs.io/en/latest/).
You can contribute to EOS's documentation by opening You can contribute to EOS's documentation by opening
[GitHub issues](https://github.com/Akkudoktor-EOS/EOS/issues) [GitHub issues](https://github.com/Akkudoktor-EOS/EOS/issues)

View File

@@ -7,7 +7,7 @@ authors = [
description = "This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period." description = "This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period."
readme = "README.md" readme = "README.md"
license = {file = "LICENSE"} license = {file = "LICENSE"}
requires-python = ">=3.10" requires-python = ">=3.11"
classifiers = [ classifiers = [
"Development Status :: 3 - Alpha", "Development Status :: 3 - Alpha",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",

View File

@@ -1,14 +1,14 @@
-r requirements.txt -r requirements.txt
gitpython==3.1.44 gitpython==3.1.44
linkify-it-py==2.0.3 linkify-it-py==2.0.3
myst-parser==4.0.0 myst-parser==4.0.1
sphinx==8.1.3 sphinx==8.2.3
sphinx_rtd_theme==3.0.2 sphinx_rtd_theme==3.0.2
sphinx-tabs==3.4.7 sphinx-tabs==3.4.7
pytest==8.3.4 pytest==8.3.5
pytest-cov==6.0.0 pytest-cov==6.0.0
pytest-xprocess==1.0.2 pytest-xprocess==1.0.2
pre-commit pre-commit
mypy==1.13.0 mypy==1.15.0
types-requests==2.32.0.20241016 types-requests==2.32.0.20250306
pandas-stubs==2.2.3.241126 pandas-stubs==2.2.3.250308

View File

@@ -1,16 +1,16 @@
numpy==2.2.2 numpy==2.2.4
numpydantic==1.6.4 numpydantic==1.6.8
matplotlib==3.10.0 matplotlib==3.10.1
fastapi[standard]==0.115.6 fastapi[standard]==0.115.11
python-fasthtml==0.12.0 python-fasthtml==0.12.4
uvicorn==0.34.0 uvicorn==0.34.0
scikit-learn==1.6.1 scikit-learn==1.6.1
timezonefinder==6.5.7 timezonefinder==6.5.8
deap==1.4.2 deap==1.4.2
requests==2.32.3 requests==2.32.3
pandas==2.2.3 pandas==2.2.3
pendulum==3.0.0 pendulum==3.0.0
platformdirs==4.3.6 platformdirs==4.3.7
pvlib==0.11.2 pvlib==0.12.0
pydantic==2.10.5 pydantic==2.10.6
statsmodels==0.14.4 statsmodels==0.14.4

View File

@@ -1,4 +1,4 @@
from typing import Any, ClassVar, Dict, Optional, Union from typing import Any, ClassVar, Optional
import numpy as np import numpy as np
from numpydantic import NDArray, Shape from numpydantic import NDArray, Shape
@@ -186,7 +186,7 @@ class EnergieManagementSystem(SingletonMixin, ConfigMixin, PredictionMixin, Pyda
len(self.load_energy_array), parameters.einspeiseverguetung_euro_pro_wh, float len(self.load_energy_array), parameters.einspeiseverguetung_euro_pro_wh, float
) )
) )
if inverter is not None: if inverter:
self.battery = inverter.battery self.battery = inverter.battery
else: else:
self.battery = None self.battery = None
@@ -198,7 +198,7 @@ class EnergieManagementSystem(SingletonMixin, ConfigMixin, PredictionMixin, Pyda
self.ev_charge_hours = np.full(self.config.prediction_hours, 0.0) self.ev_charge_hours = np.full(self.config.prediction_hours, 0.0)
def set_akku_discharge_hours(self, ds: np.ndarray) -> None: def set_akku_discharge_hours(self, ds: np.ndarray) -> None:
if self.battery is not None: if self.battery:
self.battery.set_discharge_per_hour(ds) self.battery.set_discharge_per_hour(ds)
def set_akku_ac_charge_hours(self, ds: np.ndarray) -> None: def set_akku_ac_charge_hours(self, ds: np.ndarray) -> None:
@@ -211,7 +211,7 @@ class EnergieManagementSystem(SingletonMixin, ConfigMixin, PredictionMixin, Pyda
self.ev_charge_hours = ds self.ev_charge_hours = ds
def set_home_appliance_start(self, ds: int, global_start_hour: int = 0) -> None: def set_home_appliance_start(self, ds: int, global_start_hour: int = 0) -> None:
if self.home_appliance is not None: if self.home_appliance:
self.home_appliance.set_starting_time(ds, global_start_hour=global_start_hour) self.home_appliance.set_starting_time(ds, global_start_hour=global_start_hour)
def reset(self) -> None: def reset(self) -> None:
@@ -276,53 +276,50 @@ class EnergieManagementSystem(SingletonMixin, ConfigMixin, PredictionMixin, Pyda
return self.simulate(start_hour) return self.simulate(start_hour)
def simulate(self, start_hour: int) -> dict[str, Any]: def simulate(self, start_hour: int) -> dict[str, Any]:
"""hour. """Simulate energy usage and costs for the given start hour.
akku_soc_pro_stunde begin of the hour, initial hour state! akku_soc_pro_stunde begin of the hour, initial hour state!
last_wh_pro_stunde integral of last hour (end state) last_wh_pro_stunde integral of last hour (end state)
""" """
# Check for simulation integrity # Check for simulation integrity
missing_data = [] required_attrs = [
"load_energy_array",
if self.load_energy_array is None: "pv_prediction_wh",
missing_data.append("Load Curve") "elect_price_hourly",
if self.pv_prediction_wh is None: "ev_charge_hours",
missing_data.append("PV Forecast") "ac_charge_hours",
if self.elect_price_hourly is None: "dc_charge_hours",
missing_data.append("Electricity Price") "elect_revenue_per_hour_arr",
if self.ev_charge_hours is None: ]
missing_data.append("EV Charge Hours") missing_data = [
if self.ac_charge_hours is None: attr.replace("_", " ").title() for attr in required_attrs if getattr(self, attr) is None
missing_data.append("AC Charge Hours") ]
if self.dc_charge_hours is None:
missing_data.append("DC Charge Hours")
if self.elect_revenue_per_hour_arr is None:
missing_data.append("Feed-in Tariff")
if missing_data: if missing_data:
error_msg = "Mandatory data missing - " + ", ".join(missing_data) logger.error("Mandatory data missing - %s", ", ".join(missing_data))
logger.error(error_msg) raise ValueError(f"Mandatory data missing: {', '.join(missing_data)}")
raise ValueError(error_msg)
else:
# make mypy happy
assert self.load_energy_array is not None
assert self.pv_prediction_wh is not None
assert self.elect_price_hourly is not None
assert self.ev_charge_hours is not None
assert self.ac_charge_hours is not None
assert self.dc_charge_hours is not None
assert self.elect_revenue_per_hour_arr is not None
load_energy_array = self.load_energy_array # Pre-fetch data
load_energy_array = np.array(self.load_energy_array)
pv_prediction_wh = np.array(self.pv_prediction_wh)
elect_price_hourly = np.array(self.elect_price_hourly)
ev_charge_hours = np.array(self.ev_charge_hours)
ac_charge_hours = np.array(self.ac_charge_hours)
dc_charge_hours = np.array(self.dc_charge_hours)
elect_revenue_per_hour_arr = np.array(self.elect_revenue_per_hour_arr)
if not ( # Fetch objects
len(load_energy_array) == len(self.pv_prediction_wh) == len(self.elect_price_hourly) battery = self.battery
): assert battery # to please mypy
error_msg = f"Array sizes do not match: Load Curve = {len(load_energy_array)}, PV Forecast = {len(self.pv_prediction_wh)}, Electricity Price = {len(self.elect_price_hourly)}" ev = self.ev
home_appliance = self.home_appliance
inverter = self.inverter
if not (len(load_energy_array) == len(pv_prediction_wh) == len(elect_price_hourly)):
error_msg = f"Array sizes do not match: Load Curve = {len(load_energy_array)}, PV Forecast = {len(pv_prediction_wh)}, Electricity Price = {len(elect_price_hourly)}"
logger.error(error_msg) logger.error(error_msg)
raise ValueError(error_msg) raise ValueError(error_msg)
# Optimized total hours calculation
end_hour = len(load_energy_array) end_hour = len(load_energy_array)
total_hours = end_hour - start_hour total_hours = end_hour - start_hour
@@ -332,116 +329,110 @@ class EnergieManagementSystem(SingletonMixin, ConfigMixin, PredictionMixin, Pyda
consumption_energy_per_hour = np.full((total_hours), np.nan) consumption_energy_per_hour = np.full((total_hours), np.nan)
costs_per_hour = np.full((total_hours), np.nan) costs_per_hour = np.full((total_hours), np.nan)
revenue_per_hour = np.full((total_hours), np.nan) revenue_per_hour = np.full((total_hours), np.nan)
soc_per_hour = np.full((total_hours), np.nan) # Hour End State soc_per_hour = np.full((total_hours), np.nan)
soc_ev_per_hour = np.full((total_hours), np.nan) soc_ev_per_hour = np.full((total_hours), np.nan)
losses_wh_per_hour = np.full((total_hours), np.nan) losses_wh_per_hour = np.full((total_hours), np.nan)
home_appliance_wh_per_hour = np.full((total_hours), np.nan) home_appliance_wh_per_hour = np.full((total_hours), np.nan)
electricity_price_per_hour = np.full((total_hours), np.nan) electricity_price_per_hour = np.full((total_hours), np.nan)
# Set initial state # Set initial state
if self.battery: soc_per_hour[0] = battery.current_soc_percentage()
soc_per_hour[0] = self.battery.current_soc_percentage() if ev:
if self.ev: soc_ev_per_hour[0] = ev.current_soc_percentage()
soc_ev_per_hour[0] = self.ev.current_soc_percentage()
for hour in range(start_hour, end_hour): for hour in range(start_hour, end_hour):
hour_since_now = hour - start_hour hour_idx = hour - start_hour
# save begin states # save begin states
if self.battery: soc_per_hour[hour_idx] = battery.current_soc_percentage()
soc_per_hour[hour_since_now] = self.battery.current_soc_percentage()
else: if ev:
soc_per_hour[hour_since_now] = 0.0 soc_ev_per_hour[hour_idx] = ev.current_soc_percentage()
if self.ev:
soc_ev_per_hour[hour_since_now] = self.ev.current_soc_percentage()
# Accumulate loads and PV generation # Accumulate loads and PV generation
consumption = self.load_energy_array[hour] consumption = load_energy_array[hour]
losses_wh_per_hour[hour_since_now] = 0.0 losses_wh_per_hour[hour_idx] = 0.0
# Home appliances # Home appliances
if self.home_appliance: if home_appliance:
ha_load = self.home_appliance.get_load_for_hour(hour) ha_load = home_appliance.get_load_for_hour(hour)
consumption += ha_load consumption += ha_load
home_appliance_wh_per_hour[hour_since_now] = ha_load home_appliance_wh_per_hour[hour_idx] = ha_load
# E-Auto handling # E-Auto handling
if self.ev: if ev and ev_charge_hours[hour] > 0:
if self.ev_charge_hours[hour] > 0: loaded_energy_ev, verluste_eauto = ev.charge_energy(
loaded_energy_ev, verluste_eauto = self.ev.charge_energy( None, hour, relative_power=ev_charge_hours[hour]
None, hour, relative_power=self.ev_charge_hours[hour]
) )
consumption += loaded_energy_ev consumption += loaded_energy_ev
losses_wh_per_hour[hour_since_now] += verluste_eauto losses_wh_per_hour[hour_idx] += verluste_eauto
# Process inverter logic # Process inverter logic
energy_feedin_grid_actual, energy_consumption_grid_actual, losses, eigenverbrauch = ( energy_feedin_grid_actual = energy_consumption_grid_actual = losses = eigenverbrauch = (
0.0, 0.0
0.0,
0.0,
0.0,
) )
if self.battery:
self.battery.set_charge_allowed_for_hour(self.dc_charge_hours[hour], hour) hour_ac_charge = ac_charge_hours[hour]
if self.inverter: hour_dc_charge = dc_charge_hours[hour]
energy_produced = self.pv_prediction_wh[hour] hourly_electricity_price = elect_price_hourly[hour]
hourly_energy_revenue = elect_revenue_per_hour_arr[hour]
battery.set_charge_allowed_for_hour(hour_dc_charge, hour)
if inverter:
energy_produced = pv_prediction_wh[hour]
( (
energy_feedin_grid_actual, energy_feedin_grid_actual,
energy_consumption_grid_actual, energy_consumption_grid_actual,
losses, losses,
eigenverbrauch, eigenverbrauch,
) = self.inverter.process_energy(energy_produced, consumption, hour) ) = inverter.process_energy(energy_produced, consumption, hour)
# AC PV Battery Charge # AC PV Battery Charge
if self.battery and self.ac_charge_hours[hour] > 0.0: if hour_ac_charge > 0.0:
self.battery.set_charge_allowed_for_hour(1, hour) battery.set_charge_allowed_for_hour(1, hour)
battery_charged_energy_actual, battery_losses_actual = self.battery.charge_energy( battery_charged_energy_actual, battery_losses_actual = battery.charge_energy(
None, hour, relative_power=self.ac_charge_hours[hour] None, hour, relative_power=hour_ac_charge
) )
# print(hour, " ", battery_charged_energy_actual, " ",self.ac_charge_hours[hour]," ",self.battery.current_soc_percentage())
consumption += battery_charged_energy_actual
consumption += battery_losses_actual
energy_consumption_grid_actual += battery_charged_energy_actual
energy_consumption_grid_actual += battery_losses_actual
losses_wh_per_hour[hour_since_now] += battery_losses_actual
feedin_energy_per_hour[hour_since_now] = energy_feedin_grid_actual total_battery_energy = battery_charged_energy_actual + battery_losses_actual
consumption_energy_per_hour[hour_since_now] = energy_consumption_grid_actual consumption += total_battery_energy
losses_wh_per_hour[hour_since_now] += losses energy_consumption_grid_actual += total_battery_energy
loads_energy_per_hour[hour_since_now] = consumption losses_wh_per_hour[hour_idx] += battery_losses_actual
electricity_price_per_hour[hour_since_now] = self.elect_price_hourly[hour]
# Update hourly arrays
feedin_energy_per_hour[hour_idx] = energy_feedin_grid_actual
consumption_energy_per_hour[hour_idx] = energy_consumption_grid_actual
losses_wh_per_hour[hour_idx] += losses
loads_energy_per_hour[hour_idx] = consumption
electricity_price_per_hour[hour_idx] = hourly_electricity_price
# Financial calculations # Financial calculations
costs_per_hour[hour_since_now] = ( costs_per_hour[hour_idx] = energy_consumption_grid_actual * hourly_electricity_price
energy_consumption_grid_actual * self.elect_price_hourly[hour] revenue_per_hour[hour_idx] = energy_feedin_grid_actual * hourly_energy_revenue
)
revenue_per_hour[hour_since_now] = (
energy_feedin_grid_actual * self.elect_revenue_per_hour_arr[hour]
)
# Total cost and return total_cost = np.nansum(costs_per_hour)
gesamtkosten_euro = np.nansum(costs_per_hour) - np.nansum(revenue_per_hour) total_losses = np.nansum(losses_wh_per_hour)
total_revenue = np.nansum(revenue_per_hour)
# Prepare output dictionary # Prepare output dictionary
out: Dict[str, Union[np.ndarray, float]] = { return {
"Last_Wh_pro_Stunde": loads_energy_per_hour, "Last_Wh_pro_Stunde": loads_energy_per_hour,
"Netzeinspeisung_Wh_pro_Stunde": feedin_energy_per_hour, "Netzeinspeisung_Wh_pro_Stunde": feedin_energy_per_hour,
"Netzbezug_Wh_pro_Stunde": consumption_energy_per_hour, "Netzbezug_Wh_pro_Stunde": consumption_energy_per_hour,
"Kosten_Euro_pro_Stunde": costs_per_hour, "Kosten_Euro_pro_Stunde": costs_per_hour,
"akku_soc_pro_stunde": soc_per_hour, "akku_soc_pro_stunde": soc_per_hour,
"Einnahmen_Euro_pro_Stunde": revenue_per_hour, "Einnahmen_Euro_pro_Stunde": revenue_per_hour,
"Gesamtbilanz_Euro": gesamtkosten_euro, "Gesamtbilanz_Euro": total_cost - total_revenue,
"EAuto_SoC_pro_Stunde": soc_ev_per_hour, "EAuto_SoC_pro_Stunde": soc_ev_per_hour,
"Gesamteinnahmen_Euro": np.nansum(revenue_per_hour), "Gesamteinnahmen_Euro": total_revenue,
"Gesamtkosten_Euro": np.nansum(costs_per_hour), "Gesamtkosten_Euro": total_cost,
"Verluste_Pro_Stunde": losses_wh_per_hour, "Verluste_Pro_Stunde": losses_wh_per_hour,
"Gesamt_Verluste": np.nansum(losses_wh_per_hour), "Gesamt_Verluste": total_losses,
"Home_appliance_wh_per_hour": home_appliance_wh_per_hour, "Home_appliance_wh_per_hour": home_appliance_wh_per_hour,
"Electricity_price": electricity_price_per_hour, "Electricity_price": electricity_price_per_hour,
} }
return out
# Initialize the Energy Management System, it is a singleton. # Initialize the Energy Management System, it is a singleton.
ems = EnergieManagementSystem() ems = EnergieManagementSystem()

View File

@@ -223,7 +223,6 @@ app = FastAPI(
"url": "https://www.apache.org/licenses/LICENSE-2.0.html", "url": "https://www.apache.org/licenses/LICENSE-2.0.html",
}, },
lifespan=lifespan, lifespan=lifespan,
root_path=str(Path(__file__).parent),
) )
@@ -882,9 +881,13 @@ async def proxy_put(request: Request, path: str) -> Response:
async def proxy(request: Request, path: str) -> Union[Response | RedirectResponse | HTMLResponse]: async def proxy(request: Request, path: str) -> Union[Response | RedirectResponse | HTMLResponse]:
if config_eos.server_eosdash_host and config_eos.server_eosdash_port: # Make hostname Windows friendly
host = str(config_eos.server_eosdash_host)
if host == "0.0.0.0" and os.name == "nt":
host = "localhost"
if host and config_eos.server_eosdash_port:
# Proxy to EOSdash server # Proxy to EOSdash server
url = f"http://{config_eos.server_eosdash_host}:{config_eos.server_eosdash_port}/{path}" url = f"http://{host}:{config_eos.server_eosdash_port}/{path}"
headers = dict(request.headers) headers = dict(request.headers)
data = await request.body() data = await request.body()

View File

@@ -24,7 +24,7 @@ for field_name in config_eos.model_fields:
configs.append(config) configs.append(config)
app = FastHTML() app = FastHTML(secret_key=os.getenv("EOS_SERVER__EOSDASH_SESSKEY"))
rt = app.route rt = app.route

View File

@@ -1,5 +1,6 @@
"""Server Module.""" """Server Module."""
import os
from typing import Optional from typing import Optional
from pydantic import Field, IPvAnyAddress, field_validator from pydantic import Field, IPvAnyAddress, field_validator
@@ -10,6 +11,12 @@ from akkudoktoreos.core.logging import get_logger
logger = get_logger(__name__) logger = get_logger(__name__)
def get_default_host() -> str:
if os.name == "nt":
return "127.0.0.1"
return "0.0.0.0"
class ServerCommonSettings(SettingsBaseModel): class ServerCommonSettings(SettingsBaseModel):
"""Common server settings. """Common server settings.
@@ -18,7 +25,7 @@ class ServerCommonSettings(SettingsBaseModel):
""" """
server_eos_host: Optional[IPvAnyAddress] = Field( server_eos_host: Optional[IPvAnyAddress] = Field(
default="0.0.0.0", description="EOS server IP address." default=get_default_host(), description="EOS server IP address."
) )
server_eos_port: Optional[int] = Field(default=8503, description="EOS server IP port number.") server_eos_port: Optional[int] = Field(default=8503, description="EOS server IP port number.")
server_eos_verbose: Optional[bool] = Field(default=False, description="Enable debug output") server_eos_verbose: Optional[bool] = Field(default=False, description="Enable debug output")
@@ -26,7 +33,7 @@ class ServerCommonSettings(SettingsBaseModel):
default=True, description="EOS server to start EOSdash server." default=True, description="EOS server to start EOSdash server."
) )
server_eosdash_host: Optional[IPvAnyAddress] = Field( server_eosdash_host: Optional[IPvAnyAddress] = Field(
default="0.0.0.0", description="EOSdash server IP address." default=get_default_host(), description="EOSdash server IP address."
) )
server_eosdash_port: Optional[int] = Field( server_eosdash_port: Optional[int] = Field(
default=8504, description="EOSdash server IP port number." default=8504, description="EOSdash server IP port number."

View File

@@ -13,6 +13,7 @@ import pendulum
from matplotlib.backends.backend_pdf import PdfPages from matplotlib.backends.backend_pdf import PdfPages
from akkudoktoreos.core.coreabc import ConfigMixin from akkudoktoreos.core.coreabc import ConfigMixin
from akkudoktoreos.core.ems import EnergieManagementSystem
from akkudoktoreos.core.logging import get_logger from akkudoktoreos.core.logging import get_logger
from akkudoktoreos.optimization.genetic import OptimizationParameters from akkudoktoreos.optimization.genetic import OptimizationParameters
from akkudoktoreos.utils.datetimeutil import to_datetime from akkudoktoreos.utils.datetimeutil import to_datetime
@@ -147,14 +148,16 @@ class VisualizationReport(ConfigMixin):
# Format the time axis # Format the time axis
plt.gca().xaxis.set_major_formatter( plt.gca().xaxis.set_major_formatter(
mdates.DateFormatter("%Y-%m-%d") mdates.DateFormatter("%Y-%m-%d", tz=self.config.timezone)
) # Show date and time ) # Show date and time
plt.gca().xaxis.set_major_locator( plt.gca().xaxis.set_major_locator(
mdates.DayLocator(interval=1, tz=None) mdates.DayLocator(interval=1, tz=self.config.timezone)
) # Major ticks every day ) # Major ticks every day
plt.gca().xaxis.set_minor_locator(mdates.HourLocator(interval=3, tz=None)) plt.gca().xaxis.set_minor_locator(
mdates.HourLocator(interval=2, tz=self.config.timezone)
)
# Minor ticks every 6 hours # Minor ticks every 6 hours
plt.gca().xaxis.set_minor_formatter(mdates.DateFormatter("%H")) plt.gca().xaxis.set_minor_formatter(mdates.DateFormatter("%H", tz=self.config.timezone))
# plt.gcf().autofmt_xdate(rotation=45, which="major") # plt.gcf().autofmt_xdate(rotation=45, which="major")
# Auto-format the x-axis for readability # Auto-format the x-axis for readability
@@ -174,6 +177,7 @@ class VisualizationReport(ConfigMixin):
# Add vertical line for the current date if within the axis range # Add vertical line for the current date if within the axis range
current_time = pendulum.now(self.config.timezone) current_time = pendulum.now(self.config.timezone)
# current_time = pendulum.now().add(hours=1)
if timestamps[0].subtract(hours=2) <= current_time <= timestamps[-1]: if timestamps[0].subtract(hours=2) <= current_time <= timestamps[-1]:
plt.axvline(current_time, color="r", linestyle="--", label="Now") plt.axvline(current_time, color="r", linestyle="--", label="Now")
plt.text(current_time, plt.ylim()[1], "Now", color="r", ha="center", va="bottom") plt.text(current_time, plt.ylim()[1], "Now", color="r", ha="center", va="bottom")
@@ -187,8 +191,10 @@ class VisualizationReport(ConfigMixin):
hours_since_start = [(t - timestamps[0]).total_seconds() / 3600 for t in timestamps] hours_since_start = [(t - timestamps[0]).total_seconds() / 3600 for t in timestamps]
# ax2.set_xticks(timestamps[::48]) # Set ticks every 12 hours # ax2.set_xticks(timestamps[::48]) # Set ticks every 12 hours
# ax2.set_xticklabels([f"{int(h)}" for h in hours_since_start[::48]]) # ax2.set_xticklabels([f"{int(h)}" for h in hours_since_start[::48]])
ax2.set_xticks(timestamps[:: len(timestamps) // 24]) # Select 10 evenly spaced ticks # ax2.set_xticks(timestamps[:: len(timestamps) // 24]) # Select 10 evenly spaced ticks
ax2.set_xticklabels([f"{int(h)}" for h in hours_since_start[:: len(timestamps) // 24]]) ax2.set_xticks(timestamps[:: len(timestamps) // 12]) # Select 10 evenly spaced ticks
# ax2.set_xticklabels([f"{int(h)}" for h in hours_since_start[:: len(timestamps) // 24]])
ax2.set_xticklabels([f"{int(h)}" for h in hours_since_start[:: len(timestamps) // 12]])
if x2label: if x2label:
ax2.set_xlabel(x2label) ax2.set_xlabel(x2label)
@@ -416,15 +422,17 @@ def prepare_visualize(
parameters: OptimizationParameters, parameters: OptimizationParameters,
results: dict, results: dict,
filename: str = "visualization_results.pdf", filename: str = "visualization_results.pdf",
start_hour: Optional[int] = 0, start_hour: int = 0,
) -> None: ) -> None:
report = VisualizationReport(filename) report = VisualizationReport(filename)
next_full_hour_date = pendulum.now(report.config.timezone).start_of("hour").add(hours=1) # next_full_hour_date = pendulum.now(report.config.timezone).start_of("day").add(hours=start_hour)
# next_full_hour_date = to_datetime().set(minute=0, second=0, microsecond=0)
next_full_hour_date = EnergieManagementSystem.set_start_datetime()
# Group 1: # Group 1:
report.create_line_chart_date( report.create_line_chart_date(
next_full_hour_date, # start_date next_full_hour_date,
[ [
parameters.ems.gesamtlast, parameters.ems.gesamtlast[start_hour:],
], ],
title="Load Profile", title="Load Profile",
# xlabel="Hours", # not enough space # xlabel="Hours", # not enough space
@@ -432,9 +440,9 @@ def prepare_visualize(
labels=["Total Load (Wh)"], labels=["Total Load (Wh)"],
) )
report.create_line_chart_date( report.create_line_chart_date(
next_full_hour_date, # start_date next_full_hour_date,
[ [
parameters.ems.pv_prognose_wh, parameters.ems.pv_prognose_wh[start_hour:],
], ],
title="PV Forecast", title="PV Forecast",
# xlabel="Hours", # not enough space # xlabel="Hours", # not enough space
@@ -442,8 +450,15 @@ def prepare_visualize(
) )
report.create_line_chart_date( report.create_line_chart_date(
next_full_hour_date, # start_date next_full_hour_date,
[np.full(len(parameters.ems.gesamtlast), parameters.ems.einspeiseverguetung_euro_pro_wh)], [
np.full(
len(parameters.ems.gesamtlast) - start_hour,
parameters.ems.einspeiseverguetung_euro_pro_wh[start_hour:]
if isinstance(parameters.ems.einspeiseverguetung_euro_pro_wh, list)
else parameters.ems.einspeiseverguetung_euro_pro_wh,
)
],
title="Remuneration", title="Remuneration",
# xlabel="Hours", # not enough space # xlabel="Hours", # not enough space
ylabel="€/Wh", ylabel="€/Wh",
@@ -451,9 +466,9 @@ def prepare_visualize(
) )
if parameters.temperature_forecast: if parameters.temperature_forecast:
report.create_line_chart_date( report.create_line_chart_date(
next_full_hour_date, # start_date next_full_hour_date,
[ [
parameters.temperature_forecast, parameters.temperature_forecast[start_hour:],
], ],
title="Temperature Forecast", title="Temperature Forecast",
# xlabel="Hours", # not enough space # xlabel="Hours", # not enough space
@@ -502,21 +517,35 @@ def prepare_visualize(
) )
report.create_line_chart_date( report.create_line_chart_date(
next_full_hour_date, # start_date next_full_hour_date, # start_date
[parameters.ems.strompreis_euro_pro_wh], [parameters.ems.strompreis_euro_pro_wh[start_hour:]],
# title="Electricity Price", # not enough space # title="Electricity Price", # not enough space
# xlabel="Date", # not enough space # xlabel="Date", # not enough space
ylabel="Electricity Price (€/Wh)", ylabel="Electricity Price (€/Wh)",
x2label=None, # not enough space x2label=None, # not enough space
) )
labels = list(
item
for sublist in zip(
list(str(i) for i in range(0, 23, 2)), list(str(" ") for i in range(0, 23, 2))
)
for item in sublist
)
labels = labels[start_hour:] + labels
report.create_bar_chart( report.create_bar_chart(
list(str(i) for i in range(len(results["ac_charge"]))), labels,
[results["ac_charge"], results["dc_charge"], results["discharge_allowed"]], [
results["ac_charge"][start_hour:],
results["dc_charge"][start_hour:],
results["discharge_allowed"][start_hour:],
],
title="AC/DC Charging and Discharge Overview", title="AC/DC Charging and Discharge Overview",
ylabel="Relative Power (0-1) / Discharge (0 or 1)", ylabel="Relative Power (0-1) / Discharge (0 or 1)",
label_names=["AC Charging (relative)", "DC Charging (relative)", "Discharge Allowed"], label_names=["AC Charging (relative)", "DC Charging (relative)", "Discharge Allowed"],
colors=["blue", "green", "red"], colors=["blue", "green", "red"],
bottom=3, bottom=3,
xlabels=labels,
) )
report.finalize_group() report.finalize_group()

View File

@@ -6,6 +6,7 @@ import pytest
from akkudoktoreos.core.ems import ( from akkudoktoreos.core.ems import (
EnergieManagementSystem, EnergieManagementSystem,
EnergieManagementSystemParameters, EnergieManagementSystemParameters,
SimulationResult,
get_ems, get_ems,
) )
from akkudoktoreos.devices.battery import ( from akkudoktoreos.devices.battery import (
@@ -182,6 +183,7 @@ def test_simulation(create_ems_instance):
# Assertions to validate results # Assertions to validate results
assert result is not None, "Result should not be None" assert result is not None, "Result should not be None"
assert isinstance(result, dict), "Result should be a dictionary" assert isinstance(result, dict), "Result should be a dictionary"
assert SimulationResult(**result) is not None
assert "Last_Wh_pro_Stunde" in result, "Result should contain 'Last_Wh_pro_Stunde'" assert "Last_Wh_pro_Stunde" in result, "Result should contain 'Last_Wh_pro_Stunde'"
""" """
@@ -240,7 +242,7 @@ def test_simulation(create_ems_instance):
assert ( assert (
abs(result["Netzeinspeisung_Wh_pro_Stunde"][10] - 3946.93) < 1e-3 abs(result["Netzeinspeisung_Wh_pro_Stunde"][10] - 3946.93) < 1e-3
), "'Netzeinspeisung_Wh_pro_Stunde[11]' should be 4000." ), "'Netzeinspeisung_Wh_pro_Stunde[11]' should be 3946.93."
assert ( assert (
abs(result["Netzeinspeisung_Wh_pro_Stunde"][11] - 0.0) < 1e-3 abs(result["Netzeinspeisung_Wh_pro_Stunde"][11] - 0.0) < 1e-3
@@ -251,6 +253,78 @@ def test_simulation(create_ems_instance):
), "'akku_soc_pro_stunde[20]' should be 10." ), "'akku_soc_pro_stunde[20]' should be 10."
assert ( assert (
abs(result["Last_Wh_pro_Stunde"][20] - 6050.98) < 1e-3 abs(result["Last_Wh_pro_Stunde"][20] - 6050.98) < 1e-3
), "'Netzeinspeisung_Wh_pro_Stunde[11]' should be 0.0." ), "'Last_Wh_pro_Stunde[20]' should be 6050.98."
print("All tests passed successfully.") print("All tests passed successfully.")
def test_set_parameters(create_ems_instance):
"""Test the set_parameters method of EnergieManagementSystem."""
ems = create_ems_instance
# Check if parameters are set correctly
assert ems.load_energy_array is not None, "load_energy_array should not be None"
assert ems.pv_prediction_wh is not None, "pv_prediction_wh should not be None"
assert ems.elect_price_hourly is not None, "elect_price_hourly should not be None"
assert (
ems.elect_revenue_per_hour_arr is not None
), "elect_revenue_per_hour_arr should not be None"
def test_set_akku_discharge_hours(create_ems_instance):
"""Test the set_akku_discharge_hours method of EnergieManagementSystem."""
ems = create_ems_instance
discharge_hours = np.full(ems.config.prediction_hours, 1.0)
ems.set_akku_discharge_hours(discharge_hours)
assert np.array_equal(
ems.battery.discharge_array, discharge_hours
), "Discharge hours should be set correctly"
def test_set_akku_ac_charge_hours(create_ems_instance):
"""Test the set_akku_ac_charge_hours method of EnergieManagementSystem."""
ems = create_ems_instance
ac_charge_hours = np.full(ems.config.prediction_hours, 1.0)
ems.set_akku_ac_charge_hours(ac_charge_hours)
assert np.array_equal(
ems.ac_charge_hours, ac_charge_hours
), "AC charge hours should be set correctly"
def test_set_akku_dc_charge_hours(create_ems_instance):
"""Test the set_akku_dc_charge_hours method of EnergieManagementSystem."""
ems = create_ems_instance
dc_charge_hours = np.full(ems.config.prediction_hours, 1.0)
ems.set_akku_dc_charge_hours(dc_charge_hours)
assert np.array_equal(
ems.dc_charge_hours, dc_charge_hours
), "DC charge hours should be set correctly"
def test_set_ev_charge_hours(create_ems_instance):
"""Test the set_ev_charge_hours method of EnergieManagementSystem."""
ems = create_ems_instance
ev_charge_hours = np.full(ems.config.prediction_hours, 1.0)
ems.set_ev_charge_hours(ev_charge_hours)
assert np.array_equal(
ems.ev_charge_hours, ev_charge_hours
), "EV charge hours should be set correctly"
def test_reset(create_ems_instance):
"""Test the reset method of EnergieManagementSystem."""
ems = create_ems_instance
ems.reset()
assert ems.ev.current_soc_percentage() == 100, "EV SOC should be reset to initial value"
assert (
ems.battery.current_soc_percentage() == 80
), "Battery SOC should be reset to initial value"
def test_simulate_start_now(create_ems_instance):
"""Test the simulate_start_now method of EnergieManagementSystem."""
ems = create_ems_instance
result = ems.simulate_start_now()
assert result is not None, "Result should not be None"
assert isinstance(result, dict), "Result should be a dictionary"
assert "Last_Wh_pro_Stunde" in result, "Result should contain 'Last_Wh_pro_Stunde'"

View File

@@ -1,7 +1,14 @@
{ {
"ems": { "ems": {
"preis_euro_pro_wh_akku": 0.0001, "preis_euro_pro_wh_akku": 0.0001,
"einspeiseverguetung_euro_pro_wh": 0.00007, "einspeiseverguetung_euro_pro_wh": [
0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007,
0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007,
0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007,
0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007,
0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007,
0.00007, 0.00007, 0.00007
],
"gesamtlast": [ "gesamtlast": [
676.71, 876.19, 527.13, 468.88, 531.38, 517.95, 483.15, 472.28, 1011.68, 995.00, 676.71, 876.19, 527.13, 468.88, 531.38, 517.95, 483.15, 472.28, 1011.68, 995.00,
1053.07, 1063.91, 1320.56, 1132.03, 1163.67, 1176.82, 1216.22, 1103.78, 1129.12, 1053.07, 1063.91, 1320.56, 1132.03, 1163.67, 1176.82, 1216.22, 1103.78, 1129.12,