mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2025-04-19 08:55:15 +00:00
translation of battery.py v3 (#262)
This commit is contained in:
parent
0e122a9a49
commit
5f898e8aab
11
README.md
11
README.md
@ -17,12 +17,14 @@ Following sections describe how to locally start the EOS server on `http://local
|
|||||||
Install dependencies in virtual environment:
|
Install dependencies in virtual environment:
|
||||||
|
|
||||||
Linux:
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
Windows:
|
Windows:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python -m venv .venv
|
python -m venv .venv
|
||||||
.venv\Scripts\pip install -r requirements.txt
|
.venv\Scripts\pip install -r requirements.txt
|
||||||
@ -31,13 +33,17 @@ python -m venv .venv
|
|||||||
Finally, start EOS fastapi server:
|
Finally, start EOS fastapi server:
|
||||||
|
|
||||||
Linux:
|
Linux:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
.venv/bin/fastapi run --port 8503 src/akkudoktoreos/server/fastapi_server.py
|
.venv/bin/fastapi run --port 8503 src/akkudoktoreos/server/fastapi_server.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Windows:
|
Windows:
|
||||||
|
|
||||||
```
|
```
|
||||||
.venv\Scripts\fastapi run --port 8503 src/akkudoktoreos/server/fastapi_server.py
|
.venv\Scripts\fastapi run --port 8503 src/akkudoktoreos/server/fastapi_server.py
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -67,7 +73,7 @@ If the configuration keys in the `EOS.config.json` file are missing or different
|
|||||||
|
|
||||||
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:
|
||||||
|
|
||||||
- `PVAkku`: 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.
|
||||||
|
|
||||||
@ -89,7 +95,6 @@ Each class is designed to be easily customized and extended to integrate additio
|
|||||||
|
|
||||||
See the Swagger API documentation for detailed information: [EOS OpenAPI Spec](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/Akkudoktor-EOS/EOS/refs/heads/main/docs/akkudoktoreos/openapi.json)
|
See the Swagger API documentation for detailed information: [EOS OpenAPI Spec](https://petstore3.swagger.io/?url=https://raw.githubusercontent.com/Akkudoktor-EOS/EOS/refs/heads/main/docs/akkudoktoreos/openapi.json)
|
||||||
|
|
||||||
|
|
||||||
## Further resources
|
## Further resources
|
||||||
|
|
||||||
- [Installation guide (de)](https://meintechblog.de/2024/09/05/andreas-schmitz-joerg-installiert-mein-energieoptimierungssystem/)
|
- [Installation guide (de)](https://meintechblog.de/2024/09/05/andreas-schmitz-joerg-installiert-mein-energieoptimierungssystem/)
|
||||||
|
@ -2091,7 +2091,7 @@
|
|||||||
"title": "Battery Capacity",
|
"title": "Battery Capacity",
|
||||||
"description": "Battery capacity [Wh]."
|
"description": "Battery capacity [Wh]."
|
||||||
},
|
},
|
||||||
"battery_soc_start": {
|
"battery_initial_soc": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -2100,7 +2100,7 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Battery Soc Start",
|
"title": "Battery Initial Soc",
|
||||||
"description": "Battery initial state of charge [%]."
|
"description": "Battery initial state of charge [%]."
|
||||||
},
|
},
|
||||||
"battery_soc_min": {
|
"battery_soc_min": {
|
||||||
@ -2127,7 +2127,7 @@
|
|||||||
"title": "Battery Soc Max",
|
"title": "Battery Soc Max",
|
||||||
"description": "Battery maximum state of charge [%]."
|
"description": "Battery maximum state of charge [%]."
|
||||||
},
|
},
|
||||||
"battery_charge_efficiency": {
|
"battery_charging_efficiency": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "number"
|
||||||
@ -2136,10 +2136,10 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Battery Charge Efficiency",
|
"title": "Battery Charging Efficiency",
|
||||||
"description": "Battery charging efficiency [%]."
|
"description": "Battery charging efficiency [%]."
|
||||||
},
|
},
|
||||||
"battery_discharge_efficiency": {
|
"battery_discharging_efficiency": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "number"
|
||||||
@ -2148,10 +2148,10 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Battery Discharge Efficiency",
|
"title": "Battery Discharging Efficiency",
|
||||||
"description": "Battery discharging efficiency [%]."
|
"description": "Battery discharging efficiency [%]."
|
||||||
},
|
},
|
||||||
"battery_charge_power_max": {
|
"battery_max_charging_power": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -2160,7 +2160,7 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Battery Charge Power Max",
|
"title": "Battery Max Charging Power",
|
||||||
"description": "Battery maximum charge power [W]."
|
"description": "Battery maximum charge power [W]."
|
||||||
},
|
},
|
||||||
"bev_provider": {
|
"bev_provider": {
|
||||||
@ -2187,7 +2187,7 @@
|
|||||||
"title": "Bev Capacity",
|
"title": "Bev Capacity",
|
||||||
"description": "Battery Electric Vehicle capacity [Wh]."
|
"description": "Battery Electric Vehicle capacity [Wh]."
|
||||||
},
|
},
|
||||||
"bev_soc_start": {
|
"bev_initial_soc": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -2196,7 +2196,7 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Bev Soc Start",
|
"title": "Bev Initial Soc",
|
||||||
"description": "Battery Electric Vehicle initial state of charge [%]."
|
"description": "Battery Electric Vehicle initial state of charge [%]."
|
||||||
},
|
},
|
||||||
"bev_soc_max": {
|
"bev_soc_max": {
|
||||||
@ -2211,7 +2211,7 @@
|
|||||||
"title": "Bev Soc Max",
|
"title": "Bev Soc Max",
|
||||||
"description": "Battery Electric Vehicle maximum state of charge [%]."
|
"description": "Battery Electric Vehicle maximum state of charge [%]."
|
||||||
},
|
},
|
||||||
"bev_charge_efficiency": {
|
"bev_charging_efficiency": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "number"
|
||||||
@ -2220,10 +2220,10 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Bev Charge Efficiency",
|
"title": "Bev Charging Efficiency",
|
||||||
"description": "Battery Electric Vehicle charging efficiency [%]."
|
"description": "Battery Electric Vehicle charging efficiency [%]."
|
||||||
},
|
},
|
||||||
"bev_discharge_efficiency": {
|
"bev_discharging_efficiency": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "number"
|
||||||
@ -2232,10 +2232,10 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Bev Discharge Efficiency",
|
"title": "Bev Discharging Efficiency",
|
||||||
"description": "Battery Electric Vehicle discharging efficiency [%]."
|
"description": "Battery Electric Vehicle discharging efficiency [%]."
|
||||||
},
|
},
|
||||||
"bev_charge_power_max": {
|
"bev_max_charging_power": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -2244,7 +2244,7 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Bev Charge Power Max",
|
"title": "Bev Max Charging Power",
|
||||||
"description": "Battery Electric Vehicle maximum charge power [W]."
|
"description": "Battery Electric Vehicle maximum charge power [W]."
|
||||||
},
|
},
|
||||||
"dishwasher_provider": {
|
"dishwasher_provider": {
|
||||||
@ -2485,28 +2485,28 @@
|
|||||||
"title": "ConfigEOS",
|
"title": "ConfigEOS",
|
||||||
"description": "Singleton configuration handler for the EOS application.\n\nConfigEOS extends `SettingsEOS` with support for default configuration paths and automatic\ninitialization.\n\n`ConfigEOS` ensures that only one instance of the class is created throughout the application,\nallowing consistent access to EOS configuration settings. This singleton instance loads\nconfiguration data from a predefined set of directories or creates a default configuration if\nnone is found.\n\nInitialization Process:\n - Upon instantiation, the singleton instance attempts to load a configuration file in this order:\n 1. The directory specified by the `EOS_DIR` environment variable.\n 2. A platform specific default directory for EOS.\n 3. The current working directory.\n - The first available configuration file found in these directories is loaded.\n - If no configuration file is found, a default configuration file is created in the platform\n specific default directory, and default settings are loaded into it.\n\nAttributes from the loaded configuration are accessible directly as instance attributes of\n`ConfigEOS`, providing a centralized, shared configuration object for EOS.\n\nSingleton Behavior:\n - This class uses the `SingletonMixin` to ensure that all requests for `ConfigEOS` return\n the same instance, which contains the most up-to-date configuration. Modifying the configuration\n in one part of the application reflects across all references to this class.\n\nAttributes:\n _settings (ClassVar[SettingsEOS]): Holds application-wide settings.\n _file_settings (ClassVar[SettingsEOS]): Stores configuration loaded from file.\n config_folder_path (Optional[Path]): Path to the configuration directory.\n config_file_path (Optional[Path]): Path to the configuration file.\n\nRaises:\n FileNotFoundError: If no configuration file is found, and creating a default configuration fails.\n\nExample:\n To initialize and access configuration attributes (only one instance is created):\n ```python\n config_eos = ConfigEOS() # Always returns the same instance\n print(config_eos.prediction_hours) # Access a setting from the loaded configuration\n ```"
|
"description": "Singleton configuration handler for the EOS application.\n\nConfigEOS extends `SettingsEOS` with support for default configuration paths and automatic\ninitialization.\n\n`ConfigEOS` ensures that only one instance of the class is created throughout the application,\nallowing consistent access to EOS configuration settings. This singleton instance loads\nconfiguration data from a predefined set of directories or creates a default configuration if\nnone is found.\n\nInitialization Process:\n - Upon instantiation, the singleton instance attempts to load a configuration file in this order:\n 1. The directory specified by the `EOS_DIR` environment variable.\n 2. A platform specific default directory for EOS.\n 3. The current working directory.\n - The first available configuration file found in these directories is loaded.\n - If no configuration file is found, a default configuration file is created in the platform\n specific default directory, and default settings are loaded into it.\n\nAttributes from the loaded configuration are accessible directly as instance attributes of\n`ConfigEOS`, providing a centralized, shared configuration object for EOS.\n\nSingleton Behavior:\n - This class uses the `SingletonMixin` to ensure that all requests for `ConfigEOS` return\n the same instance, which contains the most up-to-date configuration. Modifying the configuration\n in one part of the application reflects across all references to this class.\n\nAttributes:\n _settings (ClassVar[SettingsEOS]): Holds application-wide settings.\n _file_settings (ClassVar[SettingsEOS]): Stores configuration loaded from file.\n config_folder_path (Optional[Path]): Path to the configuration directory.\n config_file_path (Optional[Path]): Path to the configuration file.\n\nRaises:\n FileNotFoundError: If no configuration file is found, and creating a default configuration fails.\n\nExample:\n To initialize and access configuration attributes (only one instance is created):\n ```python\n config_eos = ConfigEOS() # Always returns the same instance\n print(config_eos.prediction_hours) # Access a setting from the loaded configuration\n ```"
|
||||||
},
|
},
|
||||||
"EAutoParameters": {
|
"ElectricVehicleParameters": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"kapazitaet_wh": {
|
"capacity_wh": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"exclusiveMinimum": 0.0,
|
"exclusiveMinimum": 0.0,
|
||||||
"title": "Kapazitaet Wh",
|
"title": "Capacity Wh",
|
||||||
"description": "An integer representing the capacity of the battery in watt-hours."
|
"description": "An integer representing the capacity of the battery in watt-hours."
|
||||||
},
|
},
|
||||||
"lade_effizienz": {
|
"charging_efficiency": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"maximum": 1.0,
|
"maximum": 1.0,
|
||||||
"exclusiveMinimum": 0.0,
|
"exclusiveMinimum": 0.0,
|
||||||
"title": "Lade Effizienz",
|
"title": "Charging Efficiency",
|
||||||
"description": "A float representing the charging efficiency of the battery.",
|
"description": "A float representing the charging efficiency of the battery.",
|
||||||
"default": 0.88
|
"default": 0.88
|
||||||
},
|
},
|
||||||
"entlade_effizienz": {
|
"discharging_efficiency": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"title": "Entlade Effizienz",
|
"title": "Discharging Efficiency",
|
||||||
"default": 1.0
|
"default": 1.0
|
||||||
},
|
},
|
||||||
"max_ladeleistung_w": {
|
"max_charge_power_w": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "number",
|
"type": "number",
|
||||||
@ -2516,38 +2516,41 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Max Ladeleistung W",
|
"title": "Max Charge Power W",
|
||||||
"description": "An integer representing the charging power of the battery in watts."
|
"description": "Maximum charging power in watts.",
|
||||||
|
"default": 5000
|
||||||
},
|
},
|
||||||
"start_soc_prozent": {
|
"initial_soc_percentage": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 100.0,
|
"maximum": 100.0,
|
||||||
"minimum": 0.0,
|
"minimum": 0.0,
|
||||||
"title": "Start Soc Prozent",
|
"title": "Initial Soc Percentage",
|
||||||
"description": "An integer representing the current state of charge (SOC) of the battery in percentage.",
|
"description": "An integer representing the current state of charge (SOC) of the battery in percentage.",
|
||||||
"default": 0
|
"default": 0
|
||||||
},
|
},
|
||||||
"min_soc_prozent": {
|
"min_soc_percentage": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 100.0,
|
"maximum": 100.0,
|
||||||
"minimum": 0.0,
|
"minimum": 0.0,
|
||||||
"title": "Min Soc Prozent",
|
"title": "Min Soc Percentage",
|
||||||
"description": "An integer representing the minimum state of charge (SOC) of the battery in percentage.",
|
"description": "An integer representing the minimum state of charge (SOC) of the battery in percentage.",
|
||||||
"default": 0
|
"default": 0
|
||||||
},
|
},
|
||||||
"max_soc_prozent": {
|
"max_soc_percentage": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"maximum": 100.0,
|
"maximum": 100.0,
|
||||||
"minimum": 0.0,
|
"minimum": 0.0,
|
||||||
"title": "Max Soc Prozent",
|
"title": "Max Soc Percentage",
|
||||||
|
"description": "An integer representing the maximum state of charge (SOC) of the battery in percentage.",
|
||||||
"default": 100
|
"default": 100
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": ["kapazitaet_wh"],
|
"required": ["capacity_wh"],
|
||||||
"title": "EAutoParameters"
|
"title": "ElectricVehicleParameters",
|
||||||
|
"description": "Parameters specific to an electric vehicle (EV)."
|
||||||
},
|
},
|
||||||
"EAutoResult": {
|
"ElectricVehicleResult": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"charge_array": {
|
"charge_array": {
|
||||||
"items": {
|
"items": {
|
||||||
@ -2555,7 +2558,7 @@
|
|||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"title": "Charge Array",
|
"title": "Charge Array",
|
||||||
"description": "Indicates for each hour whether the EV is charging (`0` for no charging, `1` for charging)."
|
"description": "Hourly charging status (0 for no charging, 1 for charging)."
|
||||||
},
|
},
|
||||||
"discharge_array": {
|
"discharge_array": {
|
||||||
"items": {
|
"items": {
|
||||||
@ -2563,58 +2566,58 @@
|
|||||||
},
|
},
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"title": "Discharge Array",
|
"title": "Discharge Array",
|
||||||
"description": "Indicates for each hour whether the EV is discharging (`0` for no discharging, `1` for discharging)."
|
"description": "Hourly discharging status (0 for no discharging, 1 for discharging)."
|
||||||
},
|
},
|
||||||
"entlade_effizienz": {
|
"discharging_efficiency": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"title": "Entlade Effizienz",
|
"title": "Discharging Efficiency",
|
||||||
"description": "The discharge efficiency as a float."
|
"description": "The discharge efficiency as a float.."
|
||||||
},
|
},
|
||||||
"hours": {
|
"hours": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"title": "Hours",
|
"title": "Hours",
|
||||||
"description": "Amount of hours the simulation is done for."
|
"description": "Number of hours in the simulation."
|
||||||
},
|
},
|
||||||
"kapazitaet_wh": {
|
"capacity_wh": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"title": "Kapazitaet Wh",
|
"title": "Capacity Wh",
|
||||||
"description": "The capacity of the EV\u2019s battery in watt-hours."
|
"description": "Capacity of the EV\u2019s battery in watt-hours."
|
||||||
},
|
},
|
||||||
"lade_effizienz": {
|
"charging_efficiency": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"title": "Lade Effizienz",
|
"title": "Charging Efficiency",
|
||||||
"description": "The charging efficiency as a float."
|
"description": "Charging efficiency as a float.."
|
||||||
},
|
},
|
||||||
"max_ladeleistung_w": {
|
"max_charge_power_w": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"title": "Max Ladeleistung W",
|
"title": "Max Charge Power W",
|
||||||
"description": "The maximum charging power of the EV in watts."
|
"description": "Maximum charging power in watts."
|
||||||
},
|
},
|
||||||
"soc_wh": {
|
"soc_wh": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"title": "Soc Wh",
|
"title": "Soc Wh",
|
||||||
"description": "The state of charge of the battery in watt-hours at the start of the simulation."
|
"description": "State of charge of the battery in watt-hours at the start of the simulation."
|
||||||
},
|
},
|
||||||
"start_soc_prozent": {
|
"initial_soc_percentage": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"title": "Start Soc Prozent",
|
"title": "Initial Soc Percentage",
|
||||||
"description": "The state of charge of the battery in percentage at the start of the simulation."
|
"description": "State of charge at the start of the simulation in percentage."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
"charge_array",
|
"charge_array",
|
||||||
"discharge_array",
|
"discharge_array",
|
||||||
"entlade_effizienz",
|
"discharging_efficiency",
|
||||||
"hours",
|
"hours",
|
||||||
"kapazitaet_wh",
|
"capacity_wh",
|
||||||
"lade_effizienz",
|
"charging_efficiency",
|
||||||
"max_ladeleistung_w",
|
"max_charge_power_w",
|
||||||
"soc_wh",
|
"soc_wh",
|
||||||
"start_soc_prozent"
|
"initial_soc_percentage"
|
||||||
],
|
],
|
||||||
"title": "EAutoResult",
|
"title": "ElectricVehicleResult",
|
||||||
"description": "This object contains information related to the electric vehicle and its charging and discharging behavior."
|
"description": "Result class containing information related to the electric vehicle's charging and discharging behavior."
|
||||||
},
|
},
|
||||||
"EnergieManagementSystemParameters": {
|
"EnergieManagementSystemParameters": {
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -2766,7 +2769,7 @@
|
|||||||
"$ref": "#/components/schemas/EnergieManagementSystemParameters"
|
"$ref": "#/components/schemas/EnergieManagementSystemParameters"
|
||||||
},
|
},
|
||||||
"pv_akku": {
|
"pv_akku": {
|
||||||
"$ref": "#/components/schemas/PVAkkuParameters"
|
"$ref": "#/components/schemas/SolarPanelBatteryParameters"
|
||||||
},
|
},
|
||||||
"inverter": {
|
"inverter": {
|
||||||
"$ref": "#/components/schemas/InverterParameters",
|
"$ref": "#/components/schemas/InverterParameters",
|
||||||
@ -2777,7 +2780,7 @@
|
|||||||
"eauto": {
|
"eauto": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/EAutoParameters"
|
"$ref": "#/components/schemas/ElectricVehicleParameters"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "null"
|
"type": "null"
|
||||||
@ -2876,7 +2879,7 @@
|
|||||||
"eauto_obj": {
|
"eauto_obj": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/EAutoResult"
|
"$ref": "#/components/schemas/ElectricVehicleResult"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "null"
|
"type": "null"
|
||||||
@ -2923,71 +2926,6 @@
|
|||||||
"title": "OptimizeResponse",
|
"title": "OptimizeResponse",
|
||||||
"description": "**Note**: The first value of \"Last_Wh_per_hour\", \"Netzeinspeisung_Wh_per_hour\", and \"Netzbezug_Wh_per_hour\", 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."
|
"description": "**Note**: The first value of \"Last_Wh_per_hour\", \"Netzeinspeisung_Wh_per_hour\", and \"Netzbezug_Wh_per_hour\", 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"
|
|
||||||
},
|
|
||||||
"SettingsEOS": {
|
"SettingsEOS": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"server_fastapi_host": {
|
"server_fastapi_host": {
|
||||||
@ -4568,7 +4506,7 @@
|
|||||||
"title": "Battery Capacity",
|
"title": "Battery Capacity",
|
||||||
"description": "Battery capacity [Wh]."
|
"description": "Battery capacity [Wh]."
|
||||||
},
|
},
|
||||||
"battery_soc_start": {
|
"battery_initial_soc": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -4577,7 +4515,7 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Battery Soc Start",
|
"title": "Battery Initial Soc",
|
||||||
"description": "Battery initial state of charge [%]."
|
"description": "Battery initial state of charge [%]."
|
||||||
},
|
},
|
||||||
"battery_soc_min": {
|
"battery_soc_min": {
|
||||||
@ -4604,7 +4542,7 @@
|
|||||||
"title": "Battery Soc Max",
|
"title": "Battery Soc Max",
|
||||||
"description": "Battery maximum state of charge [%]."
|
"description": "Battery maximum state of charge [%]."
|
||||||
},
|
},
|
||||||
"battery_charge_efficiency": {
|
"battery_charging_efficiency": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "number"
|
||||||
@ -4613,10 +4551,10 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Battery Charge Efficiency",
|
"title": "Battery Charging Efficiency",
|
||||||
"description": "Battery charging efficiency [%]."
|
"description": "Battery charging efficiency [%]."
|
||||||
},
|
},
|
||||||
"battery_discharge_efficiency": {
|
"battery_discharging_efficiency": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "number"
|
||||||
@ -4625,10 +4563,10 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Battery Discharge Efficiency",
|
"title": "Battery Discharging Efficiency",
|
||||||
"description": "Battery discharging efficiency [%]."
|
"description": "Battery discharging efficiency [%]."
|
||||||
},
|
},
|
||||||
"battery_charge_power_max": {
|
"battery_max_charging_power": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -4637,7 +4575,7 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Battery Charge Power Max",
|
"title": "Battery Max Charging Power",
|
||||||
"description": "Battery maximum charge power [W]."
|
"description": "Battery maximum charge power [W]."
|
||||||
},
|
},
|
||||||
"bev_provider": {
|
"bev_provider": {
|
||||||
@ -4664,7 +4602,7 @@
|
|||||||
"title": "Bev Capacity",
|
"title": "Bev Capacity",
|
||||||
"description": "Battery Electric Vehicle capacity [Wh]."
|
"description": "Battery Electric Vehicle capacity [Wh]."
|
||||||
},
|
},
|
||||||
"bev_soc_start": {
|
"bev_initial_soc": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -4673,7 +4611,7 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Bev Soc Start",
|
"title": "Bev Initial Soc",
|
||||||
"description": "Battery Electric Vehicle initial state of charge [%]."
|
"description": "Battery Electric Vehicle initial state of charge [%]."
|
||||||
},
|
},
|
||||||
"bev_soc_max": {
|
"bev_soc_max": {
|
||||||
@ -4688,7 +4626,7 @@
|
|||||||
"title": "Bev Soc Max",
|
"title": "Bev Soc Max",
|
||||||
"description": "Battery Electric Vehicle maximum state of charge [%]."
|
"description": "Battery Electric Vehicle maximum state of charge [%]."
|
||||||
},
|
},
|
||||||
"bev_charge_efficiency": {
|
"bev_charging_efficiency": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "number"
|
||||||
@ -4697,10 +4635,10 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Bev Charge Efficiency",
|
"title": "Bev Charging Efficiency",
|
||||||
"description": "Battery Electric Vehicle charging efficiency [%]."
|
"description": "Battery Electric Vehicle charging efficiency [%]."
|
||||||
},
|
},
|
||||||
"bev_discharge_efficiency": {
|
"bev_discharging_efficiency": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "number"
|
"type": "number"
|
||||||
@ -4709,10 +4647,10 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Bev Discharge Efficiency",
|
"title": "Bev Discharging Efficiency",
|
||||||
"description": "Battery Electric Vehicle discharging efficiency [%]."
|
"description": "Battery Electric Vehicle discharging efficiency [%]."
|
||||||
},
|
},
|
||||||
"bev_charge_power_max": {
|
"bev_max_charging_power": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
@ -4721,7 +4659,7 @@
|
|||||||
"type": "null"
|
"type": "null"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"title": "Bev Charge Power Max",
|
"title": "Bev Max Charging Power",
|
||||||
"description": "Battery Electric Vehicle maximum charge power [W]."
|
"description": "Battery Electric Vehicle maximum charge power [W]."
|
||||||
},
|
},
|
||||||
"dishwasher_provider": {
|
"dishwasher_provider": {
|
||||||
@ -5007,6 +4945,73 @@
|
|||||||
"title": "SimulationResult",
|
"title": "SimulationResult",
|
||||||
"description": "This object contains the results of the simulation and provides insights into various parameters over the entire forecast period."
|
"description": "This object contains the results of the simulation and provides insights into various parameters over the entire forecast period."
|
||||||
},
|
},
|
||||||
|
"SolarPanelBatteryParameters": {
|
||||||
|
"properties": {
|
||||||
|
"capacity_wh": {
|
||||||
|
"type": "integer",
|
||||||
|
"exclusiveMinimum": 0.0,
|
||||||
|
"title": "Capacity Wh",
|
||||||
|
"description": "An integer representing the capacity of the battery in watt-hours."
|
||||||
|
},
|
||||||
|
"charging_efficiency": {
|
||||||
|
"type": "number",
|
||||||
|
"maximum": 1.0,
|
||||||
|
"exclusiveMinimum": 0.0,
|
||||||
|
"title": "Charging Efficiency",
|
||||||
|
"description": "A float representing the charging efficiency of the battery.",
|
||||||
|
"default": 0.88
|
||||||
|
},
|
||||||
|
"discharging_efficiency": {
|
||||||
|
"type": "number",
|
||||||
|
"maximum": 1.0,
|
||||||
|
"exclusiveMinimum": 0.0,
|
||||||
|
"title": "Discharging Efficiency",
|
||||||
|
"description": "A float representing the discharge efficiency of the battery.",
|
||||||
|
"default": 0.88
|
||||||
|
},
|
||||||
|
"max_charge_power_w": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"exclusiveMinimum": 0.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Max Charge Power W",
|
||||||
|
"description": "Maximum charging power in watts.",
|
||||||
|
"default": 5000
|
||||||
|
},
|
||||||
|
"initial_soc_percentage": {
|
||||||
|
"type": "integer",
|
||||||
|
"maximum": 100.0,
|
||||||
|
"minimum": 0.0,
|
||||||
|
"title": "Initial Soc Percentage",
|
||||||
|
"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_percentage": {
|
||||||
|
"type": "integer",
|
||||||
|
"maximum": 100.0,
|
||||||
|
"minimum": 0.0,
|
||||||
|
"title": "Min Soc Percentage",
|
||||||
|
"description": "An integer representing the minimum state of charge (SOC) of the battery in percentage.",
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"max_soc_percentage": {
|
||||||
|
"type": "integer",
|
||||||
|
"maximum": 100.0,
|
||||||
|
"minimum": 0.0,
|
||||||
|
"title": "Max Soc Percentage",
|
||||||
|
"description": "An integer representing the maximum state of charge (SOC) of the battery in percentage.",
|
||||||
|
"default": 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"required": ["capacity_wh"],
|
||||||
|
"title": "SolarPanelBatteryParameters"
|
||||||
|
},
|
||||||
"ValidationError": {
|
"ValidationError": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"loc": {
|
"loc": {
|
||||||
|
@ -125,16 +125,16 @@ def prepare_optimization_real_parameters() -> OptimizationParameters:
|
|||||||
"strompreis_euro_pro_wh": strompreis_euro_pro_wh,
|
"strompreis_euro_pro_wh": strompreis_euro_pro_wh,
|
||||||
},
|
},
|
||||||
"pv_akku": {
|
"pv_akku": {
|
||||||
"kapazitaet_wh": 26400,
|
"capacity_wh": 26400,
|
||||||
"start_soc_prozent": 15,
|
"initial_soc_percentage": 15,
|
||||||
"min_soc_prozent": 15,
|
"min_soc_percentage": 15,
|
||||||
},
|
},
|
||||||
"eauto": {
|
"eauto": {
|
||||||
"min_soc_prozent": 50,
|
"min_soc_percentage": 50,
|
||||||
"kapazitaet_wh": 60000,
|
"capacity_wh": 60000,
|
||||||
"lade_effizienz": 0.95,
|
"charging_efficiency": 0.95,
|
||||||
"max_ladeleistung_w": 11040,
|
"max_charge_power_w": 11040,
|
||||||
"start_soc_prozent": 5,
|
"initial_soc_percentage": 5,
|
||||||
},
|
},
|
||||||
"temperature_forecast": temperature_forecast,
|
"temperature_forecast": temperature_forecast,
|
||||||
"start_solution": start_solution,
|
"start_solution": start_solution,
|
||||||
@ -276,16 +276,16 @@ def prepare_optimization_parameters() -> OptimizationParameters:
|
|||||||
"strompreis_euro_pro_wh": strompreis_euro_pro_wh,
|
"strompreis_euro_pro_wh": strompreis_euro_pro_wh,
|
||||||
},
|
},
|
||||||
"pv_akku": {
|
"pv_akku": {
|
||||||
"kapazitaet_wh": 26400,
|
"capacity_wh": 26400,
|
||||||
"start_soc_prozent": 15,
|
"initial_soc_percentage": 15,
|
||||||
"min_soc_prozent": 15,
|
"min_soc_percentage": 15,
|
||||||
},
|
},
|
||||||
"eauto": {
|
"eauto": {
|
||||||
"min_soc_prozent": 50,
|
"min_soc_percentage": 50,
|
||||||
"kapazitaet_wh": 60000,
|
"capacity_wh": 60000,
|
||||||
"lade_effizienz": 0.95,
|
"charging_efficiency": 0.95,
|
||||||
"max_ladeleistung_w": 11040,
|
"max_charge_power_w": 11040,
|
||||||
"start_soc_prozent": 5,
|
"initial_soc_percentage": 5,
|
||||||
},
|
},
|
||||||
"temperature_forecast": temperature_forecast,
|
"temperature_forecast": temperature_forecast,
|
||||||
"start_solution": start_solution,
|
"start_solution": start_solution,
|
||||||
|
@ -8,7 +8,7 @@ from typing_extensions import Self
|
|||||||
|
|
||||||
from akkudoktoreos.core.coreabc import ConfigMixin, PredictionMixin, SingletonMixin
|
from akkudoktoreos.core.coreabc import ConfigMixin, PredictionMixin, SingletonMixin
|
||||||
from akkudoktoreos.core.pydantic import PydanticBaseModel
|
from akkudoktoreos.core.pydantic import PydanticBaseModel
|
||||||
from akkudoktoreos.devices.battery import PVAkku
|
from akkudoktoreos.devices.battery import Battery
|
||||||
from akkudoktoreos.devices.generic import HomeAppliance
|
from akkudoktoreos.devices.generic import HomeAppliance
|
||||||
from akkudoktoreos.devices.inverter import Inverter
|
from akkudoktoreos.devices.inverter import Inverter
|
||||||
from akkudoktoreos.utils.datetimeutil import to_datetime
|
from akkudoktoreos.utils.datetimeutil import to_datetime
|
||||||
@ -152,8 +152,8 @@ class EnergieManagementSystem(SingletonMixin, ConfigMixin, PredictionMixin, Pyda
|
|||||||
# TODO: Move to devices
|
# TODO: Move to devices
|
||||||
# -------------------------
|
# -------------------------
|
||||||
|
|
||||||
akku: Optional[PVAkku] = Field(default=None, description="TBD.")
|
akku: Optional[Battery] = Field(default=None, description="TBD.")
|
||||||
eauto: Optional[PVAkku] = Field(default=None, description="TBD.")
|
eauto: Optional[Battery] = Field(default=None, description="TBD.")
|
||||||
home_appliance: Optional[HomeAppliance] = Field(default=None, description="TBD.")
|
home_appliance: Optional[HomeAppliance] = Field(default=None, description="TBD.")
|
||||||
inverter: Optional[Inverter] = Field(default=None, description="TBD.")
|
inverter: Optional[Inverter] = Field(default=None, description="TBD.")
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ class EnergieManagementSystem(SingletonMixin, ConfigMixin, PredictionMixin, Pyda
|
|||||||
def set_parameters(
|
def set_parameters(
|
||||||
self,
|
self,
|
||||||
parameters: EnergieManagementSystemParameters,
|
parameters: EnergieManagementSystemParameters,
|
||||||
eauto: Optional[PVAkku] = None,
|
eauto: Optional[Battery] = None,
|
||||||
home_appliance: Optional[HomeAppliance] = None,
|
home_appliance: Optional[HomeAppliance] = None,
|
||||||
inverter: Optional[Inverter] = None,
|
inverter: Optional[Inverter] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -323,9 +323,9 @@ class EnergieManagementSystem(SingletonMixin, ConfigMixin, PredictionMixin, Pyda
|
|||||||
|
|
||||||
# Set initial state
|
# Set initial state
|
||||||
if self.akku:
|
if self.akku:
|
||||||
akku_soc_pro_stunde[0] = self.akku.ladezustand_in_prozent()
|
akku_soc_pro_stunde[0] = self.akku.current_soc_percentage()
|
||||||
if self.eauto:
|
if self.eauto:
|
||||||
eauto_soc_pro_stunde[0] = self.eauto.ladezustand_in_prozent()
|
eauto_soc_pro_stunde[0] = self.eauto.current_soc_percentage()
|
||||||
|
|
||||||
for stunde in range(start_stunde, ende):
|
for stunde in range(start_stunde, ende):
|
||||||
stunde_since_now = stunde - start_stunde
|
stunde_since_now = stunde - start_stunde
|
||||||
@ -343,12 +343,12 @@ class EnergieManagementSystem(SingletonMixin, ConfigMixin, PredictionMixin, Pyda
|
|||||||
# E-Auto handling
|
# E-Auto handling
|
||||||
if self.eauto:
|
if self.eauto:
|
||||||
if self.ev_charge_hours[stunde] > 0:
|
if self.ev_charge_hours[stunde] > 0:
|
||||||
geladene_menge_eauto, verluste_eauto = self.eauto.energie_laden(
|
geladene_menge_eauto, verluste_eauto = self.eauto.charge_energy(
|
||||||
None, stunde, relative_power=self.ev_charge_hours[stunde]
|
None, stunde, relative_power=self.ev_charge_hours[stunde]
|
||||||
)
|
)
|
||||||
verbrauch += geladene_menge_eauto
|
verbrauch += geladene_menge_eauto
|
||||||
verluste_wh_pro_stunde[stunde_since_now] += verluste_eauto
|
verluste_wh_pro_stunde[stunde_since_now] += verluste_eauto
|
||||||
eauto_soc_pro_stunde[stunde_since_now] = self.eauto.ladezustand_in_prozent()
|
eauto_soc_pro_stunde[stunde_since_now] = self.eauto.current_soc_percentage()
|
||||||
|
|
||||||
# Process inverter logic
|
# Process inverter logic
|
||||||
netzeinspeisung, netzbezug, verluste, eigenverbrauch = (0.0, 0.0, 0.0, 0.0)
|
netzeinspeisung, netzbezug, verluste, eigenverbrauch = (0.0, 0.0, 0.0, 0.0)
|
||||||
@ -363,10 +363,10 @@ class EnergieManagementSystem(SingletonMixin, ConfigMixin, PredictionMixin, Pyda
|
|||||||
# AC PV Battery Charge
|
# AC PV Battery Charge
|
||||||
if self.akku and self.ac_charge_hours[stunde] > 0.0:
|
if self.akku and self.ac_charge_hours[stunde] > 0.0:
|
||||||
self.akku.set_charge_allowed_for_hour(1, stunde)
|
self.akku.set_charge_allowed_for_hour(1, stunde)
|
||||||
geladene_menge, verluste_wh = self.akku.energie_laden(
|
geladene_menge, verluste_wh = self.akku.charge_energy(
|
||||||
None, stunde, relative_power=self.ac_charge_hours[stunde]
|
None, stunde, relative_power=self.ac_charge_hours[stunde]
|
||||||
)
|
)
|
||||||
# print(stunde, " ", geladene_menge, " ",self.ac_charge_hours[stunde]," ",self.akku.ladezustand_in_prozent())
|
# print(stunde, " ", geladene_menge, " ",self.ac_charge_hours[stunde]," ",self.akku.current_soc_percentage())
|
||||||
verbrauch += geladene_menge
|
verbrauch += geladene_menge
|
||||||
verbrauch += verluste_wh
|
verbrauch += verluste_wh
|
||||||
netzbezug += geladene_menge
|
netzbezug += geladene_menge
|
||||||
@ -388,7 +388,7 @@ class EnergieManagementSystem(SingletonMixin, ConfigMixin, PredictionMixin, Pyda
|
|||||||
|
|
||||||
# Akku SOC tracking
|
# Akku SOC tracking
|
||||||
if self.akku:
|
if self.akku:
|
||||||
akku_soc_pro_stunde[stunde_since_now] = self.akku.ladezustand_in_prozent()
|
akku_soc_pro_stunde[stunde_since_now] = self.akku.current_soc_percentage()
|
||||||
else:
|
else:
|
||||||
akku_soc_pro_stunde[stunde_since_now] = 0.0
|
akku_soc_pro_stunde[stunde_since_now] = 0.0
|
||||||
|
|
||||||
|
@ -10,345 +10,279 @@ from akkudoktoreos.utils.utils import NumpyEncoder
|
|||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def max_ladeleistung_w_field(default: Optional[float] = None) -> Optional[float]:
|
def max_charging_power_field(description: Optional[str] = None) -> float:
|
||||||
|
if description is None:
|
||||||
|
description = "Maximum charging power in watts."
|
||||||
return Field(
|
return Field(
|
||||||
default=default,
|
default=5000,
|
||||||
gt=0,
|
gt=0,
|
||||||
description="An integer representing the charging power of the battery in watts.",
|
description=description,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def start_soc_prozent_field(description: str) -> int:
|
def initial_soc_percentage_field(description: str) -> int:
|
||||||
return Field(default=0, ge=0, le=100, description=description)
|
return Field(default=0, ge=0, le=100, description=description)
|
||||||
|
|
||||||
|
|
||||||
class BaseAkkuParameters(BaseModel):
|
class BaseBatteryParameters(BaseModel):
|
||||||
kapazitaet_wh: int = Field(
|
"""Base class for battery parameters with fields for capacity, efficiency, and state of charge."""
|
||||||
|
|
||||||
|
capacity_wh: int = Field(
|
||||||
gt=0, description="An integer representing the capacity of the battery in watt-hours."
|
gt=0, description="An integer representing the capacity of the battery in watt-hours."
|
||||||
)
|
)
|
||||||
lade_effizienz: float = Field(
|
charging_efficiency: float = Field(
|
||||||
default=0.88,
|
default=0.88,
|
||||||
gt=0,
|
gt=0,
|
||||||
le=1,
|
le=1,
|
||||||
description="A float representing the charging efficiency of the battery.",
|
description="A float representing the charging efficiency of the battery.",
|
||||||
)
|
)
|
||||||
entlade_effizienz: float = Field(default=0.88, gt=0, le=1)
|
discharging_efficiency: float = Field(
|
||||||
max_ladeleistung_w: Optional[float] = max_ladeleistung_w_field()
|
default=0.88,
|
||||||
start_soc_prozent: int = start_soc_prozent_field(
|
gt=0,
|
||||||
|
le=1,
|
||||||
|
description="A float representing the discharge efficiency of the battery.",
|
||||||
|
)
|
||||||
|
max_charge_power_w: Optional[float] = max_charging_power_field()
|
||||||
|
initial_soc_percentage: int = initial_soc_percentage_field(
|
||||||
"An integer representing the state of charge of the battery at the **start** of the current hour (not the current state)."
|
"An integer representing the state of charge of the battery at the **start** of the current hour (not the current state)."
|
||||||
)
|
)
|
||||||
min_soc_prozent: int = Field(
|
min_soc_percentage: int = Field(
|
||||||
default=0,
|
default=0,
|
||||||
ge=0,
|
ge=0,
|
||||||
le=100,
|
le=100,
|
||||||
description="An integer representing the minimum state of charge (SOC) of the battery in percentage.",
|
description="An integer representing the minimum state of charge (SOC) of the battery in percentage.",
|
||||||
)
|
)
|
||||||
max_soc_prozent: int = Field(default=100, ge=0, le=100)
|
max_soc_percentage: int = Field(
|
||||||
|
default=100,
|
||||||
|
ge=0,
|
||||||
|
le=100,
|
||||||
|
description="An integer representing the maximum state of charge (SOC) of the battery in percentage.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PVAkkuParameters(BaseAkkuParameters):
|
class SolarPanelBatteryParameters(BaseBatteryParameters):
|
||||||
max_ladeleistung_w: Optional[float] = max_ladeleistung_w_field(5000)
|
max_charge_power_w: Optional[float] = max_charging_power_field()
|
||||||
|
|
||||||
|
|
||||||
class EAutoParameters(BaseAkkuParameters):
|
class ElectricVehicleParameters(BaseBatteryParameters):
|
||||||
entlade_effizienz: float = 1.0
|
"""Parameters specific to an electric vehicle (EV)."""
|
||||||
start_soc_prozent: int = start_soc_prozent_field(
|
|
||||||
|
discharging_efficiency: float = 1.0
|
||||||
|
initial_soc_percentage: int = initial_soc_percentage_field(
|
||||||
"An integer representing the current state of charge (SOC) of the battery in percentage."
|
"An integer representing the current state of charge (SOC) of the battery in percentage."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class EAutoResult(BaseModel):
|
class ElectricVehicleResult(BaseModel):
|
||||||
"""This object contains information related to the electric vehicle and its charging and discharging behavior."""
|
"""Result class containing information related to the electric vehicle's charging and discharging behavior."""
|
||||||
|
|
||||||
charge_array: list[float] = Field(
|
charge_array: list[float] = Field(
|
||||||
description="Indicates for each hour whether the EV is charging (`0` for no charging, `1` for charging)."
|
description="Hourly charging status (0 for no charging, 1 for charging)."
|
||||||
)
|
)
|
||||||
discharge_array: list[int] = Field(
|
discharge_array: list[int] = Field(
|
||||||
description="Indicates for each hour whether the EV is discharging (`0` for no discharging, `1` for discharging)."
|
description="Hourly discharging status (0 for no discharging, 1 for discharging)."
|
||||||
)
|
)
|
||||||
entlade_effizienz: float = Field(description="The discharge efficiency as a float.")
|
discharging_efficiency: float = Field(description="The discharge efficiency as a float..")
|
||||||
hours: int = Field(description="Amount of hours the simulation is done for.")
|
hours: int = Field(description="Number of hours in the simulation.")
|
||||||
kapazitaet_wh: int = Field(description="The capacity of the EV’s battery in watt-hours.")
|
capacity_wh: int = Field(description="Capacity of the EV’s battery in watt-hours.")
|
||||||
lade_effizienz: float = Field(description="The charging efficiency as a float.")
|
charging_efficiency: float = Field(description="Charging efficiency as a float..")
|
||||||
max_ladeleistung_w: int = Field(description="The maximum charging power of the EV in watts.")
|
max_charge_power_w: int = Field(description="Maximum charging power in watts.")
|
||||||
soc_wh: float = Field(
|
soc_wh: float = Field(
|
||||||
description="The state of charge of the battery in watt-hours at the start of the simulation."
|
description="State of charge of the battery in watt-hours at the start of the simulation."
|
||||||
)
|
)
|
||||||
start_soc_prozent: int = Field(
|
initial_soc_percentage: int = Field(
|
||||||
description="The state of charge of the battery in percentage at the start of the simulation."
|
description="State of charge at the start of the simulation in percentage."
|
||||||
)
|
)
|
||||||
|
|
||||||
@field_validator(
|
@field_validator("discharge_array", "charge_array", mode="before")
|
||||||
"discharge_array",
|
|
||||||
"charge_array",
|
|
||||||
mode="before",
|
|
||||||
)
|
|
||||||
def convert_numpy(cls, field: Any) -> Any:
|
def convert_numpy(cls, field: Any) -> Any:
|
||||||
return NumpyEncoder.convert_numpy(field)[0]
|
return NumpyEncoder.convert_numpy(field)[0]
|
||||||
|
|
||||||
|
|
||||||
class PVAkku(DeviceBase):
|
class Battery(DeviceBase):
|
||||||
|
"""Represents a battery device with methods to simulate energy charging and discharging."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
parameters: Optional[BaseAkkuParameters] = None,
|
parameters: Optional[BaseBatteryParameters] = None,
|
||||||
hours: Optional[int] = 24,
|
hours: Optional[int] = 24,
|
||||||
provider_id: Optional[str] = None,
|
provider_id: Optional[str] = None,
|
||||||
):
|
):
|
||||||
# Configuration initialisation
|
# Initialize configuration and parameters
|
||||||
self.provider_id = provider_id
|
self.provider_id = provider_id
|
||||||
self.prefix = "<invalid>"
|
self.prefix = "<invalid>"
|
||||||
if self.provider_id == "GenericBattery":
|
if self.provider_id == "GenericBattery":
|
||||||
self.prefix = "battery"
|
self.prefix = "battery"
|
||||||
elif self.provider_id == "GenericBEV":
|
elif self.provider_id == "GenericBEV":
|
||||||
self.prefix = "bev"
|
self.prefix = "bev"
|
||||||
# Parameter initialisiation
|
|
||||||
self.parameters = parameters
|
self.parameters = parameters
|
||||||
if hours is None:
|
if hours is None:
|
||||||
self.hours = self.total_hours
|
self.hours = self.total_hours # TODO where does that come from?
|
||||||
else:
|
else:
|
||||||
self.hours = hours
|
self.hours = hours
|
||||||
|
|
||||||
self.initialised = False
|
self.initialised = False
|
||||||
|
|
||||||
# Run setup if parameters are given, otherwise setup() has to be called later when the config is initialised.
|
# Run setup if parameters are given, otherwise setup() has to be called later when the config is initialised.
|
||||||
if self.parameters is not None:
|
if self.parameters is not None:
|
||||||
self.setup()
|
self.setup()
|
||||||
|
|
||||||
def setup(self) -> None:
|
def setup(self) -> None:
|
||||||
|
"""Sets up the battery parameters based on configuration or provided parameters."""
|
||||||
if self.initialised:
|
if self.initialised:
|
||||||
return
|
return
|
||||||
if self.provider_id is not None:
|
|
||||||
# Setup by configuration
|
if self.provider_id:
|
||||||
# Battery capacity in Wh
|
# Setup from configuration
|
||||||
self.kapazitaet_wh = getattr(self.config, f"{self.prefix}_capacity")
|
self.capacity_wh = getattr(self.config, f"{self.prefix}_capacity")
|
||||||
# Initial state of charge in Wh
|
self.initial_soc_percentage = getattr(self.config, f"{self.prefix}_initial_soc")
|
||||||
self.start_soc_prozent = getattr(self.config, f"{self.prefix}_soc_start")
|
self.hours = self.total_hours # TODO where does that come from?
|
||||||
self.hours = self.total_hours
|
self.charging_efficiency = getattr(self.config, f"{self.prefix}_charging_efficiency")
|
||||||
# Charge and discharge efficiency
|
self.discharging_efficiency = getattr(
|
||||||
self.lade_effizienz = getattr(self.config, f"{self.prefix}_charge_efficiency")
|
self.config, f"{self.prefix}_discharging_efficiency"
|
||||||
self.entlade_effizienz = getattr(self.config, f"{self.prefix}_discharge_efficiency")
|
)
|
||||||
self.max_ladeleistung_w = getattr(self.config, f"{self.prefix}_charge_power_max")
|
self.max_charge_power_w = getattr(self.config, f"{self.prefix}_max_charging_power")
|
||||||
# Only assign for storage battery
|
|
||||||
if self.provider_id == "GenericBattery":
|
if self.provider_id == "GenericBattery":
|
||||||
self.min_soc_prozent = getattr(self.config, f"{self.prefix}_soc_mint")
|
self.min_soc_percentage = getattr(
|
||||||
|
self.config,
|
||||||
|
f"{self.prefix}_soc_min",
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.min_soc_prozent = 0
|
self.min_soc_percentage = 0
|
||||||
self.max_soc_prozent = getattr(self.config, f"{self.prefix}_soc_mint")
|
|
||||||
elif self.parameters is not None:
|
self.max_soc_percentage = getattr(
|
||||||
# Setup by parameters
|
self.config,
|
||||||
# Battery capacity in Wh
|
f"{self.prefix}_soc_max",
|
||||||
self.kapazitaet_wh = self.parameters.kapazitaet_wh
|
)
|
||||||
# Initial state of charge in Wh
|
elif self.parameters:
|
||||||
self.start_soc_prozent = self.parameters.start_soc_prozent
|
# Setup from parameters
|
||||||
# Charge and discharge efficiency
|
self.capacity_wh = self.parameters.capacity_wh
|
||||||
self.lade_effizienz = self.parameters.lade_effizienz
|
self.initial_soc_percentage = self.parameters.initial_soc_percentage
|
||||||
self.entlade_effizienz = self.parameters.entlade_effizienz
|
self.charging_efficiency = self.parameters.charging_efficiency
|
||||||
self.max_ladeleistung_w = self.parameters.max_ladeleistung_w
|
self.discharging_efficiency = self.parameters.discharging_efficiency
|
||||||
|
self.max_charge_power_w = self.parameters.max_charge_power_w
|
||||||
# Only assign for storage battery
|
# Only assign for storage battery
|
||||||
self.min_soc_prozent = (
|
self.min_soc_percentage = (
|
||||||
self.parameters.min_soc_prozent
|
self.parameters.min_soc_percentage
|
||||||
if isinstance(self.parameters, PVAkkuParameters)
|
if isinstance(self.parameters, SolarPanelBatteryParameters)
|
||||||
else 0
|
else 0
|
||||||
)
|
)
|
||||||
self.max_soc_prozent = self.parameters.max_soc_prozent
|
self.max_soc_percentage = self.parameters.max_soc_percentage
|
||||||
else:
|
else:
|
||||||
error_msg = "Parameters and provider ID missing. Can't instantiate."
|
error_msg = "Parameters and provider ID are missing. Cannot instantiate."
|
||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
raise ValueError(error_msg)
|
raise ValueError(error_msg)
|
||||||
|
|
||||||
# init
|
# Initialize state of charge
|
||||||
if self.max_ladeleistung_w is None:
|
if self.max_charge_power_w is None:
|
||||||
self.max_ladeleistung_w = self.kapazitaet_wh
|
self.max_charge_power_w = self.capacity_wh # TODO this should not be equal capacity_wh
|
||||||
self.discharge_array = np.full(self.hours, 1)
|
self.discharge_array = np.full(self.hours, 1)
|
||||||
self.charge_array = np.full(self.hours, 1)
|
self.charge_array = np.full(self.hours, 1)
|
||||||
# Calculate start, min and max SoC in Wh
|
self.soc_wh = (self.initial_soc_percentage / 100) * self.capacity_wh
|
||||||
self.soc_wh = (self.start_soc_prozent / 100) * self.kapazitaet_wh
|
self.min_soc_wh = (self.min_soc_percentage / 100) * self.capacity_wh
|
||||||
self.min_soc_wh = (self.min_soc_prozent / 100) * self.kapazitaet_wh
|
self.max_soc_wh = (self.max_soc_percentage / 100) * self.capacity_wh
|
||||||
self.max_soc_wh = (self.max_soc_prozent / 100) * self.kapazitaet_wh
|
|
||||||
|
|
||||||
self.initialised = True
|
self.initialised = True
|
||||||
|
|
||||||
def to_dict(self) -> dict[str, Any]:
|
def to_dict(self) -> dict[str, Any]:
|
||||||
|
"""Converts the object to a dictionary representation."""
|
||||||
return {
|
return {
|
||||||
"kapazitaet_wh": self.kapazitaet_wh,
|
"capacity_wh": self.capacity_wh,
|
||||||
"start_soc_prozent": self.start_soc_prozent,
|
"initial_soc_percentage": self.initial_soc_percentage,
|
||||||
"soc_wh": self.soc_wh,
|
"soc_wh": self.soc_wh,
|
||||||
"hours": self.hours,
|
"hours": self.hours,
|
||||||
"discharge_array": self.discharge_array,
|
"discharge_array": self.discharge_array,
|
||||||
"charge_array": self.charge_array,
|
"charge_array": self.charge_array,
|
||||||
"lade_effizienz": self.lade_effizienz,
|
"charging_efficiency": self.charging_efficiency,
|
||||||
"entlade_effizienz": self.entlade_effizienz,
|
"discharging_efficiency": self.discharging_efficiency,
|
||||||
"max_ladeleistung_w": self.max_ladeleistung_w,
|
"max_charge_power_w": self.max_charge_power_w,
|
||||||
}
|
}
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
self.soc_wh = (self.start_soc_prozent / 100) * self.kapazitaet_wh
|
"""Resets the battery state to its initial values."""
|
||||||
# Ensure soc_wh is within min and max limits
|
self.soc_wh = (self.initial_soc_percentage / 100) * self.capacity_wh
|
||||||
self.soc_wh = min(max(self.soc_wh, self.min_soc_wh), self.max_soc_wh)
|
self.soc_wh = min(max(self.soc_wh, self.min_soc_wh), self.max_soc_wh)
|
||||||
|
|
||||||
self.discharge_array = np.full(self.hours, 1)
|
self.discharge_array = np.full(self.hours, 1)
|
||||||
self.charge_array = np.full(self.hours, 1)
|
self.charge_array = np.full(self.hours, 1)
|
||||||
|
|
||||||
def set_discharge_per_hour(self, discharge_array: np.ndarray) -> None:
|
def set_discharge_per_hour(self, discharge_array: np.ndarray) -> None:
|
||||||
assert len(discharge_array) == self.hours
|
"""Sets the discharge values for each hour."""
|
||||||
|
if len(discharge_array) != self.hours:
|
||||||
|
raise ValueError(f"Discharge array must have exactly {self.hours} elements.")
|
||||||
self.discharge_array = np.array(discharge_array)
|
self.discharge_array = np.array(discharge_array)
|
||||||
|
|
||||||
def set_charge_per_hour(self, charge_array: np.ndarray) -> None:
|
def set_charge_per_hour(self, charge_array: np.ndarray) -> None:
|
||||||
assert len(charge_array) == self.hours
|
"""Sets the charge values for each hour."""
|
||||||
|
if len(charge_array) != self.hours:
|
||||||
|
raise ValueError(f"Charge array must have exactly {self.hours} elements.")
|
||||||
self.charge_array = np.array(charge_array)
|
self.charge_array = np.array(charge_array)
|
||||||
|
|
||||||
def set_charge_allowed_for_hour(self, charge: float, hour: int) -> None:
|
def set_charge_allowed_for_hour(self, charge: float, hour: int) -> None:
|
||||||
assert hour < self.hours
|
"""Sets the charge for a specific hour."""
|
||||||
|
if hour >= self.hours:
|
||||||
|
raise ValueError(f"Hour {hour} is out of range. Must be less than {self.hours}.")
|
||||||
self.charge_array[hour] = charge
|
self.charge_array[hour] = charge
|
||||||
|
|
||||||
def ladezustand_in_prozent(self) -> float:
|
def current_soc_percentage(self) -> float:
|
||||||
return (self.soc_wh / self.kapazitaet_wh) * 100
|
"""Calculates the current state of charge in percentage."""
|
||||||
|
return (self.soc_wh / self.capacity_wh) * 100
|
||||||
|
|
||||||
def energie_abgeben(self, wh: float, hour: int) -> tuple[float, float]:
|
def discharge_energy(self, wh: float, hour: int) -> tuple[float, float]:
|
||||||
|
"""Discharges energy from the battery."""
|
||||||
if self.discharge_array[hour] == 0:
|
if self.discharge_array[hour] == 0:
|
||||||
return 0.0, 0.0 # No energy discharge and no losses
|
return 0.0, 0.0
|
||||||
|
|
||||||
# Calculate the maximum energy that can be discharged considering min_soc and efficiency
|
max_possible_discharge_wh = (self.soc_wh - self.min_soc_wh) * self.discharging_efficiency
|
||||||
max_possible_discharge_wh = (self.soc_wh - self.min_soc_wh) * self.entlade_effizienz
|
max_possible_discharge_wh = max(max_possible_discharge_wh, 0.0)
|
||||||
max_possible_discharge_wh = max(max_possible_discharge_wh, 0.0) # Ensure non-negative
|
|
||||||
|
|
||||||
# Consider the maximum discharge power of the battery
|
max_possible_discharge_wh = min(
|
||||||
max_abgebbar_wh = min(max_possible_discharge_wh, self.max_ladeleistung_w)
|
max_possible_discharge_wh, self.max_charge_power_w
|
||||||
|
) # TODO make a new cfg variable max_discharge_power_w
|
||||||
|
|
||||||
# The actually discharged energy cannot exceed requested energy or maximum discharge
|
actual_discharge_wh = min(wh, max_possible_discharge_wh)
|
||||||
tatsaechlich_abgegeben_wh = min(wh, max_abgebbar_wh)
|
actual_withdrawal_wh = (
|
||||||
|
actual_discharge_wh / self.discharging_efficiency
|
||||||
|
if self.discharging_efficiency > 0
|
||||||
|
else 0.0
|
||||||
|
)
|
||||||
|
|
||||||
# Calculate the actual amount withdrawn from the battery (before efficiency loss)
|
self.soc_wh -= actual_withdrawal_wh
|
||||||
if self.entlade_effizienz > 0:
|
|
||||||
tatsaechliche_entnahme_wh = tatsaechlich_abgegeben_wh / self.entlade_effizienz
|
|
||||||
else:
|
|
||||||
tatsaechliche_entnahme_wh = 0.0
|
|
||||||
|
|
||||||
# Update the state of charge considering the actual withdrawal
|
|
||||||
self.soc_wh -= tatsaechliche_entnahme_wh
|
|
||||||
# Ensure soc_wh does not go below min_soc_wh
|
|
||||||
self.soc_wh = max(self.soc_wh, self.min_soc_wh)
|
self.soc_wh = max(self.soc_wh, self.min_soc_wh)
|
||||||
|
|
||||||
# Calculate losses due to efficiency
|
losses_wh = actual_withdrawal_wh - actual_discharge_wh
|
||||||
verluste_wh = tatsaechliche_entnahme_wh - tatsaechlich_abgegeben_wh
|
return actual_discharge_wh, losses_wh
|
||||||
|
|
||||||
# Return the actually discharged energy and the losses
|
def charge_energy(
|
||||||
return tatsaechlich_abgegeben_wh, verluste_wh
|
|
||||||
|
|
||||||
def energie_laden(
|
|
||||||
self, wh: Optional[float], hour: int, relative_power: float = 0.0
|
self, wh: Optional[float], hour: int, relative_power: float = 0.0
|
||||||
) -> tuple[float, float]:
|
) -> tuple[float, float]:
|
||||||
|
"""Charges energy into the battery."""
|
||||||
if hour is not None and self.charge_array[hour] == 0:
|
if hour is not None and self.charge_array[hour] == 0:
|
||||||
return 0.0, 0.0 # Charging not allowed in this hour
|
return 0.0, 0.0 # Charging not allowed in this hour
|
||||||
|
|
||||||
if relative_power > 0.0:
|
if relative_power > 0.0:
|
||||||
wh = self.max_ladeleistung_w * relative_power
|
wh = self.max_charge_power_w * relative_power
|
||||||
# If no value for wh is given, use the maximum charging power
|
|
||||||
wh = wh if wh is not None else self.max_ladeleistung_w
|
|
||||||
|
|
||||||
# Calculate the maximum energy that can be charged considering max_soc and efficiency
|
wh = wh if wh is not None else self.max_charge_power_w
|
||||||
if self.lade_effizienz > 0:
|
|
||||||
max_possible_charge_wh = (self.max_soc_wh - self.soc_wh) / self.lade_effizienz
|
|
||||||
else:
|
|
||||||
max_possible_charge_wh = 0.0
|
|
||||||
max_possible_charge_wh = max(max_possible_charge_wh, 0.0) # Ensure non-negative
|
|
||||||
|
|
||||||
# The actually charged energy cannot exceed requested energy, charging power, or maximum possible charge
|
max_possible_charge_wh = (
|
||||||
effektive_lademenge = min(wh, max_possible_charge_wh)
|
(self.max_soc_wh - self.soc_wh) / self.charging_efficiency
|
||||||
|
if self.charging_efficiency > 0
|
||||||
|
else 0.0
|
||||||
|
)
|
||||||
|
max_possible_charge_wh = max(max_possible_charge_wh, 0.0)
|
||||||
|
|
||||||
# Energy actually stored in the battery
|
effective_charge_wh = min(wh, max_possible_charge_wh)
|
||||||
geladene_menge = effektive_lademenge * self.lade_effizienz
|
charged_wh = effective_charge_wh * self.charging_efficiency
|
||||||
|
|
||||||
# Update soc_wh
|
self.soc_wh += charged_wh
|
||||||
self.soc_wh += geladene_menge
|
|
||||||
# Ensure soc_wh does not exceed max_soc_wh
|
|
||||||
self.soc_wh = min(self.soc_wh, self.max_soc_wh)
|
self.soc_wh = min(self.soc_wh, self.max_soc_wh)
|
||||||
|
|
||||||
# Calculate losses
|
losses_wh = effective_charge_wh - charged_wh
|
||||||
verluste_wh = effektive_lademenge - geladene_menge
|
return charged_wh, losses_wh
|
||||||
return geladene_menge, verluste_wh
|
|
||||||
|
|
||||||
def aktueller_energieinhalt(self) -> float:
|
def current_energy_content(self) -> float:
|
||||||
"""This method returns the current remaining energy considering efficiency.
|
"""Returns the current usable energy in the battery."""
|
||||||
|
usable_energy = (self.soc_wh - self.min_soc_wh) * self.discharging_efficiency
|
||||||
It accounts for both charging and discharging efficiency.
|
return max(usable_energy, 0.0)
|
||||||
"""
|
|
||||||
# Calculate remaining energy considering discharge efficiency
|
|
||||||
nutzbare_energie = (self.soc_wh - self.min_soc_wh) * self.entlade_effizienz
|
|
||||||
return max(nutzbare_energie, 0.0)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# Test battery discharge below min_soc
|
|
||||||
print("Test: Discharge below min_soc")
|
|
||||||
akku = PVAkku(
|
|
||||||
PVAkkuParameters(
|
|
||||||
kapazitaet_wh=10000,
|
|
||||||
start_soc_prozent=50,
|
|
||||||
min_soc_prozent=20,
|
|
||||||
max_soc_prozent=80,
|
|
||||||
),
|
|
||||||
hours=1,
|
|
||||||
)
|
|
||||||
akku.reset()
|
|
||||||
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
|
||||||
|
|
||||||
# Try to discharge 5000 Wh
|
|
||||||
abgegeben_wh, verlust_wh = akku.energie_abgeben(5000, 0)
|
|
||||||
print(f"Energy discharged: {abgegeben_wh} Wh, Losses: {verlust_wh} Wh")
|
|
||||||
print(f"SoC after discharge: {akku.ladezustand_in_prozent()}%")
|
|
||||||
print(f"Expected min SoC: {akku.min_soc_prozent}%")
|
|
||||||
|
|
||||||
# Test battery charge above max_soc
|
|
||||||
print("\nTest: Charge above max_soc")
|
|
||||||
akku = PVAkku(
|
|
||||||
PVAkkuParameters(
|
|
||||||
kapazitaet_wh=10000,
|
|
||||||
start_soc_prozent=50,
|
|
||||||
min_soc_prozent=20,
|
|
||||||
max_soc_prozent=80,
|
|
||||||
),
|
|
||||||
hours=1,
|
|
||||||
)
|
|
||||||
akku.reset()
|
|
||||||
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
|
||||||
|
|
||||||
# Try to charge 5000 Wh
|
|
||||||
geladen_wh, verlust_wh = akku.energie_laden(5000, 0)
|
|
||||||
print(f"Energy charged: {geladen_wh} Wh, Losses: {verlust_wh} Wh")
|
|
||||||
print(f"SoC after charge: {akku.ladezustand_in_prozent()}%")
|
|
||||||
print(f"Expected max SoC: {akku.max_soc_prozent}%")
|
|
||||||
|
|
||||||
# Test charging when battery is at max_soc
|
|
||||||
print("\nTest: Charging when at max_soc")
|
|
||||||
akku = PVAkku(
|
|
||||||
PVAkkuParameters(
|
|
||||||
kapazitaet_wh=10000,
|
|
||||||
start_soc_prozent=80,
|
|
||||||
min_soc_prozent=20,
|
|
||||||
max_soc_prozent=80,
|
|
||||||
),
|
|
||||||
hours=1,
|
|
||||||
)
|
|
||||||
akku.reset()
|
|
||||||
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
|
||||||
|
|
||||||
geladen_wh, verlust_wh = akku.energie_laden(5000, 0)
|
|
||||||
print(f"Energy charged: {geladen_wh} Wh, Losses: {verlust_wh} Wh")
|
|
||||||
print(f"SoC after charge: {akku.ladezustand_in_prozent()}%")
|
|
||||||
|
|
||||||
# Test discharging when battery is at min_soc
|
|
||||||
print("\nTest: Discharging when at min_soc")
|
|
||||||
akku = PVAkku(
|
|
||||||
PVAkkuParameters(
|
|
||||||
kapazitaet_wh=10000,
|
|
||||||
start_soc_prozent=20,
|
|
||||||
min_soc_prozent=20,
|
|
||||||
max_soc_prozent=80,
|
|
||||||
),
|
|
||||||
hours=1,
|
|
||||||
)
|
|
||||||
akku.reset()
|
|
||||||
print(f"Initial SoC: {akku.ladezustand_in_prozent()}%")
|
|
||||||
|
|
||||||
abgegeben_wh, verlust_wh = akku.energie_abgeben(5000, 0)
|
|
||||||
print(f"Energy discharged: {abgegeben_wh} Wh, Losses: {verlust_wh} Wh")
|
|
||||||
print(f"SoC after discharge: {akku.ladezustand_in_prozent()}%")
|
|
||||||
|
@ -6,7 +6,7 @@ from pydantic import Field, computed_field
|
|||||||
|
|
||||||
from akkudoktoreos.config.configabc import SettingsBaseModel
|
from akkudoktoreos.config.configabc import SettingsBaseModel
|
||||||
from akkudoktoreos.core.coreabc import SingletonMixin
|
from akkudoktoreos.core.coreabc import SingletonMixin
|
||||||
from akkudoktoreos.devices.battery import PVAkku
|
from akkudoktoreos.devices.battery import Battery
|
||||||
from akkudoktoreos.devices.devicesabc import DevicesBase
|
from akkudoktoreos.devices.devicesabc import DevicesBase
|
||||||
from akkudoktoreos.devices.generic import HomeAppliance
|
from akkudoktoreos.devices.generic import HomeAppliance
|
||||||
from akkudoktoreos.devices.inverter import Inverter
|
from akkudoktoreos.devices.inverter import Inverter
|
||||||
@ -25,7 +25,7 @@ class DevicesCommonSettings(SettingsBaseModel):
|
|||||||
default=None, description="Id of Battery simulation provider."
|
default=None, description="Id of Battery simulation provider."
|
||||||
)
|
)
|
||||||
battery_capacity: Optional[int] = Field(default=None, description="Battery capacity [Wh].")
|
battery_capacity: Optional[int] = Field(default=None, description="Battery capacity [Wh].")
|
||||||
battery_soc_start: Optional[int] = Field(
|
battery_initial_soc: Optional[int] = Field(
|
||||||
default=None, description="Battery initial state of charge [%]."
|
default=None, description="Battery initial state of charge [%]."
|
||||||
)
|
)
|
||||||
battery_soc_min: Optional[int] = Field(
|
battery_soc_min: Optional[int] = Field(
|
||||||
@ -34,13 +34,13 @@ class DevicesCommonSettings(SettingsBaseModel):
|
|||||||
battery_soc_max: Optional[int] = Field(
|
battery_soc_max: Optional[int] = Field(
|
||||||
default=None, description="Battery maximum state of charge [%]."
|
default=None, description="Battery maximum state of charge [%]."
|
||||||
)
|
)
|
||||||
battery_charge_efficiency: Optional[float] = Field(
|
battery_charging_efficiency: Optional[float] = Field(
|
||||||
default=None, description="Battery charging efficiency [%]."
|
default=None, description="Battery charging efficiency [%]."
|
||||||
)
|
)
|
||||||
battery_discharge_efficiency: Optional[float] = Field(
|
battery_discharging_efficiency: Optional[float] = Field(
|
||||||
default=None, description="Battery discharging efficiency [%]."
|
default=None, description="Battery discharging efficiency [%]."
|
||||||
)
|
)
|
||||||
battery_charge_power_max: Optional[int] = Field(
|
battery_max_charging_power: Optional[int] = Field(
|
||||||
default=None, description="Battery maximum charge power [W]."
|
default=None, description="Battery maximum charge power [W]."
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -52,19 +52,19 @@ class DevicesCommonSettings(SettingsBaseModel):
|
|||||||
bev_capacity: Optional[int] = Field(
|
bev_capacity: Optional[int] = Field(
|
||||||
default=None, description="Battery Electric Vehicle capacity [Wh]."
|
default=None, description="Battery Electric Vehicle capacity [Wh]."
|
||||||
)
|
)
|
||||||
bev_soc_start: Optional[int] = Field(
|
bev_initial_soc: Optional[int] = Field(
|
||||||
default=None, description="Battery Electric Vehicle initial state of charge [%]."
|
default=None, description="Battery Electric Vehicle initial state of charge [%]."
|
||||||
)
|
)
|
||||||
bev_soc_max: Optional[int] = Field(
|
bev_soc_max: Optional[int] = Field(
|
||||||
default=None, description="Battery Electric Vehicle maximum state of charge [%]."
|
default=None, description="Battery Electric Vehicle maximum state of charge [%]."
|
||||||
)
|
)
|
||||||
bev_charge_efficiency: Optional[float] = Field(
|
bev_charging_efficiency: Optional[float] = Field(
|
||||||
default=None, description="Battery Electric Vehicle charging efficiency [%]."
|
default=None, description="Battery Electric Vehicle charging efficiency [%]."
|
||||||
)
|
)
|
||||||
bev_discharge_efficiency: Optional[float] = Field(
|
bev_discharging_efficiency: Optional[float] = Field(
|
||||||
default=None, description="Battery Electric Vehicle discharging efficiency [%]."
|
default=None, description="Battery Electric Vehicle discharging efficiency [%]."
|
||||||
)
|
)
|
||||||
bev_charge_power_max: Optional[int] = Field(
|
bev_max_charging_power: Optional[int] = Field(
|
||||||
default=None, description="Battery Electric Vehicle maximum charge power [W]."
|
default=None, description="Battery Electric Vehicle maximum charge power [W]."
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -159,8 +159,8 @@ class Devices(SingletonMixin, DevicesBase):
|
|||||||
# Devices
|
# Devices
|
||||||
# TODO: Make devices class a container of device simulation providers.
|
# TODO: Make devices class a container of device simulation providers.
|
||||||
# Device simulations to be used are then enabled in the configuration.
|
# Device simulations to be used are then enabled in the configuration.
|
||||||
akku: ClassVar[PVAkku] = PVAkku(provider_id="GenericBattery")
|
akku: ClassVar[Battery] = Battery(provider_id="GenericBattery")
|
||||||
eauto: ClassVar[PVAkku] = PVAkku(provider_id="GenericBEV")
|
eauto: ClassVar[Battery] = Battery(provider_id="GenericBEV")
|
||||||
home_appliance: ClassVar[HomeAppliance] = HomeAppliance(provider_id="GenericDishWasher")
|
home_appliance: ClassVar[HomeAppliance] = HomeAppliance(provider_id="GenericDishWasher")
|
||||||
inverter: ClassVar[Inverter] = Inverter(akku=akku, provider_id="GenericInverter")
|
inverter: ClassVar[Inverter] = Inverter(akku=akku, provider_id="GenericInverter")
|
||||||
|
|
||||||
@ -186,9 +186,9 @@ class Devices(SingletonMixin, DevicesBase):
|
|||||||
# Set initial state
|
# Set initial state
|
||||||
simulation_step = to_duration("1 hour")
|
simulation_step = to_duration("1 hour")
|
||||||
if self.akku:
|
if self.akku:
|
||||||
self.akku_soc_pro_stunde[0] = self.akku.ladezustand_in_prozent()
|
self.akku_soc_pro_stunde[0] = self.akku.current_soc_percentage()
|
||||||
if self.eauto:
|
if self.eauto:
|
||||||
self.eauto_soc_pro_stunde[0] = self.eauto.ladezustand_in_prozent()
|
self.eauto_soc_pro_stunde[0] = self.eauto.current_soc_percentage()
|
||||||
|
|
||||||
# Get predictions for full device simulation time range
|
# Get predictions for full device simulation time range
|
||||||
# gesamtlast[stunde]
|
# gesamtlast[stunde]
|
||||||
@ -232,12 +232,12 @@ class Devices(SingletonMixin, DevicesBase):
|
|||||||
# E-Auto handling
|
# E-Auto handling
|
||||||
if self.eauto:
|
if self.eauto:
|
||||||
if self.ev_charge_hours[hour] > 0:
|
if self.ev_charge_hours[hour] > 0:
|
||||||
geladene_menge_eauto, verluste_eauto = self.eauto.energie_laden(
|
geladene_menge_eauto, verluste_eauto = self.eauto.charge_energy(
|
||||||
None, hour, relative_power=self.ev_charge_hours[hour]
|
None, hour, relative_power=self.ev_charge_hours[hour]
|
||||||
)
|
)
|
||||||
consumption += geladene_menge_eauto
|
consumption += geladene_menge_eauto
|
||||||
self.verluste_wh_pro_stunde[stunde_since_now] += verluste_eauto
|
self.verluste_wh_pro_stunde[stunde_since_now] += verluste_eauto
|
||||||
self.eauto_soc_pro_stunde[stunde_since_now] = self.eauto.ladezustand_in_prozent()
|
self.eauto_soc_pro_stunde[stunde_since_now] = self.eauto.current_soc_percentage()
|
||||||
|
|
||||||
# Process inverter logic
|
# Process inverter logic
|
||||||
grid_export, grid_import, losses, self_consumption = (0.0, 0.0, 0.0, 0.0)
|
grid_export, grid_import, losses, self_consumption = (0.0, 0.0, 0.0, 0.0)
|
||||||
@ -252,10 +252,10 @@ class Devices(SingletonMixin, DevicesBase):
|
|||||||
# AC PV Battery Charge
|
# AC PV Battery Charge
|
||||||
if self.akku and self.ac_charge_hours[hour] > 0.0:
|
if self.akku and self.ac_charge_hours[hour] > 0.0:
|
||||||
self.akku.set_charge_allowed_for_hour(1, hour)
|
self.akku.set_charge_allowed_for_hour(1, hour)
|
||||||
geladene_menge, verluste_wh = self.akku.energie_laden(
|
geladene_menge, verluste_wh = self.akku.charge_energy(
|
||||||
None, hour, relative_power=self.ac_charge_hours[hour]
|
None, hour, relative_power=self.ac_charge_hours[hour]
|
||||||
)
|
)
|
||||||
# print(stunde, " ", geladene_menge, " ",self.ac_charge_hours[stunde]," ",self.akku.ladezustand_in_prozent())
|
# print(stunde, " ", geladene_menge, " ",self.ac_charge_hours[stunde]," ",self.akku.current_soc_percentage())
|
||||||
consumption += geladene_menge
|
consumption += geladene_menge
|
||||||
grid_import += geladene_menge
|
grid_import += geladene_menge
|
||||||
self.verluste_wh_pro_stunde[stunde_since_now] += verluste_wh
|
self.verluste_wh_pro_stunde[stunde_since_now] += verluste_wh
|
||||||
@ -275,7 +275,7 @@ class Devices(SingletonMixin, DevicesBase):
|
|||||||
|
|
||||||
# Akku SOC tracking
|
# Akku SOC tracking
|
||||||
if self.akku:
|
if self.akku:
|
||||||
self.akku_soc_pro_stunde[stunde_since_now] = self.akku.ladezustand_in_prozent()
|
self.akku_soc_pro_stunde[stunde_since_now] = self.akku.current_soc_percentage()
|
||||||
else:
|
else:
|
||||||
self.akku_soc_pro_stunde[stunde_since_now] = 0.0
|
self.akku_soc_pro_stunde[stunde_since_now] = 0.0
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ from typing import Optional, Tuple
|
|||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from akkudoktoreos.devices.battery import PVAkku
|
from akkudoktoreos.devices.battery import Battery
|
||||||
from akkudoktoreos.devices.devicesabc import DeviceBase
|
from akkudoktoreos.devices.devicesabc import DeviceBase
|
||||||
from akkudoktoreos.utils.logutil import get_logger
|
from akkudoktoreos.utils.logutil import get_logger
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ class Inverter(DeviceBase):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
parameters: Optional[InverterParameters] = None,
|
parameters: Optional[InverterParameters] = None,
|
||||||
akku: Optional[PVAkku] = None,
|
akku: Optional[Battery] = None,
|
||||||
provider_id: Optional[str] = None,
|
provider_id: Optional[str] = None,
|
||||||
):
|
):
|
||||||
# Configuration initialisation
|
# Configuration initialisation
|
||||||
@ -70,7 +70,7 @@ class Inverter(DeviceBase):
|
|||||||
remaining_energy = generation - actual_consumption
|
remaining_energy = generation - actual_consumption
|
||||||
|
|
||||||
# Charge battery with excess energy
|
# Charge battery with excess energy
|
||||||
charged_energy, charging_losses = self.akku.energie_laden(remaining_energy, hour)
|
charged_energy, charging_losses = self.akku.charge_energy(remaining_energy, hour)
|
||||||
losses += charging_losses
|
losses += charging_losses
|
||||||
|
|
||||||
# Calculate remaining surplus after battery charge
|
# Calculate remaining surplus after battery charge
|
||||||
@ -87,7 +87,7 @@ class Inverter(DeviceBase):
|
|||||||
available_ac_power = max(self.max_power_wh - generation, 0)
|
available_ac_power = max(self.max_power_wh - generation, 0)
|
||||||
|
|
||||||
# Discharge battery to cover shortfall, if possible
|
# Discharge battery to cover shortfall, if possible
|
||||||
battery_discharge, discharge_losses = self.akku.energie_abgeben(
|
battery_discharge, discharge_losses = self.akku.discharge_energy(
|
||||||
min(shortfall, available_ac_power), hour
|
min(shortfall, available_ac_power), hour
|
||||||
)
|
)
|
||||||
losses += discharge_losses
|
losses += discharge_losses
|
||||||
|
@ -13,10 +13,10 @@ from akkudoktoreos.core.coreabc import (
|
|||||||
)
|
)
|
||||||
from akkudoktoreos.core.ems import EnergieManagementSystemParameters, SimulationResult
|
from akkudoktoreos.core.ems import EnergieManagementSystemParameters, SimulationResult
|
||||||
from akkudoktoreos.devices.battery import (
|
from akkudoktoreos.devices.battery import (
|
||||||
EAutoParameters,
|
Battery,
|
||||||
EAutoResult,
|
ElectricVehicleParameters,
|
||||||
PVAkku,
|
ElectricVehicleResult,
|
||||||
PVAkkuParameters,
|
SolarPanelBatteryParameters,
|
||||||
)
|
)
|
||||||
from akkudoktoreos.devices.generic import HomeAppliance, HomeApplianceParameters
|
from akkudoktoreos.devices.generic import HomeAppliance, HomeApplianceParameters
|
||||||
from akkudoktoreos.devices.inverter import Inverter, InverterParameters
|
from akkudoktoreos.devices.inverter import Inverter, InverterParameters
|
||||||
@ -26,9 +26,9 @@ from akkudoktoreos.visualize import visualisiere_ergebnisse
|
|||||||
|
|
||||||
class OptimizationParameters(BaseModel):
|
class OptimizationParameters(BaseModel):
|
||||||
ems: EnergieManagementSystemParameters
|
ems: EnergieManagementSystemParameters
|
||||||
pv_akku: PVAkkuParameters
|
pv_akku: SolarPanelBatteryParameters
|
||||||
inverter: InverterParameters = InverterParameters()
|
inverter: InverterParameters = InverterParameters()
|
||||||
eauto: Optional[EAutoParameters]
|
eauto: Optional[ElectricVehicleParameters]
|
||||||
dishwasher: Optional[HomeApplianceParameters] = None
|
dishwasher: Optional[HomeApplianceParameters] = None
|
||||||
temperature_forecast: Optional[list[float]] = Field(
|
temperature_forecast: Optional[list[float]] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
@ -68,7 +68,7 @@ class OptimizeResponse(BaseModel):
|
|||||||
)
|
)
|
||||||
eautocharge_hours_float: Optional[list[float]] = Field(description="TBD")
|
eautocharge_hours_float: Optional[list[float]] = Field(description="TBD")
|
||||||
result: SimulationResult
|
result: SimulationResult
|
||||||
eauto_obj: Optional[EAutoResult]
|
eauto_obj: Optional[ElectricVehicleResult]
|
||||||
start_solution: Optional[list[float]] = Field(
|
start_solution: Optional[list[float]] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
description="An array of binary values (0 or 1) representing a possible starting solution for the simulation.",
|
description="An array of binary values (0 or 1) representing a possible starting solution for the simulation.",
|
||||||
@ -92,8 +92,8 @@ class OptimizeResponse(BaseModel):
|
|||||||
mode="before",
|
mode="before",
|
||||||
)
|
)
|
||||||
def convert_eauto(cls, field: Any) -> Any:
|
def convert_eauto(cls, field: Any) -> Any:
|
||||||
if isinstance(field, PVAkku):
|
if isinstance(field, Battery):
|
||||||
return EAutoResult(**field.to_dict())
|
return ElectricVehicleResult(**field.to_dict())
|
||||||
return field
|
return field
|
||||||
|
|
||||||
|
|
||||||
@ -367,7 +367,7 @@ class optimization_problem(ConfigMixin, DevicesMixin, EnergyManagementSystemMixi
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Penalty for not meeting the minimum SOC (State of Charge) requirement
|
# Penalty for not meeting the minimum SOC (State of Charge) requirement
|
||||||
# if parameters.eauto_min_soc_prozent - ems.eauto.ladezustand_in_prozent() <= 0.0 and self.optimize_ev:
|
# if parameters.eauto_min_soc_prozent - ems.eauto.current_soc_percentage() <= 0.0 and self.optimize_ev:
|
||||||
# gesamtbilanz += sum(
|
# gesamtbilanz += sum(
|
||||||
# self.config.optimization_penalty for ladeleistung in eautocharge_hours_float if ladeleistung != 0.0
|
# self.config.optimization_penalty for ladeleistung in eautocharge_hours_float if ladeleistung != 0.0
|
||||||
# )
|
# )
|
||||||
@ -375,7 +375,7 @@ class optimization_problem(ConfigMixin, DevicesMixin, EnergyManagementSystemMixi
|
|||||||
individual.extra_data = ( # type: ignore[attr-defined]
|
individual.extra_data = ( # type: ignore[attr-defined]
|
||||||
o["Gesamtbilanz_Euro"],
|
o["Gesamtbilanz_Euro"],
|
||||||
o["Gesamt_Verluste"],
|
o["Gesamt_Verluste"],
|
||||||
parameters.eauto.min_soc_prozent - self.ems.eauto.ladezustand_in_prozent()
|
parameters.eauto.min_soc_percentage - self.ems.eauto.current_soc_percentage()
|
||||||
if parameters.eauto and self.ems.eauto
|
if parameters.eauto and self.ems.eauto
|
||||||
else 0,
|
else 0,
|
||||||
)
|
)
|
||||||
@ -383,16 +383,16 @@ class optimization_problem(ConfigMixin, DevicesMixin, EnergyManagementSystemMixi
|
|||||||
# Adjust total balance with battery value and penalties for unmet SOC
|
# Adjust total balance with battery value and penalties for unmet SOC
|
||||||
|
|
||||||
restwert_akku = (
|
restwert_akku = (
|
||||||
self.ems.akku.aktueller_energieinhalt() * parameters.ems.preis_euro_pro_wh_akku
|
self.ems.akku.current_energy_content() * parameters.ems.preis_euro_pro_wh_akku
|
||||||
)
|
)
|
||||||
# print(ems.akku.aktueller_energieinhalt()," * ", parameters.ems.preis_euro_pro_wh_akku , " ", restwert_akku, " ", gesamtbilanz)
|
# print(ems.akku.current_energy_content()," * ", parameters.ems.preis_euro_pro_wh_akku , " ", restwert_akku, " ", gesamtbilanz)
|
||||||
gesamtbilanz += -restwert_akku
|
gesamtbilanz += -restwert_akku
|
||||||
# print(gesamtbilanz)
|
# print(gesamtbilanz)
|
||||||
if self.optimize_ev:
|
if self.optimize_ev:
|
||||||
gesamtbilanz += max(
|
gesamtbilanz += max(
|
||||||
0,
|
0,
|
||||||
(
|
(
|
||||||
parameters.eauto.min_soc_prozent - self.ems.eauto.ladezustand_in_prozent()
|
parameters.eauto.min_soc_percentage - self.ems.eauto.current_soc_percentage()
|
||||||
if parameters.eauto and self.ems.eauto
|
if parameters.eauto and self.ems.eauto
|
||||||
else 0
|
else 0
|
||||||
)
|
)
|
||||||
@ -458,21 +458,21 @@ class optimization_problem(ConfigMixin, DevicesMixin, EnergyManagementSystemMixi
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Initialize PV and EV batteries
|
# Initialize PV and EV batteries
|
||||||
akku = PVAkku(
|
akku = Battery(
|
||||||
parameters.pv_akku,
|
parameters.pv_akku,
|
||||||
hours=self.config.prediction_hours,
|
hours=self.config.prediction_hours,
|
||||||
)
|
)
|
||||||
akku.set_charge_per_hour(np.full(self.config.prediction_hours, 1))
|
akku.set_charge_per_hour(np.full(self.config.prediction_hours, 1))
|
||||||
|
|
||||||
eauto: Optional[PVAkku] = None
|
eauto: Optional[Battery] = None
|
||||||
if parameters.eauto:
|
if parameters.eauto:
|
||||||
eauto = PVAkku(
|
eauto = Battery(
|
||||||
parameters.eauto,
|
parameters.eauto,
|
||||||
hours=self.config.prediction_hours,
|
hours=self.config.prediction_hours,
|
||||||
)
|
)
|
||||||
eauto.set_charge_per_hour(np.full(self.config.prediction_hours, 1))
|
eauto.set_charge_per_hour(np.full(self.config.prediction_hours, 1))
|
||||||
self.optimize_ev = (
|
self.optimize_ev = (
|
||||||
parameters.eauto.min_soc_prozent - parameters.eauto.start_soc_prozent >= 0
|
parameters.eauto.min_soc_percentage - parameters.eauto.initial_soc_percentage >= 0
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.optimize_ev = False
|
self.optimize_ev = False
|
||||||
|
219
tests/test_battery.py
Normal file
219
tests/test_battery.py
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from akkudoktoreos.devices.battery import Battery, SolarPanelBatteryParameters
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def setup_pv_battery():
|
||||||
|
params = SolarPanelBatteryParameters(
|
||||||
|
capacity_wh=10000,
|
||||||
|
initial_soc_percentage=50,
|
||||||
|
min_soc_percentage=20,
|
||||||
|
max_soc_percentage=80,
|
||||||
|
max_charge_power_w=8000,
|
||||||
|
)
|
||||||
|
battery = Battery(params, hours=24)
|
||||||
|
battery.reset()
|
||||||
|
return battery
|
||||||
|
|
||||||
|
|
||||||
|
def test_initial_state_of_charge(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
assert battery.current_soc_percentage() == 50.0, "Initial SoC should be 50%"
|
||||||
|
|
||||||
|
|
||||||
|
def test_battery_discharge_below_min_soc(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
discharged_wh, loss_wh = battery.discharge_energy(5000, 0)
|
||||||
|
|
||||||
|
# Ensure it discharges energy and stops at the min SOC
|
||||||
|
assert discharged_wh > 0
|
||||||
|
print(discharged_wh, loss_wh, battery.current_soc_percentage(), battery.min_soc_percentage)
|
||||||
|
assert battery.current_soc_percentage() >= 20 # Ensure it's above min_soc_percentage
|
||||||
|
assert loss_wh >= 0 # Losses should not be negative
|
||||||
|
assert discharged_wh == 2640.0, "The energy discharged should be limited by min_soc"
|
||||||
|
|
||||||
|
|
||||||
|
def test_battery_charge_above_max_soc(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
charged_wh, loss_wh = battery.charge_energy(5000, 0)
|
||||||
|
|
||||||
|
# Ensure it charges energy and stops at the max SOC
|
||||||
|
assert charged_wh > 0
|
||||||
|
assert battery.current_soc_percentage() <= 80 # Ensure it's below max_soc_percentage
|
||||||
|
assert loss_wh >= 0 # Losses should not be negative
|
||||||
|
assert charged_wh == 3000.0, "The energy charged should be limited by max_soc"
|
||||||
|
|
||||||
|
|
||||||
|
def test_battery_charge_when_full(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
battery.soc_wh = battery.max_soc_wh # Set battery to full
|
||||||
|
charged_wh, loss_wh = battery.charge_energy(5000, 0)
|
||||||
|
|
||||||
|
# No charging should happen if battery is full
|
||||||
|
assert charged_wh == 0
|
||||||
|
assert loss_wh == 0
|
||||||
|
assert battery.current_soc_percentage() == 80, "SoC should remain at max_soc"
|
||||||
|
|
||||||
|
|
||||||
|
def test_battery_discharge_when_empty(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
battery.soc_wh = battery.min_soc_wh # Set battery to minimum SOC
|
||||||
|
discharged_wh, loss_wh = battery.discharge_energy(5000, 0)
|
||||||
|
|
||||||
|
# No discharge should happen if battery is at min SOC
|
||||||
|
assert discharged_wh == 0
|
||||||
|
assert loss_wh == 0
|
||||||
|
assert battery.current_soc_percentage() == 20, "SoC should remain at min_soc"
|
||||||
|
|
||||||
|
|
||||||
|
def test_battery_discharge_exactly_min_soc(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
battery.soc_wh = battery.min_soc_wh # Set battery to exactly min SOC
|
||||||
|
discharged_wh, loss_wh = battery.discharge_energy(1000, 0)
|
||||||
|
|
||||||
|
# Battery should not go below the min SOC
|
||||||
|
assert discharged_wh == 0
|
||||||
|
assert battery.current_soc_percentage() == 20 # SOC should remain at min_SOC
|
||||||
|
|
||||||
|
|
||||||
|
def test_battery_charge_exactly_max_soc(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
battery.soc_wh = battery.max_soc_wh # Set battery to exactly max SOC
|
||||||
|
charged_wh, loss_wh = battery.charge_energy(1000, 0)
|
||||||
|
|
||||||
|
# Battery should not exceed the max SOC
|
||||||
|
assert charged_wh == 0
|
||||||
|
assert battery.current_soc_percentage() == 80 # SOC should remain at max_SOC
|
||||||
|
|
||||||
|
|
||||||
|
def test_battery_reset_function(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
battery.soc_wh = 8000 # Change the SOC to some value
|
||||||
|
battery.reset()
|
||||||
|
|
||||||
|
# After reset, SOC should be equal to the initial value
|
||||||
|
assert battery.current_soc_percentage() == battery.initial_soc_percentage
|
||||||
|
|
||||||
|
|
||||||
|
def test_soc_limits(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
|
||||||
|
# Manually set SoC above max limit
|
||||||
|
battery.soc_wh = battery.max_soc_wh + 1000
|
||||||
|
battery.soc_wh = min(battery.soc_wh, battery.max_soc_wh)
|
||||||
|
assert battery.current_soc_percentage() <= 80, "SoC should not exceed max_soc"
|
||||||
|
|
||||||
|
# Manually set SoC below min limit
|
||||||
|
battery.soc_wh = battery.min_soc_wh - 1000
|
||||||
|
battery.soc_wh = max(battery.soc_wh, battery.min_soc_wh)
|
||||||
|
assert battery.current_soc_percentage() >= 20, "SoC should not drop below min_soc"
|
||||||
|
|
||||||
|
|
||||||
|
def test_max_charge_power_w(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
battery.setup()
|
||||||
|
assert (
|
||||||
|
battery.parameters.max_charge_power_w == 8000
|
||||||
|
), "Default max charge power should be 5000W, We ask for 8000W here"
|
||||||
|
|
||||||
|
|
||||||
|
def test_charge_energy_within_limits(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
battery.setup()
|
||||||
|
initial_soc_wh = battery.soc_wh
|
||||||
|
|
||||||
|
charged_wh, losses_wh = battery.charge_energy(wh=4000, hour=1)
|
||||||
|
|
||||||
|
assert charged_wh > 0, "Charging should add energy"
|
||||||
|
assert losses_wh >= 0, "Losses should not be negative"
|
||||||
|
assert battery.soc_wh > initial_soc_wh, "State of charge should increase after charging"
|
||||||
|
assert battery.soc_wh <= battery.max_soc_wh, "SOC should not exceed max SOC"
|
||||||
|
|
||||||
|
|
||||||
|
def test_charge_energy_exceeds_capacity(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
battery.setup()
|
||||||
|
initial_soc_wh = battery.soc_wh
|
||||||
|
|
||||||
|
# Try to overcharge beyond max capacity
|
||||||
|
charged_wh, losses_wh = battery.charge_energy(wh=20000, hour=2)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
charged_wh + initial_soc_wh <= battery.max_soc_wh
|
||||||
|
), "Charging should not exceed max capacity"
|
||||||
|
assert losses_wh >= 0, "Losses should not be negative"
|
||||||
|
assert battery.soc_wh == battery.max_soc_wh, "SOC should be at max after overcharge attempt"
|
||||||
|
|
||||||
|
|
||||||
|
def test_charge_energy_not_allowed_hour(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
battery.setup()
|
||||||
|
|
||||||
|
# Disable charging for all hours
|
||||||
|
battery.set_charge_per_hour(np.zeros(battery.hours))
|
||||||
|
|
||||||
|
charged_wh, losses_wh = battery.charge_energy(wh=4000, hour=3)
|
||||||
|
|
||||||
|
assert charged_wh == 0, "No energy should be charged in disallowed hours"
|
||||||
|
assert losses_wh == 0, "No losses should occur if charging is not allowed"
|
||||||
|
assert (
|
||||||
|
battery.soc_wh == (battery.parameters.initial_soc_percentage / 100) * battery.capacity_wh
|
||||||
|
), "SOC should remain unchanged"
|
||||||
|
|
||||||
|
|
||||||
|
def test_charge_energy_relative_power(setup_pv_battery):
|
||||||
|
battery = setup_pv_battery
|
||||||
|
battery.setup()
|
||||||
|
|
||||||
|
relative_power = 0.5 # 50% of max charge power
|
||||||
|
charged_wh, losses_wh = battery.charge_energy(wh=None, hour=4, relative_power=relative_power)
|
||||||
|
|
||||||
|
assert charged_wh > 0, "Charging should occur with relative power"
|
||||||
|
assert losses_wh >= 0, "Losses should not be negative"
|
||||||
|
assert (
|
||||||
|
charged_wh <= battery.max_charge_power_w * relative_power
|
||||||
|
), "Charging should respect relative power limit"
|
||||||
|
assert battery.soc_wh > 0, "SOC should increase after charging"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def setup_car_battery():
|
||||||
|
from akkudoktoreos.devices.battery import ElectricVehicleParameters
|
||||||
|
|
||||||
|
params = ElectricVehicleParameters(
|
||||||
|
capacity_wh=40000,
|
||||||
|
initial_soc_percentage=60,
|
||||||
|
min_soc_percentage=10,
|
||||||
|
max_soc_percentage=90,
|
||||||
|
max_charge_power_w=7000,
|
||||||
|
)
|
||||||
|
battery = Battery(params, hours=24)
|
||||||
|
battery.reset()
|
||||||
|
return battery
|
||||||
|
|
||||||
|
|
||||||
|
def test_car_and_pv_battery_discharge_and_max_charge_power(setup_pv_battery, setup_car_battery):
|
||||||
|
pv_battery = setup_pv_battery
|
||||||
|
car_battery = setup_car_battery
|
||||||
|
|
||||||
|
# Test discharge for PV battery
|
||||||
|
pv_discharged_wh, pv_loss_wh = pv_battery.discharge_energy(3000, 5)
|
||||||
|
assert pv_discharged_wh > 0, "PV battery should discharge energy"
|
||||||
|
assert (
|
||||||
|
pv_battery.current_soc_percentage() >= pv_battery.parameters.min_soc_percentage
|
||||||
|
), "PV battery SOC should stay above min SOC"
|
||||||
|
assert (
|
||||||
|
pv_battery.parameters.max_charge_power_w == 8000
|
||||||
|
), "PV battery max charge power should remain as defined"
|
||||||
|
|
||||||
|
# Test discharge for car battery
|
||||||
|
car_discharged_wh, car_loss_wh = car_battery.discharge_energy(5000, 10)
|
||||||
|
assert car_discharged_wh > 0, "Car battery should discharge energy"
|
||||||
|
assert (
|
||||||
|
car_battery.current_soc_percentage() >= car_battery.parameters.min_soc_percentage
|
||||||
|
), "Car battery SOC should stay above min SOC"
|
||||||
|
assert (
|
||||||
|
car_battery.parameters.max_charge_power_w == 7000
|
||||||
|
), "Car battery max charge power should remain as defined"
|
@ -1,143 +0,0 @@
|
|||||||
import unittest
|
|
||||||
|
|
||||||
from akkudoktoreos.devices.battery import PVAkku, PVAkkuParameters
|
|
||||||
|
|
||||||
|
|
||||||
class TestPVAkku(unittest.TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
# Initializing common parameters for tests
|
|
||||||
self.kapazitaet_wh = 10000 # 10,000 Wh capacity
|
|
||||||
self.lade_effizienz = 0.88
|
|
||||||
self.entlade_effizienz = 0.88
|
|
||||||
self.min_soc_prozent = 20 # Minimum SoC is 20%
|
|
||||||
self.max_soc_prozent = 80 # Maximum SoC is 80%
|
|
||||||
|
|
||||||
def test_initial_state_of_charge(self):
|
|
||||||
akku = PVAkku(
|
|
||||||
PVAkkuParameters(
|
|
||||||
kapazitaet_wh=self.kapazitaet_wh,
|
|
||||||
start_soc_prozent=50,
|
|
||||||
min_soc_prozent=self.min_soc_prozent,
|
|
||||||
max_soc_prozent=self.max_soc_prozent,
|
|
||||||
),
|
|
||||||
hours=1,
|
|
||||||
)
|
|
||||||
self.assertEqual(akku.ladezustand_in_prozent(), 50.0, "Initial SoC should be 50%")
|
|
||||||
|
|
||||||
def test_discharge_below_min_soc(self):
|
|
||||||
akku = PVAkku(
|
|
||||||
PVAkkuParameters(
|
|
||||||
kapazitaet_wh=self.kapazitaet_wh,
|
|
||||||
start_soc_prozent=50,
|
|
||||||
min_soc_prozent=self.min_soc_prozent,
|
|
||||||
max_soc_prozent=self.max_soc_prozent,
|
|
||||||
),
|
|
||||||
hours=1,
|
|
||||||
)
|
|
||||||
akku.reset()
|
|
||||||
# Try to discharge more energy than available above min_soc
|
|
||||||
abgegeben_wh, verlust_wh = akku.energie_abgeben(5000, 0) # Try to discharge 5000 Wh
|
|
||||||
expected_soc = self.min_soc_prozent # SoC should not drop below min_soc
|
|
||||||
self.assertEqual(
|
|
||||||
akku.ladezustand_in_prozent(),
|
|
||||||
expected_soc,
|
|
||||||
"SoC should not drop below min_soc after discharge",
|
|
||||||
)
|
|
||||||
self.assertEqual(abgegeben_wh, 2640.0, "The energy discharged should be limited by min_soc")
|
|
||||||
|
|
||||||
def test_charge_above_max_soc(self):
|
|
||||||
akku = PVAkku(
|
|
||||||
PVAkkuParameters(
|
|
||||||
kapazitaet_wh=self.kapazitaet_wh,
|
|
||||||
start_soc_prozent=50,
|
|
||||||
min_soc_prozent=self.min_soc_prozent,
|
|
||||||
max_soc_prozent=self.max_soc_prozent,
|
|
||||||
),
|
|
||||||
hours=1,
|
|
||||||
)
|
|
||||||
akku.reset()
|
|
||||||
# Try to charge more energy than available up to max_soc
|
|
||||||
geladen_wh, verlust_wh = akku.energie_laden(5000, 0) # Try to charge 5000 Wh
|
|
||||||
expected_soc = self.max_soc_prozent # SoC should not exceed max_soc
|
|
||||||
self.assertEqual(
|
|
||||||
akku.ladezustand_in_prozent(),
|
|
||||||
expected_soc,
|
|
||||||
"SoC should not exceed max_soc after charge",
|
|
||||||
)
|
|
||||||
self.assertEqual(geladen_wh, 3000.0, "The energy charged should be limited by max_soc")
|
|
||||||
|
|
||||||
def test_charging_at_max_soc(self):
|
|
||||||
akku = PVAkku(
|
|
||||||
PVAkkuParameters(
|
|
||||||
kapazitaet_wh=self.kapazitaet_wh,
|
|
||||||
start_soc_prozent=80,
|
|
||||||
min_soc_prozent=self.min_soc_prozent,
|
|
||||||
max_soc_prozent=self.max_soc_prozent,
|
|
||||||
),
|
|
||||||
hours=1,
|
|
||||||
)
|
|
||||||
akku.reset()
|
|
||||||
# Try to charge when SoC is already at max_soc
|
|
||||||
geladen_wh, verlust_wh = akku.energie_laden(5000, 0)
|
|
||||||
self.assertEqual(geladen_wh, 0.0, "No energy should be charged when at max_soc")
|
|
||||||
self.assertEqual(
|
|
||||||
akku.ladezustand_in_prozent(),
|
|
||||||
self.max_soc_prozent,
|
|
||||||
"SoC should remain at max_soc",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_discharging_at_min_soc(self):
|
|
||||||
akku = PVAkku(
|
|
||||||
PVAkkuParameters(
|
|
||||||
kapazitaet_wh=self.kapazitaet_wh,
|
|
||||||
start_soc_prozent=20,
|
|
||||||
min_soc_prozent=self.min_soc_prozent,
|
|
||||||
max_soc_prozent=self.max_soc_prozent,
|
|
||||||
),
|
|
||||||
hours=1,
|
|
||||||
)
|
|
||||||
akku.reset()
|
|
||||||
# Try to discharge when SoC is already at min_soc
|
|
||||||
abgegeben_wh, verlust_wh = akku.energie_abgeben(5000, 0)
|
|
||||||
self.assertEqual(abgegeben_wh, 0.0, "No energy should be discharged when at min_soc")
|
|
||||||
self.assertEqual(
|
|
||||||
akku.ladezustand_in_prozent(),
|
|
||||||
self.min_soc_prozent,
|
|
||||||
"SoC should remain at min_soc",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_soc_limits(self):
|
|
||||||
# Test to ensure that SoC never exceeds max_soc or drops below min_soc
|
|
||||||
akku = PVAkku(
|
|
||||||
PVAkkuParameters(
|
|
||||||
kapazitaet_wh=self.kapazitaet_wh,
|
|
||||||
start_soc_prozent=50,
|
|
||||||
min_soc_prozent=self.min_soc_prozent,
|
|
||||||
max_soc_prozent=self.max_soc_prozent,
|
|
||||||
),
|
|
||||||
hours=1,
|
|
||||||
)
|
|
||||||
akku.reset()
|
|
||||||
akku.soc_wh = (
|
|
||||||
self.max_soc_prozent / 100
|
|
||||||
) * self.kapazitaet_wh + 1000 # Manually set SoC above max limit
|
|
||||||
akku.soc_wh = min(akku.soc_wh, akku.max_soc_wh)
|
|
||||||
self.assertLessEqual(
|
|
||||||
akku.ladezustand_in_prozent(),
|
|
||||||
self.max_soc_prozent,
|
|
||||||
"SoC should not exceed max_soc",
|
|
||||||
)
|
|
||||||
|
|
||||||
akku.soc_wh = (
|
|
||||||
self.min_soc_prozent / 100
|
|
||||||
) * self.kapazitaet_wh - 1000 # Manually set SoC below min limit
|
|
||||||
akku.soc_wh = max(akku.soc_wh, akku.min_soc_wh)
|
|
||||||
self.assertGreaterEqual(
|
|
||||||
akku.ladezustand_in_prozent(),
|
|
||||||
self.min_soc_prozent,
|
|
||||||
"SoC should not drop below min_soc",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
@ -8,7 +8,11 @@ from akkudoktoreos.core.ems import (
|
|||||||
SimulationResult,
|
SimulationResult,
|
||||||
get_ems,
|
get_ems,
|
||||||
)
|
)
|
||||||
from akkudoktoreos.devices.battery import EAutoParameters, PVAkku, PVAkkuParameters
|
from akkudoktoreos.devices.battery import (
|
||||||
|
Battery,
|
||||||
|
ElectricVehicleParameters,
|
||||||
|
SolarPanelBatteryParameters,
|
||||||
|
)
|
||||||
from akkudoktoreos.devices.generic import HomeAppliance, HomeApplianceParameters
|
from akkudoktoreos.devices.generic import HomeAppliance, HomeApplianceParameters
|
||||||
from akkudoktoreos.devices.inverter import Inverter, InverterParameters
|
from akkudoktoreos.devices.inverter import Inverter, InverterParameters
|
||||||
|
|
||||||
@ -25,8 +29,10 @@ def create_ems_instance() -> EnergieManagementSystem:
|
|||||||
assert config_eos.prediction_hours is not None
|
assert config_eos.prediction_hours is not None
|
||||||
|
|
||||||
# Initialize the battery and the inverter
|
# Initialize the battery and the inverter
|
||||||
akku = PVAkku(
|
akku = Battery(
|
||||||
PVAkkuParameters(kapazitaet_wh=5000, start_soc_prozent=80, min_soc_prozent=10),
|
SolarPanelBatteryParameters(
|
||||||
|
capacity_wh=5000, initial_soc_percentage=80, min_soc_percentage=10
|
||||||
|
),
|
||||||
hours=config_eos.prediction_hours,
|
hours=config_eos.prediction_hours,
|
||||||
)
|
)
|
||||||
akku.reset()
|
akku.reset()
|
||||||
@ -43,8 +49,10 @@ def create_ems_instance() -> EnergieManagementSystem:
|
|||||||
home_appliance.set_starting_time(2)
|
home_appliance.set_starting_time(2)
|
||||||
|
|
||||||
# Example initialization of electric car battery
|
# Example initialization of electric car battery
|
||||||
eauto = PVAkku(
|
eauto = Battery(
|
||||||
EAutoParameters(kapazitaet_wh=26400, start_soc_prozent=10, min_soc_prozent=10),
|
ElectricVehicleParameters(
|
||||||
|
capacity_wh=26400, initial_soc_percentage=10, min_soc_percentage=10
|
||||||
|
),
|
||||||
hours=config_eos.prediction_hours,
|
hours=config_eos.prediction_hours,
|
||||||
)
|
)
|
||||||
eauto.set_charge_per_hour(np.full(config_eos.prediction_hours, 1))
|
eauto.set_charge_per_hour(np.full(config_eos.prediction_hours, 1))
|
||||||
|
@ -7,7 +7,11 @@ from akkudoktoreos.core.ems import (
|
|||||||
EnergieManagementSystemParameters,
|
EnergieManagementSystemParameters,
|
||||||
get_ems,
|
get_ems,
|
||||||
)
|
)
|
||||||
from akkudoktoreos.devices.battery import EAutoParameters, PVAkku, PVAkkuParameters
|
from akkudoktoreos.devices.battery import (
|
||||||
|
Battery,
|
||||||
|
ElectricVehicleParameters,
|
||||||
|
SolarPanelBatteryParameters,
|
||||||
|
)
|
||||||
from akkudoktoreos.devices.generic import HomeAppliance, HomeApplianceParameters
|
from akkudoktoreos.devices.generic import HomeAppliance, HomeApplianceParameters
|
||||||
from akkudoktoreos.devices.inverter import Inverter, InverterParameters
|
from akkudoktoreos.devices.inverter import Inverter, InverterParameters
|
||||||
|
|
||||||
@ -24,8 +28,10 @@ def create_ems_instance() -> EnergieManagementSystem:
|
|||||||
assert config_eos.prediction_hours is not None
|
assert config_eos.prediction_hours is not None
|
||||||
|
|
||||||
# Initialize the battery and the inverter
|
# Initialize the battery and the inverter
|
||||||
akku = PVAkku(
|
akku = Battery(
|
||||||
PVAkkuParameters(kapazitaet_wh=5000, start_soc_prozent=80, min_soc_prozent=10),
|
SolarPanelBatteryParameters(
|
||||||
|
capacity_wh=5000, initial_soc_percentage=80, min_soc_percentage=10
|
||||||
|
),
|
||||||
hours=config_eos.prediction_hours,
|
hours=config_eos.prediction_hours,
|
||||||
)
|
)
|
||||||
akku.reset()
|
akku.reset()
|
||||||
@ -42,8 +48,10 @@ def create_ems_instance() -> EnergieManagementSystem:
|
|||||||
home_appliance.set_starting_time(2)
|
home_appliance.set_starting_time(2)
|
||||||
|
|
||||||
# Example initialization of electric car battery
|
# Example initialization of electric car battery
|
||||||
eauto = PVAkku(
|
eauto = Battery(
|
||||||
EAutoParameters(kapazitaet_wh=26400, start_soc_prozent=100, min_soc_prozent=100),
|
ElectricVehicleParameters(
|
||||||
|
capacity_wh=26400, initial_soc_percentage=100, min_soc_percentage=100
|
||||||
|
),
|
||||||
hours=config_eos.prediction_hours,
|
hours=config_eos.prediction_hours,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ from akkudoktoreos.devices.inverter import Inverter, InverterParameters
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_battery():
|
def mock_battery():
|
||||||
mock_battery = Mock()
|
mock_battery = Mock()
|
||||||
mock_battery.energie_laden = Mock(return_value=(0.0, 0.0))
|
mock_battery.charge_energy = Mock(return_value=(0.0, 0.0))
|
||||||
mock_battery.energie_abgeben = Mock(return_value=(0.0, 0.0))
|
mock_battery.discharge_energy = Mock(return_value=(0.0, 0.0))
|
||||||
return mock_battery
|
return mock_battery
|
||||||
|
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ def inverter(mock_battery):
|
|||||||
|
|
||||||
def test_process_energy_excess_generation(inverter, mock_battery):
|
def test_process_energy_excess_generation(inverter, mock_battery):
|
||||||
# Battery charges 100 Wh with 10 Wh loss
|
# Battery charges 100 Wh with 10 Wh loss
|
||||||
mock_battery.energie_laden.return_value = (100.0, 10.0)
|
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
||||||
generation = 600.0
|
generation = 600.0
|
||||||
consumption = 200.0
|
consumption = 200.0
|
||||||
hour = 12
|
hour = 12
|
||||||
@ -33,7 +33,7 @@ def test_process_energy_excess_generation(inverter, mock_battery):
|
|||||||
assert grid_import == 0.0 # No grid draw
|
assert grid_import == 0.0 # No grid draw
|
||||||
assert losses == 10.0 # Battery charging losses
|
assert losses == 10.0 # Battery charging losses
|
||||||
assert self_consumption == 200.0 # All consumption is met
|
assert self_consumption == 200.0 # All consumption is met
|
||||||
mock_battery.energie_laden.assert_called_once_with(400.0, hour)
|
mock_battery.charge_energy.assert_called_once_with(400.0, hour)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_generation_equals_consumption(inverter, mock_battery):
|
def test_process_energy_generation_equals_consumption(inverter, mock_battery):
|
||||||
@ -50,12 +50,12 @@ def test_process_energy_generation_equals_consumption(inverter, mock_battery):
|
|||||||
assert losses == 0.0 # No losses
|
assert losses == 0.0 # No losses
|
||||||
assert self_consumption == 300.0 # All consumption is met with generation
|
assert self_consumption == 300.0 # All consumption is met with generation
|
||||||
|
|
||||||
mock_battery.energie_laden.assert_called_once_with(0.0, hour)
|
mock_battery.charge_energy.assert_called_once_with(0.0, hour)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_battery_discharges(inverter, mock_battery):
|
def test_process_energy_battery_discharges(inverter, mock_battery):
|
||||||
# Battery discharges 100 Wh with 10 Wh loss already accounted for in the discharge
|
# Battery discharges 100 Wh with 10 Wh loss already accounted for in the discharge
|
||||||
mock_battery.energie_abgeben.return_value = (100.0, 10.0)
|
mock_battery.discharge_energy.return_value = (100.0, 10.0)
|
||||||
generation = 100.0
|
generation = 100.0
|
||||||
consumption = 250.0
|
consumption = 250.0
|
||||||
hour = 12
|
hour = 12
|
||||||
@ -70,12 +70,12 @@ def test_process_energy_battery_discharges(inverter, mock_battery):
|
|||||||
) # Grid supplies remaining shortfall after battery discharge
|
) # Grid supplies remaining shortfall after battery discharge
|
||||||
assert losses == 10.0 # Discharge losses
|
assert losses == 10.0 # Discharge losses
|
||||||
assert self_consumption == 200.0 # Generation + battery discharge
|
assert self_consumption == 200.0 # Generation + battery discharge
|
||||||
mock_battery.energie_abgeben.assert_called_once_with(150.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(150.0, hour)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_battery_empty(inverter, mock_battery):
|
def test_process_energy_battery_empty(inverter, mock_battery):
|
||||||
# Battery is empty, so no energy can be discharged
|
# Battery is empty, so no energy can be discharged
|
||||||
mock_battery.energie_abgeben.return_value = (0.0, 0.0)
|
mock_battery.discharge_energy.return_value = (0.0, 0.0)
|
||||||
generation = 100.0
|
generation = 100.0
|
||||||
consumption = 300.0
|
consumption = 300.0
|
||||||
hour = 12
|
hour = 12
|
||||||
@ -88,12 +88,12 @@ def test_process_energy_battery_empty(inverter, mock_battery):
|
|||||||
assert grid_import == pytest.approx(200.0, rel=1e-2) # Grid has to cover the full shortfall
|
assert grid_import == pytest.approx(200.0, rel=1e-2) # Grid has to cover the full shortfall
|
||||||
assert losses == 0.0 # No losses as the battery didn't discharge
|
assert losses == 0.0 # No losses as the battery didn't discharge
|
||||||
assert self_consumption == 100.0 # Only generation is consumed
|
assert self_consumption == 100.0 # Only generation is consumed
|
||||||
mock_battery.energie_abgeben.assert_called_once_with(200.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(200.0, hour)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_battery_full_at_start(inverter, mock_battery):
|
def test_process_energy_battery_full_at_start(inverter, mock_battery):
|
||||||
# Battery is full, so no charging happens
|
# Battery is full, so no charging happens
|
||||||
mock_battery.energie_laden.return_value = (0.0, 0.0)
|
mock_battery.charge_energy.return_value = (0.0, 0.0)
|
||||||
generation = 500.0
|
generation = 500.0
|
||||||
consumption = 200.0
|
consumption = 200.0
|
||||||
hour = 12
|
hour = 12
|
||||||
@ -108,12 +108,12 @@ def test_process_energy_battery_full_at_start(inverter, mock_battery):
|
|||||||
assert grid_import == 0.0 # No grid draw
|
assert grid_import == 0.0 # No grid draw
|
||||||
assert losses == 0.0 # No losses
|
assert losses == 0.0 # No losses
|
||||||
assert self_consumption == 200.0 # Only consumption is met
|
assert self_consumption == 200.0 # Only consumption is met
|
||||||
mock_battery.energie_laden.assert_called_once_with(300.0, hour)
|
mock_battery.charge_energy.assert_called_once_with(300.0, hour)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_insufficient_generation_no_battery(inverter, mock_battery):
|
def test_process_energy_insufficient_generation_no_battery(inverter, mock_battery):
|
||||||
# Insufficient generation and no battery discharge
|
# Insufficient generation and no battery discharge
|
||||||
mock_battery.energie_abgeben.return_value = (0.0, 0.0)
|
mock_battery.discharge_energy.return_value = (0.0, 0.0)
|
||||||
generation = 100.0
|
generation = 100.0
|
||||||
consumption = 500.0
|
consumption = 500.0
|
||||||
hour = 12
|
hour = 12
|
||||||
@ -126,12 +126,12 @@ def test_process_energy_insufficient_generation_no_battery(inverter, mock_batter
|
|||||||
assert grid_import == pytest.approx(400.0, rel=1e-2) # Grid supplies the shortfall
|
assert grid_import == pytest.approx(400.0, rel=1e-2) # Grid supplies the shortfall
|
||||||
assert losses == 0.0 # No losses
|
assert losses == 0.0 # No losses
|
||||||
assert self_consumption == 100.0 # Only generation is consumed
|
assert self_consumption == 100.0 # Only generation is consumed
|
||||||
mock_battery.energie_abgeben.assert_called_once_with(400.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(400.0, hour)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_insufficient_generation_battery_assists(inverter, mock_battery):
|
def test_process_energy_insufficient_generation_battery_assists(inverter, mock_battery):
|
||||||
# Battery assists with some discharge to cover the shortfall
|
# Battery assists with some discharge to cover the shortfall
|
||||||
mock_battery.energie_abgeben.return_value = (
|
mock_battery.discharge_energy.return_value = (
|
||||||
50.0,
|
50.0,
|
||||||
5.0,
|
5.0,
|
||||||
) # Battery discharges 50 Wh with 5 Wh loss
|
) # Battery discharges 50 Wh with 5 Wh loss
|
||||||
@ -149,12 +149,12 @@ def test_process_energy_insufficient_generation_battery_assists(inverter, mock_b
|
|||||||
) # Grid supplies the remaining shortfall after battery discharge
|
) # Grid supplies the remaining shortfall after battery discharge
|
||||||
assert losses == 5.0 # Discharge losses
|
assert losses == 5.0 # Discharge losses
|
||||||
assert self_consumption == 250.0 # Generation + battery discharge
|
assert self_consumption == 250.0 # Generation + battery discharge
|
||||||
mock_battery.energie_abgeben.assert_called_once_with(200.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(200.0, hour)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_zero_generation(inverter, mock_battery):
|
def test_process_energy_zero_generation(inverter, mock_battery):
|
||||||
# Zero generation, full reliance on battery and grid
|
# Zero generation, full reliance on battery and grid
|
||||||
mock_battery.energie_abgeben.return_value = (
|
mock_battery.discharge_energy.return_value = (
|
||||||
100.0,
|
100.0,
|
||||||
5.0,
|
5.0,
|
||||||
) # Battery discharges 100 Wh with 5 Wh loss
|
) # Battery discharges 100 Wh with 5 Wh loss
|
||||||
@ -170,12 +170,12 @@ def test_process_energy_zero_generation(inverter, mock_battery):
|
|||||||
assert grid_import == pytest.approx(200.0, rel=1e-2) # Grid supplies the remaining shortfall
|
assert grid_import == pytest.approx(200.0, rel=1e-2) # Grid supplies the remaining shortfall
|
||||||
assert losses == 5.0 # Discharge losses
|
assert losses == 5.0 # Discharge losses
|
||||||
assert self_consumption == 100.0 # Only battery discharge is consumed
|
assert self_consumption == 100.0 # Only battery discharge is consumed
|
||||||
mock_battery.energie_abgeben.assert_called_once_with(300.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(300.0, hour)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_zero_consumption(inverter, mock_battery):
|
def test_process_energy_zero_consumption(inverter, mock_battery):
|
||||||
# Generation exceeds consumption, but consumption is zero
|
# Generation exceeds consumption, but consumption is zero
|
||||||
mock_battery.energie_laden.return_value = (100.0, 10.0)
|
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
||||||
generation = 500.0
|
generation = 500.0
|
||||||
consumption = 0.0
|
consumption = 0.0
|
||||||
hour = 12
|
hour = 12
|
||||||
@ -188,7 +188,7 @@ def test_process_energy_zero_consumption(inverter, mock_battery):
|
|||||||
assert grid_import == 0.0 # No grid draw as no consumption
|
assert grid_import == 0.0 # No grid draw as no consumption
|
||||||
assert losses == 10.0 # Charging losses
|
assert losses == 10.0 # Charging losses
|
||||||
assert self_consumption == 0.0 # Zero consumption
|
assert self_consumption == 0.0 # Zero consumption
|
||||||
mock_battery.energie_laden.assert_called_once_with(500.0, hour)
|
mock_battery.charge_energy.assert_called_once_with(500.0, hour)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_zero_generation_zero_consumption(inverter, mock_battery):
|
def test_process_energy_zero_generation_zero_consumption(inverter, mock_battery):
|
||||||
@ -207,7 +207,7 @@ def test_process_energy_zero_generation_zero_consumption(inverter, mock_battery)
|
|||||||
|
|
||||||
|
|
||||||
def test_process_energy_partial_battery_discharge(inverter, mock_battery):
|
def test_process_energy_partial_battery_discharge(inverter, mock_battery):
|
||||||
mock_battery.energie_abgeben.return_value = (50.0, 5.0)
|
mock_battery.discharge_energy.return_value = (50.0, 5.0)
|
||||||
generation = 200.0
|
generation = 200.0
|
||||||
consumption = 400.0
|
consumption = 400.0
|
||||||
hour = 12
|
hour = 12
|
||||||
@ -226,7 +226,7 @@ def test_process_energy_partial_battery_discharge(inverter, mock_battery):
|
|||||||
|
|
||||||
def test_process_energy_consumption_exceeds_max_no_battery(inverter, mock_battery):
|
def test_process_energy_consumption_exceeds_max_no_battery(inverter, mock_battery):
|
||||||
# Battery is empty, and consumption is much higher than the inverter's max power
|
# Battery is empty, and consumption is much higher than the inverter's max power
|
||||||
mock_battery.energie_abgeben.return_value = (0.0, 0.0)
|
mock_battery.discharge_energy.return_value = (0.0, 0.0)
|
||||||
generation = 100.0
|
generation = 100.0
|
||||||
consumption = 1000.0 # Exceeds the inverter's max power
|
consumption = 1000.0 # Exceeds the inverter's max power
|
||||||
hour = 12
|
hour = 12
|
||||||
@ -239,12 +239,12 @@ def test_process_energy_consumption_exceeds_max_no_battery(inverter, mock_batter
|
|||||||
assert grid_import == pytest.approx(900.0, rel=1e-2) # Grid covers the remaining shortfall
|
assert grid_import == pytest.approx(900.0, rel=1e-2) # Grid covers the remaining shortfall
|
||||||
assert losses == 0.0 # No losses as the battery didn’t assist
|
assert losses == 0.0 # No losses as the battery didn’t assist
|
||||||
assert self_consumption == 100.0 # Only the generation is consumed, maxing out the inverter
|
assert self_consumption == 100.0 # Only the generation is consumed, maxing out the inverter
|
||||||
mock_battery.energie_abgeben.assert_called_once_with(400.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(400.0, hour)
|
||||||
|
|
||||||
|
|
||||||
def test_process_energy_zero_generation_full_battery_high_consumption(inverter, mock_battery):
|
def test_process_energy_zero_generation_full_battery_high_consumption(inverter, mock_battery):
|
||||||
# Full battery, no generation, and high consumption
|
# Full battery, no generation, and high consumption
|
||||||
mock_battery.energie_abgeben.return_value = (500.0, 10.0)
|
mock_battery.discharge_energy.return_value = (500.0, 10.0)
|
||||||
generation = 0.0
|
generation = 0.0
|
||||||
consumption = 600.0
|
consumption = 600.0
|
||||||
hour = 12
|
hour = 12
|
||||||
@ -259,4 +259,4 @@ def test_process_energy_zero_generation_full_battery_high_consumption(inverter,
|
|||||||
) # Grid covers remaining shortfall after battery discharge
|
) # Grid covers remaining shortfall after battery discharge
|
||||||
assert losses == 10.0 # Battery discharge losses
|
assert losses == 10.0 # Battery discharge losses
|
||||||
assert self_consumption == 500.0 # Battery fully discharges to meet consumption
|
assert self_consumption == 500.0 # Battery fully discharges to meet consumption
|
||||||
mock_battery.energie_abgeben.assert_called_once_with(500.0, hour)
|
mock_battery.discharge_energy.assert_called_once_with(500.0, hour)
|
||||||
|
24
tests/testdata/optimize_input_1.json
vendored
24
tests/testdata/optimize_input_1.json
vendored
@ -26,21 +26,21 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"pv_akku": {
|
"pv_akku": {
|
||||||
"kapazitaet_wh": 26400,
|
"capacity_wh": 26400,
|
||||||
"max_ladeleistung_w": 5000,
|
"max_charge_power_w": 5000,
|
||||||
"start_soc_prozent": 80,
|
"initial_soc_percentage": 80,
|
||||||
"min_soc_prozent": 15
|
"min_soc_percentage": 15
|
||||||
},
|
},
|
||||||
"inverter": {
|
"wechselrichter": {
|
||||||
"max_power_wh": 10000
|
"max_leistung_wh": 10000
|
||||||
},
|
},
|
||||||
"eauto": {
|
"eauto": {
|
||||||
"kapazitaet_wh": 60000,
|
"capacity_wh": 60000,
|
||||||
"lade_effizienz": 0.95,
|
"charging_efficiency": 0.95,
|
||||||
"entlade_effizienz": 1.0,
|
"discharging_efficiency": 1.0,
|
||||||
"max_ladeleistung_w": 11040,
|
"max_charge_power_w": 11040,
|
||||||
"start_soc_prozent": 54,
|
"initial_soc_percentage": 54,
|
||||||
"min_soc_prozent": 0
|
"min_soc_percentage": 0
|
||||||
},
|
},
|
||||||
"temperature_forecast": [
|
"temperature_forecast": [
|
||||||
18.3, 17.8, 16.9, 16.2, 15.6, 15.1, 14.6, 14.2, 14.3, 14.8, 15.7, 16.7, 17.4,
|
18.3, 17.8, 16.9, 16.2, 15.6, 15.1, 14.6, 14.2, 14.3, 14.8, 15.7, 16.7, 17.4,
|
||||||
|
16
tests/testdata/optimize_input_2.json
vendored
16
tests/testdata/optimize_input_2.json
vendored
@ -26,16 +26,16 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"pv_akku": {
|
"pv_akku": {
|
||||||
"kapazitaet_wh": 26400,
|
"capacity_wh": 26400,
|
||||||
"start_soc_prozent": 80,
|
"initial_soc_percentage": 80,
|
||||||
"min_soc_prozent": 15
|
"min_soc_percentage": 15
|
||||||
},
|
},
|
||||||
"eauto": {
|
"eauto": {
|
||||||
"kapazitaet_wh": 60000,
|
"capacity_wh": 60000,
|
||||||
"lade_effizienz": 0.95,
|
"charging_efficiency": 0.95,
|
||||||
"max_ladeleistung_w": 11040,
|
"max_charge_power_w": 11040,
|
||||||
"start_soc_prozent": 5,
|
"initial_soc_percentage": 5,
|
||||||
"min_soc_prozent": 80
|
"min_soc_percentage": 80
|
||||||
},
|
},
|
||||||
"dishwasher" :{
|
"dishwasher" :{
|
||||||
"consumption_wh": 5000,
|
"consumption_wh": 5000,
|
||||||
|
12
tests/testdata/optimize_result_1.json
vendored
12
tests/testdata/optimize_result_1.json
vendored
@ -617,13 +617,13 @@
|
|||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
],
|
],
|
||||||
"entlade_effizienz": 1.0,
|
"discharging_efficiency": 1.0,
|
||||||
"hours": 48,
|
"hours": 48,
|
||||||
"kapazitaet_wh": 60000,
|
"capacity_wh": 60000,
|
||||||
"lade_effizienz": 0.95,
|
"charging_efficiency": 0.95,
|
||||||
"max_ladeleistung_w": 11040,
|
"max_charge_power_w": 11040,
|
||||||
"soc_wh": 32400.000000000004,
|
"soc_wh": 32400.000000000004,
|
||||||
"start_soc_prozent": 54
|
"initial_soc_percentage": 54
|
||||||
},
|
},
|
||||||
"start_solution": [
|
"start_solution": [
|
||||||
18.0,
|
18.0,
|
||||||
@ -676,4 +676,4 @@
|
|||||||
10.0
|
10.0
|
||||||
],
|
],
|
||||||
"washingstart": null
|
"washingstart": null
|
||||||
}
|
}
|
||||||
|
12
tests/testdata/optimize_result_2.json
vendored
12
tests/testdata/optimize_result_2.json
vendored
@ -666,13 +666,13 @@
|
|||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
],
|
],
|
||||||
"entlade_effizienz": 1.0,
|
"discharging_efficiency": 1.0,
|
||||||
"hours": 48,
|
"hours": 48,
|
||||||
"kapazitaet_wh": 60000,
|
"capacity_wh": 60000,
|
||||||
"lade_effizienz": 0.95,
|
"charging_efficiency": 0.95,
|
||||||
"max_ladeleistung_w": 11040,
|
"max_charge_power_w": 11040,
|
||||||
"soc_wh": 60000.0,
|
"soc_wh": 60000.0,
|
||||||
"start_soc_prozent": 5
|
"initial_soc_percentage": 5
|
||||||
},
|
},
|
||||||
"start_solution": [
|
"start_solution": [
|
||||||
0.0,
|
0.0,
|
||||||
@ -774,4 +774,4 @@
|
|||||||
14.0
|
14.0
|
||||||
],
|
],
|
||||||
"washingstart": 14
|
"washingstart": 14
|
||||||
}
|
}
|
||||||
|
12
tests/testdata/optimize_result_2_full.json
vendored
12
tests/testdata/optimize_result_2_full.json
vendored
@ -666,13 +666,13 @@
|
|||||||
1,
|
1,
|
||||||
1
|
1
|
||||||
],
|
],
|
||||||
"entlade_effizienz": 1.0,
|
"discharging_efficiency": 1.0,
|
||||||
"hours": 48,
|
"hours": 48,
|
||||||
"kapazitaet_wh": 60000,
|
"capacity_wh": 60000,
|
||||||
"lade_effizienz": 0.95,
|
"charging_efficiency": 0.95,
|
||||||
"max_ladeleistung_w": 11040,
|
"max_charge_power_w": 11040,
|
||||||
"soc_wh": 60000.0,
|
"soc_wh": 60000.0,
|
||||||
"start_soc_prozent": 5
|
"initial_soc_percentage": 5
|
||||||
},
|
},
|
||||||
"start_solution": [
|
"start_solution": [
|
||||||
12.0,
|
12.0,
|
||||||
@ -774,4 +774,4 @@
|
|||||||
14.0
|
14.0
|
||||||
],
|
],
|
||||||
"washingstart": 14
|
"washingstart": 14
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user