diff --git a/.gitignore b/.gitignore index efc77834..86c71d77 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,9 @@ coverage.xml .pytest_cache/ cover/ +# Documentation generation +_autosummary/ + # Translations *.mo *.pot diff --git a/Makefile b/Makefile index 37cc1ef8..047e4c4e 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # Define the targets -.PHONY: help venv pip install dist test test-full docker-run docs clean +.PHONY: help venv pip install dist test test-full docker-run docs read-docs clean # Default target all: help @@ -14,6 +14,7 @@ help: @echo " docker-run - Run entire setup on docker" @echo " docker-build - Rebuild docker image" @echo " docs - Generate HTML documentation (in build/docs/html/)." + @echo " read-docs - Read HTML documentation in your browser." @echo " run - Run flask_server in the virtual environment (needs install before)." @echo " dist - Create distribution (in dist/)." @echo " clean - Remove generated documentation, distribution and virtual environment." @@ -51,11 +52,18 @@ docs: pip-dev .venv/bin/sphinx-build -M html docs build/docs @echo "Documentation generated to build/docs/html/." +# Target to read the HTML documentation +read-docs: docs + @echo "Read the documentation in your browser" + .venv/bin/python -m webbrowser build/docs/html/index.html + # Clean target to remove generated documentation, distribution and virtual environment clean: - @echo "Cleaning virtual env, distribution and documentation directories" - rm -rf dist - rm -rf .venv + @echo "Cleaning virtual env, distribution and build directories" + rm -rf dist build .venv + @echo "Searching and deleting all '_autosum' directories in docs..." + @find docs -type d -name '_autosummary' -exec rm -rf {} +; + @echo "Deletion complete." run: @echo "Starting flask server, please wait..." diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst new file mode 100644 index 00000000..3cddd59e --- /dev/null +++ b/docs/_templates/autosummary/class.rst @@ -0,0 +1,33 @@ +{{ fullname | escape | underline}} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + :members: + :undoc-members: + :show-inheritance: + :inherited-members: + + {% block methods %} + .. automethod:: __init__ + + {% if methods %} + .. rubric:: {{ _('Methods') }} + + .. autosummary:: + {% for item in methods %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block attributes %} + {% if attributes %} + .. rubric:: {{ _('Attributes') }} + + .. autosummary:: + {% for item in attributes %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} diff --git a/docs/_templates/autosummary/module.rst b/docs/_templates/autosummary/module.rst new file mode 100644 index 00000000..be3cf987 --- /dev/null +++ b/docs/_templates/autosummary/module.rst @@ -0,0 +1,66 @@ +{{ fullname | escape | underline}} + +.. automodule:: {{ fullname }} + + {% block attributes %} + {% if attributes %} + .. rubric:: Module Attributes + + .. autosummary:: + :toctree: + {% for item in attributes %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block functions %} + {% if functions %} + .. rubric:: {{ _('Functions') }} + + .. autosummary:: + :toctree: + {% for item in functions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block classes %} + {% if classes %} + .. rubric:: {{ _('Classes') }} + + .. autosummary:: + :toctree: + :template: autosummary/class.rst + {% for item in classes %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block exceptions %} + {% if exceptions %} + .. rubric:: {{ _('Exceptions') }} + + .. autosummary:: + :toctree: + {% for item in exceptions %} + {{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + +{% block modules %} +{% if modules %} +.. rubric:: Modules + +.. autosummary:: + :toctree: + :template: autosummary/module.rst + :recursive: +{% for item in modules %} + {{ item }} +{%- endfor %} +{% endif %} +{% endblock %} diff --git a/docs/akkudoktoreos/about.rst b/docs/akkudoktoreos/about.md similarity index 75% rename from docs/akkudoktoreos/about.rst rename to docs/akkudoktoreos/about.md index 594dc9c1..4d3a2fe5 100644 --- a/docs/akkudoktoreos/about.rst +++ b/docs/akkudoktoreos/about.md @@ -1,11 +1,6 @@ -.. - Copyright (c) 2024 Bobby Noelte - SPDX-License-Identifier: Apache-2.0 +% SPDX-License-Identifier: Apache-2.0 -.. _akkudoktoreos_about: - -About Akkudoktor EOS -#################### +# 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 diff --git a/docs/akkudoktoreos/api.rst b/docs/akkudoktoreos/api.rst new file mode 100644 index 00000000..b88cc9c3 --- /dev/null +++ b/docs/akkudoktoreos/api.rst @@ -0,0 +1,16 @@ +.. + SPDX-License-Identifier: Apache-2.0 + File has to be of RST format to make autosummary directive work correctly + +.. _akkudoktoreos_api: + +EOS API +======= + +.. autosummary:: + :toctree: _autosummary + :template: autosummary/module.rst + :recursive: + + akkudoktoreos + akkudoktoreosserver diff --git a/docs/akkudoktoreosserver/openapi.json b/docs/akkudoktoreosserver/openapi.json new file mode 100644 index 00000000..2925bd7b --- /dev/null +++ b/docs/akkudoktoreosserver/openapi.json @@ -0,0 +1,921 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Akkudoktor-EOS", + "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.", + "version": "0.0.1" + }, + "paths": { + "/strompreis": { + "get": { + "summary": "Fastapi Strompreis", + "operationId": "fastapi_strompreis_strompreis_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Response Fastapi Strompreis Strompreis Get" + } + } + } + } + } + } + }, + "/gesamtlast": { + "post": { + "summary": "Fastapi Gesamtlast", + "description": "Endpoint to handle total load calculation based on the latest measured data", + "operationId": "fastapi_gesamtlast_gesamtlast_post", + "parameters": [ + { + "name": "year_energy", + "in": "query", + "required": true, + "schema": { + "type": "number", + "title": "Year Energy" + } + }, + { + "name": "hours", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 48, + "title": "Hours" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object" + }, + "title": "Measured Data" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "number" + }, + "title": "Response Fastapi Gesamtlast Gesamtlast Post" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/gesamtlast_simple": { + "get": { + "summary": "Fastapi Gesamtlast Simple", + "operationId": "fastapi_gesamtlast_simple_gesamtlast_simple_get", + "parameters": [ + { + "name": "year_energy", + "in": "query", + "required": true, + "schema": { + "type": "number", + "title": "Year Energy" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "number" + }, + "title": "Response Fastapi Gesamtlast Simple Gesamtlast Simple Get" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/pvforecast": { + "get": { + "summary": "Fastapi Pvprognose", + "operationId": "fastapi_pvprognose_pvforecast_get", + "parameters": [ + { + "name": "url", + "in": "query", + "required": true, + "schema": { + "type": "string", + "title": "Url" + } + }, + { + "name": "ac_power_measurement", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Ac Power Measurement" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForecastResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/optimize": { + "post": { + "summary": "Fastapi Optimize", + "operationId": "fastapi_optimize_optimize_post", + "parameters": [ + { + "name": "start_hour", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Defaults to current hour of the day.", + "title": "Start Hour" + }, + "description": "Defaults to current hour of the day." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OptimizationParameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/OptimizeResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/visualization_results.pdf": { + "get": { + "summary": "Get Pdf", + "operationId": "get_pdf_visualization_results_pdf_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + } + }, + "components": { + "schemas": { + "EAutoParameters": { + "properties": { + "kapazitaet_wh": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Kapazitaet Wh", + "description": "An integer representing the capacity of the battery in watt-hours." + }, + "lade_effizienz": { + "type": "number", + "maximum": 1.0, + "exclusiveMinimum": 0.0, + "title": "Lade Effizienz", + "description": "A float representing the charging efficiency of the battery.", + "default": 0.88 + }, + "entlade_effizienz": { + "type": "number", + "title": "Entlade Effizienz", + "default": 1.0 + }, + "max_ladeleistung_w": { + "anyOf": [ + { + "type": "number", + "exclusiveMinimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Max Ladeleistung W", + "description": "An integer representing the charging power of the battery in watts." + }, + "start_soc_prozent": { + "type": "integer", + "maximum": 100.0, + "minimum": 0.0, + "title": "Start Soc Prozent", + "description": "An integer representing the current state of charge (SOC) of the battery in percentage.", + "default": 0 + }, + "min_soc_prozent": { + "type": "integer", + "maximum": 100.0, + "minimum": 0.0, + "title": "Min Soc Prozent", + "description": "An integer representing the minimum state of charge (SOC) of the battery in percentage.", + "default": 0 + }, + "max_soc_prozent": { + "type": "integer", + "maximum": 100.0, + "minimum": 0.0, + "title": "Max Soc Prozent", + "default": 100 + } + }, + "type": "object", + "required": [ + "kapazitaet_wh" + ], + "title": "EAutoParameters" + }, + "EAutoResult": { + "properties": { + "charge_array": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Charge Array", + "description": "Indicates for each hour whether the EV is charging (`0` for no charging, `1` for charging)." + }, + "discharge_array": { + "items": { + "type": "integer" + }, + "type": "array", + "title": "Discharge Array", + "description": "Indicates for each hour whether the EV is discharging (`0` for no discharging, `1` for discharging)." + }, + "entlade_effizienz": { + "type": "number", + "title": "Entlade Effizienz", + "description": "The discharge efficiency as a float." + }, + "hours": { + "type": "integer", + "title": "Hours", + "default": "Amount of hours the simulation is done for." + }, + "kapazitaet_wh": { + "type": "integer", + "title": "Kapazitaet Wh", + "default": "The capacity of the EV\u2019s battery in watt-hours." + }, + "lade_effizienz": { + "type": "number", + "title": "Lade Effizienz", + "default": "The charging efficiency as a float." + }, + "max_ladeleistung_w": { + "type": "integer", + "title": "Max Ladeleistung W", + "description": "The maximum charging power of the EV in watts." + }, + "soc_wh": { + "type": "number", + "title": "Soc Wh", + "description": "The state of charge of the battery in watt-hours at the start of the simulation." + }, + "start_soc_prozent": { + "type": "integer", + "title": "Start Soc Prozent", + "description": "The state of charge of the battery in percentage at the start of the simulation." + } + }, + "type": "object", + "required": [ + "charge_array", + "discharge_array", + "entlade_effizienz", + "max_ladeleistung_w", + "soc_wh", + "start_soc_prozent" + ], + "title": "EAutoResult", + "description": "\"This object contains information related to the electric vehicle and its charging and discharging behavior" + }, + "EnergieManagementSystemParameters": { + "properties": { + "pv_prognose_wh": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Pv Prognose Wh", + "description": "An array of floats representing the forecasted photovoltaic output in watts for different time intervals." + }, + "strompreis_euro_pro_wh": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Strompreis Euro Pro Wh", + "description": "An array of floats representing the electricity price in euros per watt-hour for different time intervals." + }, + "einspeiseverguetung_euro_pro_wh": { + "type": "number", + "title": "Einspeiseverguetung Euro Pro Wh", + "description": "A float representing the feed-in compensation in euros per watt-hour." + }, + "preis_euro_pro_wh_akku": { + "type": "number", + "title": "Preis Euro Pro Wh Akku" + }, + "gesamtlast": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Gesamtlast", + "description": "An array of floats representing the total load (consumption) in watts for different time intervals." + } + }, + "type": "object", + "required": [ + "pv_prognose_wh", + "strompreis_euro_pro_wh", + "einspeiseverguetung_euro_pro_wh", + "preis_euro_pro_wh_akku", + "gesamtlast" + ], + "title": "EnergieManagementSystemParameters" + }, + "ForecastResponse": { + "properties": { + "temperature": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Temperature" + }, + "pvpower": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Pvpower" + } + }, + "type": "object", + "required": [ + "temperature", + "pvpower" + ], + "title": "ForecastResponse" + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "HaushaltsgeraetParameters": { + "properties": { + "verbrauch_wh": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Verbrauch Wh", + "description": "An integer representing the energy consumption of a household device in watt-hours." + }, + "dauer_h": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Dauer H", + "description": "An integer representing the usage duration of a household device in hours." + } + }, + "type": "object", + "required": [ + "verbrauch_wh", + "dauer_h" + ], + "title": "HaushaltsgeraetParameters" + }, + "OptimizationParameters": { + "properties": { + "ems": { + "$ref": "#/components/schemas/EnergieManagementSystemParameters" + }, + "pv_akku": { + "$ref": "#/components/schemas/PVAkkuParameters" + }, + "wechselrichter": { + "$ref": "#/components/schemas/WechselrichterParameters", + "default": { + "max_leistung_wh": 10000.0 + } + }, + "eauto": { + "$ref": "#/components/schemas/EAutoParameters" + }, + "spuelmaschine": { + "anyOf": [ + { + "$ref": "#/components/schemas/HaushaltsgeraetParameters" + }, + { + "type": "null" + } + ] + }, + "temperature_forecast": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Temperature Forecast", + "default": "An array of floats representing the temperature forecast in degrees Celsius for different time intervals." + }, + "start_solution": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Start Solution", + "description": "Can be `null` or contain a previous solution (if available)." + } + }, + "type": "object", + "required": [ + "ems", + "pv_akku", + "eauto" + ], + "title": "OptimizationParameters" + }, + "OptimizeResponse": { + "properties": { + "discharge_hours_bin": { + "items": { + "type": "integer" + }, + "type": "array", + "title": "Discharge Hours Bin", + "description": "An array that indicates for each hour of the forecast period whether energy is discharged from the battery or not. The values are either `0` (no discharge) or `1` (discharge)." + }, + "eautocharge_hours_float": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Eautocharge Hours Float", + "description": "An array of binary values (0 or 1) that indicates whether the EV will be charged in a certain hour." + }, + "result": { + "$ref": "#/components/schemas/SimulationResult" + }, + "eauto_obj": { + "$ref": "#/components/schemas/EAutoResult" + }, + "start_solution": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Start Solution", + "description": "An array of binary values (0 or 1) representing a possible starting solution for the simulation." + }, + "spuelstart": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Spuelstart", + "description": "Can be `null` or contain an object representing the start of washing (if applicable)." + } + }, + "type": "object", + "required": [ + "discharge_hours_bin", + "eautocharge_hours_float", + "result", + "eauto_obj" + ], + "title": "OptimizeResponse", + "description": "**Note**: The first value of \"Last_Wh_pro_Stunde\", \"Netzeinspeisung_Wh_pro_Stunde\" and \"Netzbezug_Wh_pro_Stunde\", will be set to null in the JSON output and represented as NaN or None in the corresponding classes' data returns. This approach is adopted to ensure that the current hour's processing remains unchanged." + }, + "PVAkkuParameters": { + "properties": { + "kapazitaet_wh": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Kapazitaet Wh", + "description": "An integer representing the capacity of the battery in watt-hours." + }, + "lade_effizienz": { + "type": "number", + "maximum": 1.0, + "exclusiveMinimum": 0.0, + "title": "Lade Effizienz", + "description": "A float representing the charging efficiency of the battery.", + "default": 0.88 + }, + "entlade_effizienz": { + "type": "number", + "maximum": 1.0, + "exclusiveMinimum": 0.0, + "title": "Entlade Effizienz", + "default": 0.88 + }, + "max_ladeleistung_w": { + "anyOf": [ + { + "type": "number", + "exclusiveMinimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Max Ladeleistung W", + "description": "An integer representing the charging power of the battery in watts.", + "default": 5000 + }, + "start_soc_prozent": { + "type": "integer", + "maximum": 100.0, + "minimum": 0.0, + "title": "Start Soc Prozent", + "description": "An integer representing the state of charge of the battery at the **start** of the current hour (not the current state).", + "default": 0 + }, + "min_soc_prozent": { + "type": "integer", + "maximum": 100.0, + "minimum": 0.0, + "title": "Min Soc Prozent", + "description": "An integer representing the minimum state of charge (SOC) of the battery in percentage.", + "default": 0 + }, + "max_soc_prozent": { + "type": "integer", + "maximum": 100.0, + "minimum": 0.0, + "title": "Max Soc Prozent", + "default": 100 + } + }, + "type": "object", + "required": [ + "kapazitaet_wh" + ], + "title": "PVAkkuParameters" + }, + "SimulationResult": { + "properties": { + "Last_Wh_pro_Stunde": { + "items": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "type": "array", + "title": "Last Wh Pro Stunde", + "description": "TBD" + }, + "EAuto_SoC_pro_Stunde": { + "items": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "type": "array", + "title": "Eauto Soc Pro Stunde", + "description": "The state of charge of the EV for each hour." + }, + "Einnahmen_Euro_pro_Stunde": { + "items": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "type": "array", + "title": "Einnahmen Euro Pro Stunde", + "description": "The revenue from grid feed-in or other sources in euros per hour." + }, + "Gesamt_Verluste": { + "type": "number", + "title": "Gesamt Verluste", + "description": "The total losses in watt-hours over the entire period." + }, + "Gesamtbilanz_Euro": { + "type": "number", + "title": "Gesamtbilanz Euro", + "description": "The total balance of revenues minus costs in euros." + }, + "Gesamteinnahmen_Euro": { + "type": "number", + "title": "Gesamteinnahmen Euro", + "description": "The total revenues in euros." + }, + "Gesamtkosten_Euro": { + "type": "number", + "title": "Gesamtkosten Euro", + "description": "The total costs in euros." + }, + "Haushaltsgeraet_wh_pro_stunde": { + "items": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "type": "array", + "title": "Haushaltsgeraet Wh Pro Stunde", + "description": "The energy consumption of a household appliance in watt-hours per hour." + }, + "Kosten_Euro_pro_Stunde": { + "items": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "type": "array", + "title": "Kosten Euro Pro Stunde", + "description": "The costs in euros per hour." + }, + "Netzbezug_Wh_pro_Stunde": { + "items": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "type": "array", + "title": "Netzbezug Wh Pro Stunde", + "description": "The grid energy drawn in watt-hours per hour." + }, + "Netzeinspeisung_Wh_pro_Stunde": { + "items": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "type": "array", + "title": "Netzeinspeisung Wh Pro Stunde", + "description": "The energy fed into the grid in watt-hours per hour." + }, + "Verluste_Pro_Stunde": { + "items": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "type": "array", + "title": "Verluste Pro Stunde", + "description": "The losses in watt-hours per hour." + }, + "akku_soc_pro_stunde": { + "items": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "type": "array", + "title": "Akku Soc Pro Stunde", + "description": "The state of charge of the battery (not the EV) in percentage per hour." + } + }, + "type": "object", + "required": [ + "Last_Wh_pro_Stunde", + "EAuto_SoC_pro_Stunde", + "Einnahmen_Euro_pro_Stunde", + "Gesamt_Verluste", + "Gesamtbilanz_Euro", + "Gesamteinnahmen_Euro", + "Gesamtkosten_Euro", + "Haushaltsgeraet_wh_pro_stunde", + "Kosten_Euro_pro_Stunde", + "Netzbezug_Wh_pro_Stunde", + "Netzeinspeisung_Wh_pro_Stunde", + "Verluste_Pro_Stunde", + "akku_soc_pro_stunde" + ], + "title": "SimulationResult", + "description": "This object contains the results of the simulation and provides insights into various parameters over the entire forecast period" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + }, + "WechselrichterParameters": { + "properties": { + "max_leistung_wh": { + "type": "number", + "exclusiveMinimum": 0.0, + "title": "Max Leistung Wh", + "default": 10000 + } + }, + "type": "object", + "title": "WechselrichterParameters" + } + } + } +} \ No newline at end of file diff --git a/docs/akkudoktoreosserver/serverapi.rst b/docs/akkudoktoreosserver/serverapi.rst new file mode 100644 index 00000000..c93e3a26 --- /dev/null +++ b/docs/akkudoktoreosserver/serverapi.rst @@ -0,0 +1,17 @@ +.. + SPDX-License-Identifier: Apache-2.0 + File has to be of RST format to make openapi directive work correctly + +.. _akkudoktoreos_server_api: + +Server API +########## + +For a more detailed documentation see the Swagger interface: `EOS OpenAPI Spec `_ + +.. openapi:: openapi.json + :examples: + +.. + Due to bugs in sphinxcontrib-openapi referenced request/response objects fail to render and anyOf is broken too. + :request: diff --git a/docs/conf.py b/docs/conf.py index 8d650608..a669d36d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,14 +1,12 @@ -# Configuration file for the Sphinx documentation builder. -# -# For the full list of built-in configuration values, see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html +"""Configuration file for the Sphinx documentation builder. + +For the full list of built-in configuration values, see the documentation: +https://www.sphinx-doc.org/en/master/usage/configuration.html +""" import sys from pathlib import Path -# Make source file directories available to sphinx -sys.path.insert(0, str(Path("..", "src").resolve())) - # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information @@ -23,7 +21,9 @@ extensions = [ "sphinx.ext.autodoc", "sphinx.ext.autosummary", + "sphinx.ext.napoleon", "sphinx_rtd_theme", + "sphinxcontrib.openapi", "myst_parser", ] @@ -36,6 +36,59 @@ ".md": "markdown", } +# -- Options for Myst Markdown ----------------------------------------------- +# see https://github.com/executablebooks/MyST-Parser/blob/master/docs/conf.py + +myst_enable_extensions = [ + "dollarmath", + "amsmath", + "deflist", + "fieldlist", + "html_admonition", + "html_image", + "colon_fence", + "smartquotes", + "replacements", + "linkify", + "strikethrough", + "substitution", + "tasklist", + "attrs_inline", + "attrs_block", +] +myst_url_schemes = { + "http": None, + "https": None, + "mailto": None, + "ftp": None, + "wiki": "https://en.wikipedia.org/wiki/{{path}}#{{fragment}}", + "doi": "https://doi.org/{{path}}", + "gh-pr": { + "url": "https://github.com/Akkudoktor-EOS/EOS/pull/{{path}}#{{fragment}}", + "title": "PR #{{path}}", + "classes": ["github"], + }, + "gh-issue": { + "url": "https://github.com/Akkudoktor-EOS/EOS/issue/{{path}}#{{fragment}}", + "title": "Issue #{{path}}", + "classes": ["github"], + }, + "gh-user": { + "url": "https://github.com/{{path}}", + "title": "@{{path}}", + "classes": ["github"], + }, +} +myst_number_code_blocks = ["typescript"] +myst_heading_anchors = 2 +myst_footnote_transition = True +myst_dmath_double_inline = True +myst_enable_checkboxes = True +myst_substitutions = { + "role": "[role](#syntax/roles)", + "directive": "[directive](#syntax/directives)", +} + # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output @@ -46,3 +99,39 @@ "logo_only": False, "titles_only": True, } + +# -- Options for autodoc ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html + +# Make source file directories available to sphinx +sys.path.insert(0, str(Path("..", "src").resolve())) + +autodoc_default_options = { + "members": "var1, var2", + "member-order": "bysource", + "special-members": "__init__", + "undoc-members": True, + "exclude-members": "__weakref__", +} + +# -- Options for autosummary ------------------------------------------------- +autosummary_generate = True + +# -- Options for openapi ----------------------------------------------------- +openapi_default_renderer = "httpdomain:old" + +# -- Options for napoleon ------------------------------------------------- +napoleon_google_docstring = True +napoleon_numpy_docstring = False +napoleon_include_init_with_doc = False +napoleon_include_private_with_doc = False +napoleon_include_special_with_doc = True +napoleon_use_admonition_for_examples = False +napoleon_use_admonition_for_notes = False +napoleon_use_admonition_for_references = False +napoleon_use_ivar = False +napoleon_use_param = True +napoleon_use_rtype = True +napoleon_preprocess_types = False +napoleon_type_aliases = None +napoleon_attr_annotations = True diff --git a/docs/develop/getting_started.rst b/docs/develop/getting_started.md similarity index 56% rename from docs/develop/getting_started.rst rename to docs/develop/getting_started.md index 8256c514..406c973a 100644 --- a/docs/develop/getting_started.rst +++ b/docs/develop/getting_started.md @@ -1,20 +1,15 @@ -.. - SPDX-License-Identifier: Apache-2.0 +% SPDX-License-Identifier: Apache-2.0 -.. _akkudoktoreos_getting_started: +# Getting Started -Getting Started -############### +## Installation -Installation -************ - -`Good installation guide `_ +Good installation guide: +https://meintechblog.de/2024/09/05/andreas-schmitz-joerg-installiert-mein-energieoptimierungssystem/ The project requires Python 3.10 or newer. -Quick Start Guide ------------------ +### Quick Start Guide On Linux (Ubuntu/Debian): @@ -22,7 +17,7 @@ On Linux (Ubuntu/Debian): sudo apt install make ``` -On MacOS (requires `Homebrew `_): +On MacOS (requires [Homebrew](https://brew.sh)): ```zsh brew install make @@ -31,8 +26,7 @@ brew install make Next, adjust `config.py`. The server can then be started with `make run`. A full overview of the main shortcuts is given by `make help`. -Detailed Instructions ---------------------- +### Detailed Instructions All necessary dependencies can be installed via `pip`. Clone the repository and install the required packages with: @@ -63,22 +57,12 @@ source .venv/bin/activate ```zsh . .venv/bin/activate ``` -(if using zsh, primarily for MacOS users). - -If `pip install` fails to install the mariadb dependency, the following commands may help: - -* Debian/Ubuntu: `sudo apt-get install -y libmariadb-dev` -* MacOS/Homebrew: `brew install mariadb-connector-c` -Followed by a renewed `pip install -r requirements.txt`. - -Usage -***** +## Usage Adjust `config.py`. - -To start the server: +To use the system, run `flask_server.py`, which starts the server: ```bash -make run +./flask_server.py ``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..e47d1569 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,25 @@ +% SPDX-License-Identifier: Apache-2.0 + +```{image} _static/logo.png + +``` + +# Akkudoktor EOS documentation + +```{toctree} +:maxdepth: 2 +:caption: 'Contents:' + +welcome.md +akkudoktoreos/about.md +develop/getting_started.md +develop/CONTRIBUTING.md +akkudoktoreosserver/serverapi.rst +akkudoktoreos/api.rst +``` + +# Indices and tables + +- {ref}`genindex` +- {ref}`modindex` +- {ref}`search` diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 2986047a..00000000 --- a/docs/index.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. - Copyright (c) 2024 Bobby Noelte - SPDX-License-Identifier: Apache-2.0 - -.. _akkudoktoreos: - -Akkudoktor EOS documentation -============================ - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - welcome - akkudoktoreos/about - develop/getting_started - develop/CONTRIBUTING diff --git a/docs/welcome.md b/docs/welcome.md new file mode 100644 index 00000000..688fd141 --- /dev/null +++ b/docs/welcome.md @@ -0,0 +1,14 @@ +% SPDX-License-Identifier: Apache-2.0 + +# Welcome to the EOS documentation! + +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) +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 +[Read the Docs](https://akkudoktoreos.readthedocs.io/en/latest/index.html). + +You can contribute to EOS's documentation by opening +[GitHub issues](https://github.com/Akkudoktor-EOS/EOS/issues) +or sending patches via pull requests on its +[GitHub repository](https://github.com/Akkudoktor-EOS/EOS). diff --git a/docs/welcome.rst b/docs/welcome.rst deleted file mode 100644 index 9e7ac1ec..00000000 --- a/docs/welcome.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. - Copyright (c) 2024 Bobby Noelte - SPDX-License-Identifier: Apache-2.0 - -.. _akkudoktoreos_welcome: - -Welcome to the EOS documentation! -################################# - -This documentation is continuously written. It is edited via text files in the -`reStructuredText(reST) `_ -markup language and then compiled into a static website/offline document using the open source tool -`Sphinx `_ and will hopefully someday land on -`Read the Docs `_. - -You can contribute to EOS's documentation by opening -`GitHub issues `_ -or sending patches via pull requests on its -`GitHub repository `_. diff --git a/pyproject.toml b/pyproject.toml index 08e91730..18c870f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,10 +45,29 @@ profile = "black" line-length = 100 [tool.ruff.lint] +select = [ + "F", # Enable all `Pyflakes` rules. + "D", # Enable all `pydocstyle` rules, limiting to those that adhere to the + # Google convention via `convention = "google"`, below. +] ignore = [ - "F841", # don't complain about unused variables + # On top of `Pyflakes (F)` to prevent errors for existing sources. Should be removed!!! + "F841", # unused-variable: Local variable {name} is assigned to but never used + # On top of `pydocstyle (D)` to prevent errors for existing sources. Should be removed!!! + "D100", # undocumented-public-module: Missing docstring in public module + "D101", # undocumented-public-class: Missing docstring in public class + "D102", # undocumented-public-method: Missing docstring in public method + "D103", # undocumented-public-function: Missing docstring in public function + "D104", # undocumented-public-package: Missing docstring in public package + "D105", # undocumented-magic-method: Missing docstring in magic method + "D106", # undocumented-public-nested-class: Missing docstring in public nested class + "D107", # undocumented-public-init: Missing docstring in __init__ + "D417", # undocumented-param: Missing argument description in the docstring for {definition}: {name} ] +[tool.ruff.lint.pydocstyle] +convention = "google" + [tool.pytest.ini_options] minversion = "8.3.3" pythonpath = [ "src", ] diff --git a/requirements-dev.txt b/requirements-dev.txt index 2313211f..f2fb66e0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,9 @@ -r requirements.txt +linkify-it-py==2.0.3 myst-parser==4.0.0 sphinx==8.1.3 sphinx_rtd_theme==3.0.1 +sphinxcontrib-openapi==0.8.4 pytest==8.3.3 pytest-cov==5.0.0 pytest-xprocess==1.0.2 diff --git a/src/akkudoktoreos/class_akku.py b/src/akkudoktoreos/class_akku.py index 5778c510..a4c12b18 100644 --- a/src/akkudoktoreos/class_akku.py +++ b/src/akkudoktoreos/class_akku.py @@ -149,8 +149,8 @@ def energie_laden(self, wh, hour, relative_power=0.0): return geladene_menge, verluste_wh def aktueller_energieinhalt(self): - """ - This method returns the current remaining energy considering efficiency. + """This method returns the current remaining energy considering efficiency. + It accounts for both charging and discharging efficiency. """ # Calculate remaining energy considering discharge efficiency diff --git a/src/akkudoktoreos/class_ems.py b/src/akkudoktoreos/class_ems.py index b8b8b0a8..9900919c 100644 --- a/src/akkudoktoreos/class_ems.py +++ b/src/akkudoktoreos/class_ems.py @@ -54,12 +54,11 @@ def simuliere_ab_jetzt(self) -> dict: return self.simuliere(start_stunde) def simuliere(self, start_stunde: int) -> dict: - """ - hour: - akku_soc_pro_stunde begin of the hour, initial hour state! - last_wh_pro_stunde integral of last hour (end state) - """ + """hour. + akku_soc_pro_stunde begin of the hour, initial hour state! + last_wh_pro_stunde integral of last hour (end state) + """ lastkurve_wh = self.gesamtlast assert ( len(lastkurve_wh) == len(self.pv_prognose_wh) == len(self.strompreis_euro_pro_wh) diff --git a/src/akkudoktoreos/class_haushaltsgeraet.py b/src/akkudoktoreos/class_haushaltsgeraet.py index 7fbc64df..d15b4dd2 100644 --- a/src/akkudoktoreos/class_haushaltsgeraet.py +++ b/src/akkudoktoreos/class_haushaltsgeraet.py @@ -9,8 +9,8 @@ def __init__(self, hours=None, verbrauch_wh=None, dauer_h=None): self.lastkurve = np.zeros(self.hours) # Initialize the load curve with zeros def set_startzeitpunkt(self, start_hour, global_start_hour=0): - """ - Sets the start time of the device and generates the corresponding load curve. + """Sets the start time of the device and generates the corresponding load curve. + :param start_hour: The hour at which the device should start. """ self.reset() @@ -27,20 +27,16 @@ def set_startzeitpunkt(self, start_hour, global_start_hour=0): self.lastkurve[start_hour : start_hour + self.dauer_h] = leistung_pro_stunde def reset(self): - """ - Resets the load curve. - """ + """Resets the load curve.""" self.lastkurve = np.zeros(self.hours) def get_lastkurve(self): - """ - Returns the current load curve. - """ + """Returns the current load curve.""" return self.lastkurve def get_last_fuer_stunde(self, hour): - """ - Returns the load for a specific hour. + """Returns the load for a specific hour. + :param hour: The hour for which the load is queried. :return: The load in watts for the specified hour. """ @@ -50,7 +46,5 @@ def get_last_fuer_stunde(self, hour): return self.lastkurve[hour] def spaetestmoeglicher_startzeitpunkt(self): - """ - Returns the latest possible start time at which the device can still run completely. - """ + """Returns the latest possible start time at which the device can still run completely.""" return self.hours - self.dauer_h diff --git a/src/akkudoktoreos/class_load.py b/src/akkudoktoreos/class_load.py index 7e3bffc1..5bf2503d 100644 --- a/src/akkudoktoreos/class_load.py +++ b/src/akkudoktoreos/class_load.py @@ -14,8 +14,7 @@ def __init__(self, filepath=None, year_energy=None): self.load_data() def get_daily_stats(self, date_str): - """ - Returns the 24-hour profile with mean and standard deviation for a given date. + """Returns the 24-hour profile with mean and standard deviation for a given date. :param date_str: Date as a string in the format "YYYY-MM-DD" :return: An array with shape (2, 24), contains means and standard deviations @@ -31,8 +30,7 @@ def get_daily_stats(self, date_str): return daily_stats def get_hourly_stats(self, date_str, hour): - """ - Returns the mean and standard deviation for a specific hour of a given date. + """Returns the mean and standard deviation for a specific hour of a given date. :param date_str: Date as a string in the format "YYYY-MM-DD" :param hour: Specific hour (0 to 23) @@ -50,8 +48,7 @@ def get_hourly_stats(self, date_str, hour): return hourly_stats def get_stats_for_date_range(self, start_date_str, end_date_str): - """ - Returns the means and standard deviations for a date range. + """Returns the means and standard deviations for a date range. :param start_date_str: Start date as a string in the format "YYYY-MM-DD" :param end_date_str: End date as a string in the format "YYYY-MM-DD" diff --git a/src/akkudoktoreos/class_load_container.py b/src/akkudoktoreos/class_load_container.py index 13eccbc0..d19984c6 100644 --- a/src/akkudoktoreos/class_load_container.py +++ b/src/akkudoktoreos/class_load_container.py @@ -7,8 +7,7 @@ def __init__(self, prediction_hours=24): self.prediction_hours = prediction_hours def hinzufuegen(self, name, last_array): - """ - Adds an array of loads for a specific source. + """Adds an array of loads for a specific source. :param name: Name of the load source (e.g., "Household", "Heat Pump") :param last_array: Array of loads, where each entry corresponds to an hour @@ -18,8 +17,7 @@ def hinzufuegen(self, name, last_array): self.lasten[name] = last_array def gesamtlast_berechnen(self): - """ - Calculates the total load for each hour and returns an array of total loads. + """Calculates the total load for each hour and returns an array of total loads. :return: Array of total loads, where each entry corresponds to an hour """ diff --git a/src/akkudoktoreos/class_numpy_encoder.py b/src/akkudoktoreos/class_numpy_encoder.py index 3c42ad47..e2f77b7d 100644 --- a/src/akkudoktoreos/class_numpy_encoder.py +++ b/src/akkudoktoreos/class_numpy_encoder.py @@ -13,8 +13,7 @@ def default(self, obj): @staticmethod def dumps(data): - """ - Static method to serialize a Python object into a JSON string using NumpyEncoder. + """Static method to serialize a Python object into a JSON string using NumpyEncoder. Args: data: The Python object to serialize. diff --git a/src/akkudoktoreos/class_optimize.py b/src/akkudoktoreos/class_optimize.py index 05d1902c..d68bcdef 100644 --- a/src/akkudoktoreos/class_optimize.py +++ b/src/akkudoktoreos/class_optimize.py @@ -39,8 +39,8 @@ def __init__( def decode_charge_discharge( self, discharge_hours_bin: np.ndarray ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: - """ - Decode the input array `discharge_hours_bin` into three separate arrays for AC charging, DC charging, and discharge. + """Decode the input array `discharge_hours_bin` into three separate arrays for AC charging, DC charging, and discharge. + The function maps AC and DC charging values to relative power levels (0 to 1), while the discharge remains binary (0 or 1). Parameters: @@ -81,8 +81,9 @@ def decode_charge_discharge( # Custom mutation function that applies type-specific mutations def mutate(self, individual): - """ - Custom mutation function for the individual. This function mutates different parts of the individual: + """Custom mutation function for the individual. + + This function mutates different parts of the individual: - Mutates the discharge and charge states (AC, DC, idle) using the split_charge_discharge method. - Mutates the EV charging schedule if EV optimization is enabled. - Mutates appliance start times if household appliances are part of the optimization. @@ -93,7 +94,6 @@ def mutate(self, individual): Returns: - (tuple): The mutated individual as a tuple (required by DEAP). """ - # Step 1: Mutate the charge/discharge states (idle, discharge, AC charge, DC charge) # Extract the relevant part of the individual for prediction hours, which represents the charge/discharge behavior. charge_discharge_part = individual[: self.prediction_hours] @@ -167,13 +167,13 @@ def create_individual(self): def split_individual( self, individual: List[float] ) -> Tuple[List[int], List[float], Optional[int]]: - """ - Split the individual solution into its components: - 1. Discharge hours (-1 (Charge),0 (Nothing),1 (Discharge)), - 2. Electric vehicle charge hours (possible_charge_values), + """Split the individual solution into its components. + + Components: + 1. Discharge hours (binary), + 2. Electric vehicle charge hours (float), 3. Dishwasher start time (integer if applicable). """ - discharge_hours_bin = individual[: self.prediction_hours] eautocharge_hours_float = ( individual[self.prediction_hours : self.prediction_hours * 2] @@ -189,9 +189,7 @@ def split_individual( return discharge_hours_bin, eautocharge_hours_float, spuelstart_int def setup_deap_environment(self, opti_param: Dict[str, Any], start_hour: int) -> None: - """ - Set up the DEAP environment with fitness and individual creation rules. - """ + """Set up the DEAP environment with fitness and individual creation rules.""" self.opti_param = opti_param # Remove existing FitnessMin and Individual classes from creator if present @@ -252,9 +250,9 @@ def setup_deap_environment(self, opti_param: Dict[str, Any], start_hour: int) -> def evaluate_inner( self, individual: List[float], ems: EnergieManagementSystem, start_hour: int ) -> Dict[str, Any]: - """ - Internal evaluation function that simulates the energy management system (EMS) - using the provided individual solution. + """Simulates the energy management system (EMS) using the provided individual solution. + + This is an internal function. """ ems.reset() discharge_hours_bin, eautocharge_hours_index, spuelstart_int = self.split_individual( @@ -288,9 +286,7 @@ def evaluate( start_hour: int, worst_case: bool, ) -> Tuple[float]: - """ - Evaluate the fitness of an individual solution based on the simulation results. - """ + """Evaluate the fitness of an individual solution based on the simulation results.""" try: o = self.evaluate_inner(individual, ems, start_hour) except Exception as e: @@ -381,9 +377,7 @@ def optimierung_ems( *, ngen: int = 600, ) -> Dict[str, Any]: - """ - Perform EMS (Energy Management System) optimization and visualize results. - """ + """Perform EMS (Energy Management System) optimization and visualize results.""" einspeiseverguetung_euro_pro_wh = np.full( self.prediction_hours, parameter["einspeiseverguetung_euro_pro_wh"] ) diff --git a/src/akkudoktoreos/heatpump.py b/src/akkudoktoreos/heatpump.py index 694305ea..bc506e55 100644 --- a/src/akkudoktoreos/heatpump.py +++ b/src/akkudoktoreos/heatpump.py @@ -35,8 +35,9 @@ def __check_outside_temperature_range__(self, temp_celsius: float) -> bool: return temp_celsius > -100 and temp_celsius < 100 def calculate_cop(self, outside_temperature_celsius: float) -> float: - """Calculate the coefficient of performance (COP) based on outside temperature. Supported - temperate range -100 degree Celsius to 100 degree Celsius. + """Calculate the coefficient of performance (COP) based on outside temperature. + + Supported temperate range -100 degree Celsius to 100 degree Celsius. Args: outside_temperature_celsius: Outside temperature in degree Celsius @@ -59,6 +60,7 @@ def calculate_cop(self, outside_temperature_celsius: float) -> float: def calculate_heating_output(self, outside_temperature_celsius: float) -> float: """Calculate the heating output in Watts based on outside temperature in degree Celsius. + Temperature range must be between -100 and 100 degree Celsius. Args: diff --git a/tests/test_class_ems.py b/tests/test_class_ems.py index 7b92173b..a24b66f5 100644 --- a/tests/test_class_ems.py +++ b/tests/test_class_ems.py @@ -14,9 +14,7 @@ # Example initialization of necessary components @pytest.fixture def create_ems_instance(): - """ - Fixture to create an EnergieManagementSystem instance with given test parameters. - """ + """Fixture to create an EnergieManagementSystem instance with given test parameters.""" # Initialize the battery and the inverter akku = PVAkku(kapazitaet_wh=5000, start_soc_prozent=80, hours=48, min_soc_prozent=10) akku.reset() @@ -204,9 +202,7 @@ def create_ems_instance(): def test_simulation(create_ems_instance): - """ - Test the EnergieManagementSystem simulation method. - """ + """Test the EnergieManagementSystem simulation method.""" ems = create_ems_instance # Simulate starting from hour 1 (this value can be adjusted) diff --git a/tests/test_class_ems_2.py b/tests/test_class_ems_2.py index 61c0be59..e2493adf 100644 --- a/tests/test_class_ems_2.py +++ b/tests/test_class_ems_2.py @@ -14,9 +14,7 @@ # Example initialization of necessary components @pytest.fixture def create_ems_instance(): - """ - Fixture to create an EnergieManagementSystem instance with given test parameters. - """ + """Fixture to create an EnergieManagementSystem instance with given test parameters.""" # Initialize the battery and the inverter akku = PVAkku(kapazitaet_wh=5000, start_soc_prozent=80, hours=48, min_soc_prozent=10) akku.reset() @@ -118,9 +116,7 @@ def create_ems_instance(): def test_simulation(create_ems_instance): - """ - Test the EnergieManagementSystem simulation method. - """ + """Test the EnergieManagementSystem simulation method.""" ems = create_ems_instance # Simulate starting from hour 0 (this value can be adjusted) diff --git a/tests/test_heatpump.py b/tests/test_heatpump.py index e32bfd55..b3385d54 100644 --- a/tests/test_heatpump.py +++ b/tests/test_heatpump.py @@ -5,13 +5,13 @@ @pytest.fixture(scope="function") def hp_5kw_24h() -> Heatpump: - """Heatpump with 5 kw heating power and 24 h prediction""" + """Heatpump with 5 kw heating power and 24 h prediction.""" return Heatpump(5000, 24) class TestHeatpump: def test_cop(self, hp_5kw_24h: Heatpump): - """Testing calculate COP for variouse outside temperatures""" + """Testing calculate COP for variouse outside temperatures.""" assert hp_5kw_24h.calculate_cop(-10) == 2.0 assert hp_5kw_24h.calculate_cop(0) == 3.0 assert hp_5kw_24h.calculate_cop(10) == 4.0 @@ -24,13 +24,13 @@ def test_cop(self, hp_5kw_24h: Heatpump): hp_5kw_24h.calculate_cop(out_temp_max) def test_heating_output(self, hp_5kw_24h: Heatpump): - """Testing calculate of heating output""" + """Testing calculate of heating output.""" assert hp_5kw_24h.calculate_heating_output(-10.0) == 5000 assert hp_5kw_24h.calculate_heating_output(0.0) == 5000 assert hp_5kw_24h.calculate_heating_output(10.0) == pytest.approx(4939.583) def test_heating_power(self, hp_5kw_24h: Heatpump): - """Testing calculation of heating power""" + """Testing calculation of heating power.""" assert hp_5kw_24h.calculate_heat_power(-10.0) == 2104 assert hp_5kw_24h.calculate_heat_power(0.0) == 1164 assert hp_5kw_24h.calculate_heat_power(10.0) == 548 diff --git a/tests/test_server.py b/tests/test_server.py index 4226f021..fbf027a7 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -4,9 +4,7 @@ def test_server(server): - """ - Test the server - """ + """Test the server.""" result = requests.get(f"{server}/gesamtlast_simple?year_energy=2000&") assert result.status_code == 200 assert len(result.json()) == prediction_hours