From 1905682113f567c9208bfb430ffca16d5a83eb27 Mon Sep 17 00:00:00 2001 From: Bobby Noelte Date: Sat, 1 Aug 2026 12:45:19 +0200 Subject: [PATCH] chore: adapt pdf visualization (#1205) Change PDF visualization to be created on demand and per optimization algorithm. The PDF for the GENETIC0 optimization is provided by the /visualization_results.pdf endpoint. There is no change in the interface. By this the optimization algorithm is offloaded from the PDF generation which spares some time. To cope with several users may call the /visualization_results.pdf endpoint at the same time the PDF is generated on the fly without any intermediate file taking the stored GENETIC0 solution as an input. SVG picture generation is removed as this would again create intermediate files. Chart pictures can easily be taken from the PDF. To allow on demand creation of the optimization results visualization the optimisation solution stored is extended by several new attributes. To keep the deprecated /optimize endpoint compatible the optimization solution is stripped to the legacy content before returned. Due to the extension of the solution the optimization tests were adapted to cover the extended content. The optimization tests are adapted to test the generated visualization report by the pypdf reader. Pypdf is added to the development dependencies. Besides the adaptation several fixes and improvements are added: * feat: extend /v1/prediction/series endpoint by resampling and filling Add parameters for resampling and filling. Add the processing parameter to control wether raw data or resampled data shall be returned. * feat: extend /v1/measurement/series endpoint by resampling and filling Add parameters for resampling and filling: Add the processing parameter to control wether raw data or resampled data shall be returned. * feat: standardize and improve API error response Use FASTApi exception handlers to provide a standardized API exception handling. All exceptions are logged. Exception traces are only returned if the new logging configuration parameter logging.api_logging_level is set to "DEBUG" or "TRACE". Avoids unwanted leackage of server internals on exceptions. * fix: align to intervall when resampling Ensure resampling is aligned to interval also when the buckets are shifted due to the align_to_intervall parameter is set. * chore: make dropna mandatory and default to True * chore: refactor key_to_xxx data management methods Make key_to_series the central method for data resampling and fill. Add a new key_to_raw_series to retrieve the data as it is stored (without resampling and filling). Users of key_to_series were mostly moved to key_to_raw_series as this resembles the former interface. Especially in predictions and tests this was done. * chore: create test data sub-directory for each optimization algorithm To prevent cluttering the test data directory and ease test data management for optimization algorithms each algorithm got it's own sub-directory. The current test data was moved to these sub-directories. * chore: update version Signed-off-by: Bobby Noelte --- .env | 2 +- config.yaml | 2 +- docs/_generated/configexample.md | 1 + docs/_generated/configlogging.md | 7 +- docs/_generated/openapi.md | 175 +- openapi.json | 1643 +++++++- pyproject.toml | 1 + src/akkudoktoreos/core/dataabc.py | 438 +- src/akkudoktoreos/core/databaseabc.py | 2 +- src/akkudoktoreos/core/logsettings.py | 12 +- .../optimization/genetic/genetic.py | 29 +- .../optimization/genetic/geneticsolution.py | 34 +- .../genetic/geneticvisualize.py} | 282 +- .../optimization/genetic0/genetic0.py | 31 +- .../optimization/genetic0/genetic0solution.py | 113 +- .../genetic0/genetic0visualize.py | 801 ++++ .../prediction/elecpriceenergycharts.py | 2 +- .../prediction/feedintariffenergycharts.py | 4 +- .../prediction/weatherbrightsky.py | 2 +- .../prediction/weatheropenmeteo.py | 2 +- src/akkudoktoreos/server/eos.py | 1208 ++++-- src/akkudoktoreos/server/rest/error.py | 105 + tests/test_dataabcsequence.py | 6 +- tests/test_databaseabc.py | 2 +- tests/test_elecpricetibber.py | 2 +- tests/test_feedintariffenergycharts.py | 2 +- tests/test_genetic0optimize.py | 88 +- tests/test_genetic0visualize.py | 37 + tests/test_geneticoptimize.py | 78 +- ..._visualize.py => test_geneticvisualize.py} | 15 +- tests/test_pvforecastakkudoktor.py | 4 +- tests/test_system.py | 88 +- .../testdata/docs/_generated/configexample.md | 1 + .../testdata/docs/_generated/configlogging.md | 7 +- .../{ => genetic}/optimize_input_1.json | 0 .../{ => genetic}/optimize_input_2.json | 0 tests/testdata/genetic/optimize_result_1.json | 2218 ++++++++++ .../genetic/optimize_result_1_be.json | 2218 ++++++++++ tests/testdata/genetic/optimize_result_2.json | 2123 ++++++++++ .../genetic/optimize_result_2_be.json | 2123 ++++++++++ .../genetic/optimize_result_2_full.json | 3711 +++++++++++++++++ .../{ => genetic}/test_example_report.pdf | Bin .../optimize_input_1.json} | 0 .../optimize_input_2.json} | 0 .../testdata/genetic0/optimize_result_1.json | 2218 ++++++++++ .../genetic0/optimize_result_1_be.json | 2218 ++++++++++ .../testdata/genetic0/optimize_result_2.json | 2123 ++++++++++ .../genetic0/optimize_result_2_be.json | 2123 ++++++++++ .../genetic0/optimize_result_2_full.json | 3711 +++++++++++++++++ .../testdata/genetic0/test_example_report.pdf | Bin 0 -> 97354 bytes tests/testdata/genetic0optimize_result_1.json | 772 ---- .../genetic0optimize_result_1_be.json | 772 ---- tests/testdata/genetic0optimize_result_2.json | 818 ---- .../genetic0optimize_result_2_be.json | 818 ---- .../genetic0optimize_result_2_full.json | 818 ---- tests/testdata/optimize_result_1.json | 772 ---- tests/testdata/optimize_result_1_be.json | 772 ---- tests/testdata/optimize_result_2.json | 818 ---- tests/testdata/optimize_result_2_be.json | 818 ---- tests/testdata/optimize_result_2_full.json | 818 ---- uv.lock | 525 +-- 61 files changed, 29450 insertions(+), 9083 deletions(-) rename src/akkudoktoreos/{utils/visualize.py => optimization/genetic/geneticvisualize.py} (80%) create mode 100644 src/akkudoktoreos/optimization/genetic0/genetic0visualize.py create mode 100644 tests/test_genetic0visualize.py rename tests/{test_visualize.py => test_geneticvisualize.py} (57%) rename tests/testdata/{ => genetic}/optimize_input_1.json (100%) rename tests/testdata/{ => genetic}/optimize_input_2.json (100%) create mode 100644 tests/testdata/genetic/optimize_result_1.json create mode 100644 tests/testdata/genetic/optimize_result_1_be.json create mode 100644 tests/testdata/genetic/optimize_result_2.json create mode 100644 tests/testdata/genetic/optimize_result_2_be.json create mode 100644 tests/testdata/genetic/optimize_result_2_full.json rename tests/testdata/{ => genetic}/test_example_report.pdf (100%) rename tests/testdata/{genetic0optimize_input_1.json => genetic0/optimize_input_1.json} (100%) rename tests/testdata/{genetic0optimize_input_2.json => genetic0/optimize_input_2.json} (100%) create mode 100644 tests/testdata/genetic0/optimize_result_1.json create mode 100644 tests/testdata/genetic0/optimize_result_1_be.json create mode 100644 tests/testdata/genetic0/optimize_result_2.json create mode 100644 tests/testdata/genetic0/optimize_result_2_be.json create mode 100644 tests/testdata/genetic0/optimize_result_2_full.json create mode 100644 tests/testdata/genetic0/test_example_report.pdf delete mode 100644 tests/testdata/genetic0optimize_result_1.json delete mode 100644 tests/testdata/genetic0optimize_result_1_be.json delete mode 100644 tests/testdata/genetic0optimize_result_2.json delete mode 100644 tests/testdata/genetic0optimize_result_2_be.json delete mode 100644 tests/testdata/genetic0optimize_result_2_full.json delete mode 100644 tests/testdata/optimize_result_1.json delete mode 100644 tests/testdata/optimize_result_1_be.json delete mode 100644 tests/testdata/optimize_result_2.json delete mode 100644 tests/testdata/optimize_result_2_be.json delete mode 100644 tests/testdata/optimize_result_2_full.json diff --git a/.env b/.env index 3cb040d6..a8690f40 100644 --- a/.env +++ b/.env @@ -11,7 +11,7 @@ DOCKER_COMPOSE_DATA_DIR=${HOME}/.local/share/net.akkudoktor.eos # ----------------------------------------------------------------------------- # Image / build # ----------------------------------------------------------------------------- -VERSION=0.3.0.dev2607301181843779 +VERSION=0.3.0.dev2608010961051894 PYTHON_VERSION=3.13.9 # ----------------------------------------------------------------------------- diff --git a/config.yaml b/config.yaml index 82662f4a..be065ddc 100644 --- a/config.yaml +++ b/config.yaml @@ -6,7 +6,7 @@ # the root directory (no add-on folder as usual). name: "Akkudoktor-EOS" -version: "0.3.0.dev2607301181843779" +version: "0.3.0.dev2608010961051894" slug: "eos" description: "Akkudoktor-EOS add-on" url: "https://github.com/Akkudoktor-EOS/EOS" diff --git a/docs/_generated/configexample.md b/docs/_generated/configexample.md index a9d63b5d..235d803c 100644 --- a/docs/_generated/configexample.md +++ b/docs/_generated/configexample.md @@ -162,6 +162,7 @@ } }, "logging": { + "api_level": "TRACE", "console_level": "TRACE", "file_level": "TRACE" }, diff --git a/docs/_generated/configlogging.md b/docs/_generated/configlogging.md index b5cf0df6..3f997e04 100644 --- a/docs/_generated/configlogging.md +++ b/docs/_generated/configlogging.md @@ -7,8 +7,9 @@ | Name | Environment Variable | Type | Read-Only | Default | Description | | ---- | -------------------- | ---- | --------- | ------- | ----------- | -| console_level | `EOS_LOGGING__CONSOLE_LEVEL` | `str | None` | `rw` | `None` | Logging level when logging to console. | -| file_level | `EOS_LOGGING__FILE_LEVEL` | `str | None` | `rw` | `None` | Logging level when logging to file. | +| api_level | `EOS_LOGGING__API_LEVEL` | `str | None` | `rw` | `None` | Logging level for API response. | +| console_level | `EOS_LOGGING__CONSOLE_LEVEL` | `str | None` | `rw` | `None` | Logging level for logging to console. | +| file_level | `EOS_LOGGING__FILE_LEVEL` | `str | None` | `rw` | `None` | Logging level for logging to file. | | file_path | | `pathlib.Path | None` | `ro` | `N/A` | Computed log file path based on data output path. | ::: @@ -21,6 +22,7 @@ ```json { "logging": { + "api_level": "TRACE", "console_level": "TRACE", "file_level": "TRACE" } @@ -36,6 +38,7 @@ ```json { "logging": { + "api_level": "TRACE", "console_level": "TRACE", "file_level": "TRACE", "file_path": "/home/user/.local/share/net.akkudoktor.eos/output/eos.log" diff --git a/docs/_generated/openapi.md b/docs/_generated/openapi.md index 08535b09..4a842abb 100644 --- a/docs/_generated/openapi.md +++ b/docs/_generated/openapi.md @@ -1,6 +1,6 @@ # Akkudoktor-EOS -**Version**: `v0.3.0.dev2607301181843779` +**Version**: `v0.3.0.dev2608010961051894` **Description**: This project provides a comprehensive solution for simulating and optimizing an energy system based on renewable energy sources. With a focus on photovoltaic (PV) systems, battery storage (batteries), load management (consumer requirements), heat pumps, electric vehicles, and consideration of electricity price data, this system enables forecasting and optimization of energy flow and costs over a specified period. @@ -143,7 +143,7 @@ Note: **Request Body**: - `application/json`: { - "$ref": "#/components/schemas/Genetic0OptimizationParameters" + "$ref": "#/components/schemas/Genetic0OptimizationParameters-Input" } **Responses**: @@ -948,6 +948,14 @@ Merge the measurement data given as dataframe into EOS measurements. Fastapi Measurement Keys Get + +```python +""" +Get a list of available measurement keys. +""" +``` + + **Responses**: - **200**: Successful Response @@ -997,7 +1005,73 @@ Fastapi Measurement Series Get ```python """ -Get the measurements of given key as series. +Get measurements for given key within given date range as series. + +Args: + key (str): Measurement key + start_datetime (Optional[str]): Starting datetime (inclusive). + Defaults to datetime of first measurement. + end_datetime (Optional[str]: Ending datetime (exclusive). + Defaults to datetime after latest measurement. + interval (Optional[str]): Time duration for each interval. + Defaults to 1 hour. + fill_method (str): Method to handle missing values during resampling. + + - 'linear': Linearly interpolate missing values (for numeric data only). + - 'time': Interpolate missing values (for numeric data only). + - 'ffill': Forward fill missing values. + - 'bfill': Backward fill missing values. + - Defaults to 'linear' for numeric values, otherwise 'ffill'. + + resample_method (str): + Method used to aggregate values within a resampling interval. + + - "first": Use the first value in each interval. + - "mean": Compute the arithmetic mean of all samples in each interval. + - "interval_mean": Compute the time-weighted mean assuming each + value remains valid until the next timestamp (piecewise-constant + signal). + + dropna: (bool): Whether to drop NAN/ None values before processing. + Defaults to True. + boundary (Literal["strict", "context"]): resampling boundary + "strict" → only values inside [start, end) + "context" → include one value before and after for proper resampling + align_to_interval (bool): When True, snap the resample origin to the nearest + UTC epoch-aligned boundary of ``interval`` before resampling. This ensures + that bucket timestamps always fall on wall-clock-round times regardless of + when ``start_datetime`` falls: + + - 15-minute interval → buckets on :00, :15, :30, :45 + - 1-hour interval → buckets on the hour + + When False (default), the origin is ``query_start`` (or ``"start_day"`` when + no start is given), preserving the existing behaviour where buckets are + aligned to the query window rather than the clock. + + Set to True when storing compacted records back to the database so that the + resulting timestamps are predictable and human-readable. Leave False for + forecast or reporting queries where alignment to the exact query window is + more important than clock-round boundaries. + processing (SeriesProcessing): + Processing mode for the returned series. + + - ``SeriesProcessing.RESAMPLED``: Return a processed series. + Measurements are first filtered by ``start_datetime``, + ``end_datetime``, and ``dropna``, then resampled according to + ``interval`` and ``resample_method``, and finally missing values + are filled using ``fill_method``. + - ``SeriesProcessing.RAW``: Return the original measurement series. + Measurements are filtered by ``start_datetime``, + ``end_datetime``, and ``dropna`` only. No resampling or filling is + performed, and ``interval``, ``fill_method``, + ``resample_method``, ``boundary``, and + ``align_to_interval`` are ignored. + + Defaults to ``SeriesProcessing.RAW``. + +Returns: + Series """ ``` @@ -1006,6 +1080,24 @@ Get the measurements of given key as series. - `key` (query, required): Measurement key. +- `start_datetime` (query, optional): Starting datetime (inclusive). + +- `end_datetime` (query, optional): Ending datetime (exclusive). + +- `interval` (query, optional): Time duration for each interval. Defaults to 1 hour. + +- `fill_method` (query, optional): Method to handle missing values during resampling. + +- `resample_method` (query, optional): Method used to aggregate values within a resampling interval. + +- `dropna` (query, optional): Drop NAN/ None values before processing. + +- `boundary` (query, optional): Resampling boundary mode. + +- `align_to_interval` (query, optional): Snap resample origin to the nearest UTC epoch-aligned boundary of interval. + +- `processing` (query, optional): Processing mode. 'raw' returns original measurement data without resampling or filling. + **Responses**: - **200**: Successful Response @@ -1118,7 +1210,7 @@ Args: value remains valid until the next timestamp (piecewise-constant signal). - dropna: (bool, optional): Whether to drop NAN/ None values before processing. + dropna: (bool): Whether to drop NAN/ None values before processing. Defaults to True. boundary (Literal["strict", "context"]): resampling boundary "strict" → only values inside [start, end) @@ -1288,7 +1380,7 @@ Args: value remains valid until the next timestamp (piecewise-constant signal). - dropna: (bool, optional): Whether to drop NAN/ None values before processing. + dropna: (bool): Whether to drop NAN/ None values before processing. Defaults to True. boundary (Literal["strict", "context"]): resampling boundary "strict" → only values inside [start, end) @@ -1423,6 +1515,65 @@ Args: Defaults to start datetime of latest prediction. end_datetime (Optional[str]: Ending datetime (exclusive). Defaults to end datetime of latest prediction. + interval (Optional[str]): Time duration for each interval. + Defaults to 1 hour. + fill_method (str): Method to handle missing values during resampling. + + - 'linear': Linearly interpolate missing values (for numeric data only). + - 'time': Interpolate missing values (for numeric data only). + - 'ffill': Forward fill missing values. + - 'bfill': Backward fill missing values. + - Defaults to 'linear' for numeric values, otherwise 'ffill'. + + resample_method (str): + Method used to aggregate values within a resampling interval. + + - "first": Use the first value in each interval. + - "mean": Compute the arithmetic mean of all samples in each interval. + - "interval_mean": Compute the time-weighted mean assuming each + value remains valid until the next timestamp (piecewise-constant + signal). + + dropna: (bool): Whether to drop NAN/ None values before processing. + Defaults to True. + boundary (Literal["strict", "context"]): resampling boundary + "strict" → only values inside [start, end) + "context" → include one value before and after for proper resampling + align_to_interval (bool): When True, snap the resample origin to the nearest + UTC epoch-aligned boundary of ``interval`` before resampling. This ensures + that bucket timestamps always fall on wall-clock-round times regardless of + when ``start_datetime`` falls: + + - 15-minute interval → buckets on :00, :15, :30, :45 + - 1-hour interval → buckets on the hour + + When False (default), the origin is ``query_start`` (or ``"start_day"`` when + no start is given), preserving the existing behaviour where buckets are + aligned to the query window rather than the clock. + + Set to True when storing compacted records back to the database so that the + resulting timestamps are predictable and human-readable. Leave False for + forecast or reporting queries where alignment to the exact query window is + more important than clock-round boundaries. + processing (SeriesProcessing): + Processing mode for the returned series. + + - ``SeriesProcessing.RESAMPLED``: Return a processed series. + Measurements are first filtered by ``start_datetime``, + ``end_datetime``, and ``dropna``, then resampled according to + ``interval`` and ``resample_method``, and finally missing values + are filled using ``fill_method``. + - ``SeriesProcessing.RAW``: Return the original measurement series. + Measurements are filtered by ``start_datetime``, + ``end_datetime``, and ``dropna`` only. No resampling or filling is + performed, and ``interval``, ``fill_method``, + ``resample_method``, ``boundary``, and + ``align_to_interval`` are ignored. + + Defaults to ``SeriesProcessing.RAW``. + +Returns: + Array """ ``` @@ -1435,6 +1586,20 @@ Args: - `end_datetime` (query, optional): Ending datetime (exclusive). +- `interval` (query, optional): Time duration for each interval. Defaults to 1 hour. + +- `fill_method` (query, optional): Method to handle missing values during resampling. + +- `resample_method` (query, optional): Method used to aggregate values within a resampling interval. + +- `dropna` (query, optional): Drop NAN/ None values before processing. + +- `boundary` (query, optional): Resampling boundary mode. + +- `align_to_interval` (query, optional): Snap resample origin to the nearest UTC epoch-aligned boundary of interval. + +- `processing` (query, optional): Processing mode. 'raw' returns original measurement data without resampling or filling. + **Responses**: - **200**: Successful Response diff --git a/openapi.json b/openapi.json index 47b26e8f..bacc9852 100644 --- a/openapi.json +++ b/openapi.json @@ -8,7 +8,7 @@ "name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html" }, - "version": "v0.3.0.dev2607301181843779" + "version": "v0.3.0.dev2608010961051894" }, "paths": { "/v1/admin/cache/clear": { @@ -953,6 +953,7 @@ "measurement" ], "summary": "Fastapi Measurement Keys Get", + "description": "Get a list of available measurement keys.", "operationId": "fastapi_measurement_keys_get_v1_measurement_keys_get", "responses": { "200": { @@ -978,7 +979,7 @@ "measurement" ], "summary": "Fastapi Measurement Series Get", - "description": "Get the measurements of given key as series.", + "description": "Get measurements for given key within given date range as series.\n\nArgs:\n key (str): Measurement key\n start_datetime (Optional[str]): Starting datetime (inclusive).\n Defaults to datetime of first measurement.\n end_datetime (Optional[str]: Ending datetime (exclusive).\n Defaults to datetime after latest measurement.\n interval (Optional[str]): Time duration for each interval.\n Defaults to 1 hour.\n fill_method (str): Method to handle missing values during resampling.\n\n - 'linear': Linearly interpolate missing values (for numeric data only).\n - 'time': Interpolate missing values (for numeric data only).\n - 'ffill': Forward fill missing values.\n - 'bfill': Backward fill missing values.\n - Defaults to 'linear' for numeric values, otherwise 'ffill'.\n\n resample_method (str):\n Method used to aggregate values within a resampling interval.\n\n - \"first\": Use the first value in each interval.\n - \"mean\": Compute the arithmetic mean of all samples in each interval.\n - \"interval_mean\": Compute the time-weighted mean assuming each\n value remains valid until the next timestamp (piecewise-constant\n signal).\n\n dropna: (bool): Whether to drop NAN/ None values before processing.\n Defaults to True.\n boundary (Literal[\"strict\", \"context\"]): resampling boundary\n \"strict\" \u2192 only values inside [start, end)\n \"context\" \u2192 include one value before and after for proper resampling\n align_to_interval (bool): When True, snap the resample origin to the nearest\n UTC epoch-aligned boundary of ``interval`` before resampling. This ensures\n that bucket timestamps always fall on wall-clock-round times regardless of\n when ``start_datetime`` falls:\n\n - 15-minute interval \u2192 buckets on :00, :15, :30, :45\n - 1-hour interval \u2192 buckets on the hour\n\n When False (default), the origin is ``query_start`` (or ``\"start_day\"`` when\n no start is given), preserving the existing behaviour where buckets are\n aligned to the query window rather than the clock.\n\n Set to True when storing compacted records back to the database so that the\n resulting timestamps are predictable and human-readable. Leave False for\n forecast or reporting queries where alignment to the exact query window is\n more important than clock-round boundaries.\n processing (SeriesProcessing):\n Processing mode for the returned series.\n\n - ``SeriesProcessing.RESAMPLED``: Return a processed series.\n Measurements are first filtered by ``start_datetime``,\n ``end_datetime``, and ``dropna``, then resampled according to\n ``interval`` and ``resample_method``, and finally missing values\n are filled using ``fill_method``.\n - ``SeriesProcessing.RAW``: Return the original measurement series.\n Measurements are filtered by ``start_datetime``,\n ``end_datetime``, and ``dropna`` only. No resampling or filling is\n performed, and ``interval``, ``fill_method``,\n ``resample_method``, ``boundary``, and\n ``align_to_interval`` are ignored.\n\n Defaults to ``SeriesProcessing.RAW``.\n\nReturns:\n Series", "operationId": "fastapi_measurement_series_get_v1_measurement_series_get", "parameters": [ { @@ -991,6 +992,152 @@ "title": "Key" }, "description": "Measurement key." + }, + { + "name": "start_datetime", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Starting datetime (inclusive).", + "title": "Start Datetime" + }, + "description": "Starting datetime (inclusive)." + }, + { + "name": "end_datetime", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Ending datetime (exclusive).", + "title": "End Datetime" + }, + "description": "Ending datetime (exclusive)." + }, + { + "name": "interval", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Time duration for each interval. Defaults to 1 hour.", + "title": "Interval" + }, + "description": "Time duration for each interval. Defaults to 1 hour." + }, + { + "name": "fill_method", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "enum": [ + "linear", + "time", + "ffill", + "bfill" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Method to handle missing values during resampling.", + "title": "Fill Method" + }, + "description": "Method to handle missing values during resampling." + }, + { + "name": "resample_method", + "in": "query", + "required": false, + "schema": { + "enum": [ + "first", + "mean", + "interval_mean" + ], + "type": "string", + "description": "Method used to aggregate values within a resampling interval.", + "default": "mean", + "title": "Resample Method" + }, + "description": "Method used to aggregate values within a resampling interval." + }, + { + "name": "dropna", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Drop NAN/ None values before processing.", + "default": true, + "title": "Dropna" + }, + "description": "Drop NAN/ None values before processing." + }, + { + "name": "boundary", + "in": "query", + "required": false, + "schema": { + "enum": [ + "strict", + "context" + ], + "type": "string", + "description": "Resampling boundary mode.", + "default": "context", + "title": "Boundary" + }, + "description": "Resampling boundary mode." + }, + { + "name": "align_to_interval", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Snap resample origin to the nearest UTC epoch-aligned boundary of interval.", + "default": false, + "title": "Align To Interval" + }, + "description": "Snap resample origin to the nearest UTC epoch-aligned boundary of interval." + }, + { + "name": "processing", + "in": "query", + "required": false, + "schema": { + "$ref": "#/components/schemas/SeriesProcessing", + "description": "Processing mode. 'raw' returns original measurement data without resampling or filling.", + "default": "raw" + }, + "description": "Processing mode. 'raw' returns original measurement data without resampling or filling." } ], "responses": { @@ -1389,7 +1536,7 @@ "prediction" ], "summary": "Fastapi Prediction Series Get", - "description": "Get prediction for given key within given date range as series.\n\nArgs:\n key (str): Prediction key\n start_datetime (Optional[str]): Starting datetime (inclusive).\n Defaults to start datetime of latest prediction.\n end_datetime (Optional[str]: Ending datetime (exclusive).\n Defaults to end datetime of latest prediction.", + "description": "Get prediction for given key within given date range as series.\n\nArgs:\n key (str): Prediction key\n start_datetime (Optional[str]): Starting datetime (inclusive).\n Defaults to start datetime of latest prediction.\n end_datetime (Optional[str]: Ending datetime (exclusive).\n Defaults to end datetime of latest prediction.\n interval (Optional[str]): Time duration for each interval.\n Defaults to 1 hour.\n fill_method (str): Method to handle missing values during resampling.\n\n - 'linear': Linearly interpolate missing values (for numeric data only).\n - 'time': Interpolate missing values (for numeric data only).\n - 'ffill': Forward fill missing values.\n - 'bfill': Backward fill missing values.\n - Defaults to 'linear' for numeric values, otherwise 'ffill'.\n\n resample_method (str):\n Method used to aggregate values within a resampling interval.\n\n - \"first\": Use the first value in each interval.\n - \"mean\": Compute the arithmetic mean of all samples in each interval.\n - \"interval_mean\": Compute the time-weighted mean assuming each\n value remains valid until the next timestamp (piecewise-constant\n signal).\n\n dropna: (bool): Whether to drop NAN/ None values before processing.\n Defaults to True.\n boundary (Literal[\"strict\", \"context\"]): resampling boundary\n \"strict\" \u2192 only values inside [start, end)\n \"context\" \u2192 include one value before and after for proper resampling\n align_to_interval (bool): When True, snap the resample origin to the nearest\n UTC epoch-aligned boundary of ``interval`` before resampling. This ensures\n that bucket timestamps always fall on wall-clock-round times regardless of\n when ``start_datetime`` falls:\n\n - 15-minute interval \u2192 buckets on :00, :15, :30, :45\n - 1-hour interval \u2192 buckets on the hour\n\n When False (default), the origin is ``query_start`` (or ``\"start_day\"`` when\n no start is given), preserving the existing behaviour where buckets are\n aligned to the query window rather than the clock.\n\n Set to True when storing compacted records back to the database so that the\n resulting timestamps are predictable and human-readable. Leave False for\n forecast or reporting queries where alignment to the exact query window is\n more important than clock-round boundaries.\n processing (SeriesProcessing):\n Processing mode for the returned series.\n\n - ``SeriesProcessing.RESAMPLED``: Return a processed series.\n Measurements are first filtered by ``start_datetime``,\n ``end_datetime``, and ``dropna``, then resampled according to\n ``interval`` and ``resample_method``, and finally missing values\n are filled using ``fill_method``.\n - ``SeriesProcessing.RAW``: Return the original measurement series.\n Measurements are filtered by ``start_datetime``,\n ``end_datetime``, and ``dropna`` only. No resampling or filling is\n performed, and ``interval``, ``fill_method``,\n ``resample_method``, ``boundary``, and\n ``align_to_interval`` are ignored.\n\n Defaults to ``SeriesProcessing.RAW``.\n\nReturns:\n Array", "operationId": "fastapi_prediction_series_get_v1_prediction_series_get", "parameters": [ { @@ -1438,6 +1585,116 @@ "title": "End Datetime" }, "description": "Ending datetime (exclusive)." + }, + { + "name": "interval", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Time duration for each interval. Defaults to 1 hour.", + "title": "Interval" + }, + "description": "Time duration for each interval. Defaults to 1 hour." + }, + { + "name": "fill_method", + "in": "query", + "required": false, + "schema": { + "anyOf": [ + { + "enum": [ + "linear", + "time", + "ffill", + "bfill" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "description": "Method to handle missing values during resampling.", + "title": "Fill Method" + }, + "description": "Method to handle missing values during resampling." + }, + { + "name": "resample_method", + "in": "query", + "required": false, + "schema": { + "enum": [ + "first", + "mean", + "interval_mean" + ], + "type": "string", + "description": "Method used to aggregate values within a resampling interval.", + "default": "mean", + "title": "Resample Method" + }, + "description": "Method used to aggregate values within a resampling interval." + }, + { + "name": "dropna", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Drop NAN/ None values before processing.", + "default": true, + "title": "Dropna" + }, + "description": "Drop NAN/ None values before processing." + }, + { + "name": "boundary", + "in": "query", + "required": false, + "schema": { + "enum": [ + "strict", + "context" + ], + "type": "string", + "description": "Resampling boundary mode.", + "default": "context", + "title": "Boundary" + }, + "description": "Resampling boundary mode." + }, + { + "name": "align_to_interval", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Snap resample origin to the nearest UTC epoch-aligned boundary of interval.", + "default": false, + "title": "Align To Interval" + }, + "description": "Snap resample origin to the nearest UTC epoch-aligned boundary of interval." + }, + { + "name": "processing", + "in": "query", + "required": false, + "schema": { + "$ref": "#/components/schemas/SeriesProcessing", + "description": "Processing mode. 'raw' returns original measurement data without resampling or filling.", + "default": "raw" + }, + "description": "Processing mode. 'raw' returns original measurement data without resampling or filling." } ], "responses": { @@ -1470,7 +1727,7 @@ "prediction" ], "summary": "Fastapi Prediction Dataframe Get", - "description": "Get prediction for given keys within given date range as dataframe.\n\nArgs:\n key (list[str]): Prediction keys\n start_datetime (Optional[str]): Starting datetime (inclusive).\n Defaults to start datetime of latest prediction.\n end_datetime (Optional[str]: Ending datetime (exclusive).\n Defaults to end datetime of latest prediction.\n interval (Optional[str]): Time duration for each interval.\n Defaults to 1 hour.\n fill_method (str): Method to handle missing values during resampling.\n\n - 'linear': Linearly interpolate missing values (for numeric data only).\n - 'time': Interpolate missing values (for numeric data only).\n - 'ffill': Forward fill missing values.\n - 'bfill': Backward fill missing values.\n - Defaults to 'linear' for numeric values, otherwise 'ffill'.\n\n resample_method (str):\n Method used to aggregate values within a resampling interval.\n\n - \"first\": Use the first value in each interval.\n - \"mean\": Compute the arithmetic mean of all samples in each interval.\n - \"interval_mean\": Compute the time-weighted mean assuming each\n value remains valid until the next timestamp (piecewise-constant\n signal).\n\n dropna: (bool, optional): Whether to drop NAN/ None values before processing.\n Defaults to True.\n boundary (Literal[\"strict\", \"context\"]): resampling boundary\n \"strict\" \u2192 only values inside [start, end)\n \"context\" \u2192 include one value before and after for proper resampling\n align_to_interval (bool): When True, snap the resample origin to the nearest\n UTC epoch-aligned boundary of ``interval`` before resampling. This ensures\n that bucket timestamps always fall on wall-clock-round times regardless of\n when ``start_datetime`` falls:\n\n - 15-minute interval \u2192 buckets on :00, :15, :30, :45\n - 1-hour interval \u2192 buckets on the hour\n\n When False (default), the origin is ``query_start`` (or ``\"start_day\"`` when\n no start is given), preserving the existing behaviour where buckets are\n aligned to the query window rather than the clock.\n\n Set to True when storing compacted records back to the database so that the\n resulting timestamps are predictable and human-readable. Leave False for\n forecast or reporting queries where alignment to the exact query window is\n more important than clock-round boundaries.", + "description": "Get prediction for given keys within given date range as dataframe.\n\nArgs:\n key (list[str]): Prediction keys\n start_datetime (Optional[str]): Starting datetime (inclusive).\n Defaults to start datetime of latest prediction.\n end_datetime (Optional[str]: Ending datetime (exclusive).\n Defaults to end datetime of latest prediction.\n interval (Optional[str]): Time duration for each interval.\n Defaults to 1 hour.\n fill_method (str): Method to handle missing values during resampling.\n\n - 'linear': Linearly interpolate missing values (for numeric data only).\n - 'time': Interpolate missing values (for numeric data only).\n - 'ffill': Forward fill missing values.\n - 'bfill': Backward fill missing values.\n - Defaults to 'linear' for numeric values, otherwise 'ffill'.\n\n resample_method (str):\n Method used to aggregate values within a resampling interval.\n\n - \"first\": Use the first value in each interval.\n - \"mean\": Compute the arithmetic mean of all samples in each interval.\n - \"interval_mean\": Compute the time-weighted mean assuming each\n value remains valid until the next timestamp (piecewise-constant\n signal).\n\n dropna: (bool): Whether to drop NAN/ None values before processing.\n Defaults to True.\n boundary (Literal[\"strict\", \"context\"]): resampling boundary\n \"strict\" \u2192 only values inside [start, end)\n \"context\" \u2192 include one value before and after for proper resampling\n align_to_interval (bool): When True, snap the resample origin to the nearest\n UTC epoch-aligned boundary of ``interval`` before resampling. This ensures\n that bucket timestamps always fall on wall-clock-round times regardless of\n when ``start_datetime`` falls:\n\n - 15-minute interval \u2192 buckets on :00, :15, :30, :45\n - 1-hour interval \u2192 buckets on the hour\n\n When False (default), the origin is ``query_start`` (or ``\"start_day\"`` when\n no start is given), preserving the existing behaviour where buckets are\n aligned to the query window rather than the clock.\n\n Set to True when storing compacted records back to the database so that the\n resulting timestamps are predictable and human-readable. Leave False for\n forecast or reporting queries where alignment to the exact query window is\n more important than clock-round boundaries.", "operationId": "fastapi_prediction_dataframe_get_v1_prediction_dataframe_get", "parameters": [ { @@ -1587,15 +1844,9 @@ "in": "query", "required": false, "schema": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], + "type": "boolean", "description": "Drop NAN/ None values before processing.", + "default": true, "title": "Dropna" }, "description": "Drop NAN/ None values before processing." @@ -1659,7 +1910,7 @@ "prediction" ], "summary": "Fastapi Prediction List Get", - "description": "Get prediction for given key within given date range as value list.\n\nArgs:\n key (str): Prediction key\n start_datetime (Optional[str]): Starting datetime (inclusive).\n Defaults to start datetime of latest prediction.\n end_datetime (Optional[str]: Ending datetime (exclusive).\n Defaults to end datetime of latest prediction.\n interval (Optional[str]): Time duration for each interval.\n Defaults to 1 hour.\n fill_method (str): Method to handle missing values during resampling.\n\n - 'linear': Linearly interpolate missing values (for numeric data only).\n - 'time': Interpolate missing values (for numeric data only).\n - 'ffill': Forward fill missing values.\n - 'bfill': Backward fill missing values.\n - Defaults to 'linear' for numeric values, otherwise 'ffill'.\n\n resample_method (str):\n Method used to aggregate values within a resampling interval.\n\n - \"first\": Use the first value in each interval.\n - \"mean\": Compute the arithmetic mean of all samples in each interval.\n - \"interval_mean\": Compute the time-weighted mean assuming each\n value remains valid until the next timestamp (piecewise-constant\n signal).\n\n dropna: (bool, optional): Whether to drop NAN/ None values before processing.\n Defaults to True.\n boundary (Literal[\"strict\", \"context\"]): resampling boundary\n \"strict\" \u2192 only values inside [start, end)\n \"context\" \u2192 include one value before and after for proper resampling\n align_to_interval (bool): When True, snap the resample origin to the nearest\n UTC epoch-aligned boundary of ``interval`` before resampling. This ensures\n that bucket timestamps always fall on wall-clock-round times regardless of\n when ``start_datetime`` falls:\n\n - 15-minute interval \u2192 buckets on :00, :15, :30, :45\n - 1-hour interval \u2192 buckets on the hour\n\n When False (default), the origin is ``query_start`` (or ``\"start_day\"`` when\n no start is given), preserving the existing behaviour where buckets are\n aligned to the query window rather than the clock.\n\n Set to True when storing compacted records back to the database so that the\n resulting timestamps are predictable and human-readable. Leave False for\n forecast or reporting queries where alignment to the exact query window is\n more important than clock-round boundaries.", + "description": "Get prediction for given key within given date range as value list.\n\nArgs:\n key (str): Prediction key\n start_datetime (Optional[str]): Starting datetime (inclusive).\n Defaults to start datetime of latest prediction.\n end_datetime (Optional[str]: Ending datetime (exclusive).\n Defaults to end datetime of latest prediction.\n interval (Optional[str]): Time duration for each interval.\n Defaults to 1 hour.\n fill_method (str): Method to handle missing values during resampling.\n\n - 'linear': Linearly interpolate missing values (for numeric data only).\n - 'time': Interpolate missing values (for numeric data only).\n - 'ffill': Forward fill missing values.\n - 'bfill': Backward fill missing values.\n - Defaults to 'linear' for numeric values, otherwise 'ffill'.\n\n resample_method (str):\n Method used to aggregate values within a resampling interval.\n\n - \"first\": Use the first value in each interval.\n - \"mean\": Compute the arithmetic mean of all samples in each interval.\n - \"interval_mean\": Compute the time-weighted mean assuming each\n value remains valid until the next timestamp (piecewise-constant\n signal).\n\n dropna: (bool): Whether to drop NAN/ None values before processing.\n Defaults to True.\n boundary (Literal[\"strict\", \"context\"]): resampling boundary\n \"strict\" \u2192 only values inside [start, end)\n \"context\" \u2192 include one value before and after for proper resampling\n align_to_interval (bool): When True, snap the resample origin to the nearest\n UTC epoch-aligned boundary of ``interval`` before resampling. This ensures\n that bucket timestamps always fall on wall-clock-round times regardless of\n when ``start_datetime`` falls:\n\n - 15-minute interval \u2192 buckets on :00, :15, :30, :45\n - 1-hour interval \u2192 buckets on the hour\n\n When False (default), the origin is ``query_start`` (or ``\"start_day\"`` when\n no start is given), preserving the existing behaviour where buckets are\n aligned to the query window rather than the clock.\n\n Set to True when storing compacted records back to the database so that the\n resulting timestamps are predictable and human-readable. Leave False for\n forecast or reporting queries where alignment to the exact query window is\n more important than clock-round boundaries.", "operationId": "fastapi_prediction_list_get_v1_prediction_list_get", "parameters": [ { @@ -1773,15 +2024,9 @@ "in": "query", "required": false, "schema": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], + "type": "boolean", "description": "Drop NAN/ None values before processing.", + "default": true, "title": "Dropna" }, "description": "Drop NAN/ None values before processing." @@ -2436,7 +2681,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Genetic0OptimizationParameters" + "$ref": "#/components/schemas/Genetic0OptimizationParameters-Input" } } } @@ -2447,7 +2692,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Genetic0Solution" + "$ref": "#/components/schemas/Genetic0SolutionLegacy" } } } @@ -2476,10 +2721,8 @@ "200": { "description": "Successful Response", "content": { - "application/pdf": { - "schema": { - "type": "string" - } + "application/json": { + "schema": {} } } } @@ -4045,6 +4288,136 @@ "title": "ElecPriceTibberCommonSettings", "description": "Common settings for the Tibber electricity price provider." }, + "ElectricVehicleParameters": { + "properties": { + "device_id": { + "type": "string", + "title": "Device Id", + "description": "ID of electric vehicle", + "examples": [ + "ev1" + ] + }, + "hours": { + "anyOf": [ + { + "type": "integer", + "exclusiveMinimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Hours", + "description": "Number of prediction hours. Defaults to global config prediction hours.", + "examples": [ + null + ] + }, + "capacity_wh": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Capacity Wh", + "description": "An integer representing the capacity of the battery in watt-hours.", + "examples": [ + 8000 + ] + }, + "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": 1.0 + }, + "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 current state of charge (SOC) of the battery in percentage.", + "default": 0, + "examples": [ + 42 + ] + }, + "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, + "examples": [ + 10 + ] + }, + "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 + }, + "charge_rates": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Charge Rates", + "description": "Charge rates as factor of maximum charging power [0.00 ... 1.00]. None denotes all charge rates are available.", + "examples": [ + [ + 0.0, + 0.25, + 0.5, + 0.75, + 1.0 + ], + null + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "device_id", + "capacity_wh" + ], + "title": "ElectricVehicleParameters", + "description": "Battery Electric Vehicle Device Simulation Configuration." + }, "ElectricVehicleResult": { "properties": { "device_id": { @@ -5436,7 +5809,7 @@ "title": "Genetic0ElectricVehicleResult", "description": "Result class containing information related to the electric vehicle's charging and discharging behavior." }, - "Genetic0EnergyManagementParameters": { + "Genetic0EnergyManagementParameters-Input": { "properties": { "pv_forecast_wh": { "items": { @@ -5494,7 +5867,124 @@ "title": "Genetic0EnergyManagementParameters", "description": "Encapsulates energy-related forecasts and costs used in GENETIC optimization." }, - "Genetic0HomeApplianceParameters": { + "Genetic0EnergyManagementParameters-Output": { + "properties": { + "pv_forecast_wh": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Pv Forecast Wh", + "description": "An array of floats representing the forecasted photovoltaic output in watts for different time intervals." + }, + "electricity_price_per_wh": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Electricity Price Per Wh", + "description": "An array of floats representing the electricity price per watt-hour for different time intervals." + }, + "feed_in_tariff_per_wh": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "number" + } + ], + "title": "Feed In Tariff Per Wh", + "description": "A float or array of floats representing the feed-in compensation per watt-hour." + }, + "price_per_wh_battery": { + "type": "number", + "title": "Price Per Wh Battery", + "description": "A float representing the cost of battery energy per watt-hour." + }, + "total_load": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Total Load", + "description": "An array of floats representing the total load (consumption) in watts for different time intervals." + }, + "pv_prognose_wh": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Pv Prognose Wh", + "description": "Deprecated: Use pv_forecast_wh instead.", + "deprecated": true, + "readOnly": true + }, + "strompreis_euro_pro_wh": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Strompreis Euro Pro Wh", + "description": "Deprecated: Use electricity_price_per_wh instead.", + "deprecated": true, + "readOnly": true + }, + "einspeiseverguetung_euro_pro_wh": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "number" + } + ], + "title": "Einspeiseverguetung Euro Pro Wh", + "description": "Deprecated: Use feed_in_tariff_per_wh instead.", + "deprecated": true, + "readOnly": true + }, + "preis_euro_pro_wh_akku": { + "type": "number", + "title": "Preis Euro Pro Wh Akku", + "description": "Deprecated: Use price_per_wh_battery instead.", + "deprecated": true, + "readOnly": true + }, + "gesamtlast": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Gesamtlast", + "description": "Deprecated: Use total_load instead.", + "deprecated": true, + "readOnly": true + } + }, + "type": "object", + "required": [ + "pv_forecast_wh", + "electricity_price_per_wh", + "feed_in_tariff_per_wh", + "price_per_wh_battery", + "total_load", + "pv_prognose_wh", + "strompreis_euro_pro_wh", + "einspeiseverguetung_euro_pro_wh", + "preis_euro_pro_wh_akku", + "gesamtlast" + ], + "title": "Genetic0EnergyManagementParameters", + "description": "Encapsulates energy-related forecasts and costs used in GENETIC optimization." + }, + "Genetic0HomeApplianceParameters-Input": { "properties": { "device_id": { "type": "string", @@ -5568,6 +6058,80 @@ "title": "Genetic0HomeApplianceParameters", "description": "Home Appliance Device Simulation Configuration." }, + "Genetic0HomeApplianceParameters-Output": { + "properties": { + "device_id": { + "type": "string", + "title": "Device Id", + "description": "ID of home appliance", + "examples": [ + "dishwasher" + ] + }, + "hours": { + "anyOf": [ + { + "type": "integer", + "exclusiveMinimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Hours", + "description": "Number of prediction hours. Defaults to global config prediction hours.", + "examples": [ + null + ] + }, + "consumption_wh": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Consumption Wh", + "description": "An integer representing the energy consumption of a household device in watt-hours.", + "examples": [ + 2000 + ] + }, + "duration_h": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Duration H", + "description": "An integer representing the usage duration of a household device in hours.", + "examples": [ + 3 + ] + }, + "time_windows": { + "anyOf": [ + { + "$ref": "#/components/schemas/TimeWindowSequence-Output" + }, + { + "type": "null" + } + ], + "description": "List of allowed time windows. Defaults to optimization general time window.", + "examples": [ + [ + { + "duration": "3 hours", + "start_time": "10:00" + } + ] + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "device_id", + "consumption_wh", + "duration_h" + ], + "title": "Genetic0HomeApplianceParameters", + "description": "Home Appliance Device Simulation Configuration." + }, "Genetic0InverterParameters": { "properties": { "device_id": { @@ -5671,10 +6235,10 @@ "title": "Genetic0InverterParameters", "description": "Inverter Device Simulation Configuration." }, - "Genetic0OptimizationParameters": { + "Genetic0OptimizationParameters-Input": { "properties": { "ems": { - "$ref": "#/components/schemas/Genetic0EnergyManagementParameters" + "$ref": "#/components/schemas/Genetic0EnergyManagementParameters-Input" }, "pv_battery": { "anyOf": [ @@ -5711,7 +6275,7 @@ "dishwasher": { "anyOf": [ { - "$ref": "#/components/schemas/Genetic0HomeApplianceParameters" + "$ref": "#/components/schemas/Genetic0HomeApplianceParameters-Input" }, { "type": "null" @@ -5766,6 +6330,129 @@ "title": "Genetic0OptimizationParameters", "description": "Main parameter class for running the genetic energy optimization.\n\nCollects all model and configuration parameters necessary to run the\noptimization process, such as forecasts, pricing, battery and appliance models." }, + "Genetic0OptimizationParameters-Output": { + "properties": { + "ems": { + "$ref": "#/components/schemas/Genetic0EnergyManagementParameters-Output" + }, + "pv_battery": { + "anyOf": [ + { + "$ref": "#/components/schemas/Genetic0SolarPanelBatteryParameters" + }, + { + "type": "null" + } + ], + "description": "PV battery parameters." + }, + "inverter": { + "anyOf": [ + { + "$ref": "#/components/schemas/Genetic0InverterParameters" + }, + { + "type": "null" + } + ] + }, + "ev": { + "anyOf": [ + { + "$ref": "#/components/schemas/Genetic0ElectricVehicleParameters" + }, + { + "type": "null" + } + ], + "description": "Electric vehicle parameters." + }, + "dishwasher": { + "anyOf": [ + { + "$ref": "#/components/schemas/Genetic0HomeApplianceParameters-Output" + }, + { + "type": "null" + } + ] + }, + "temperature_forecast": { + "anyOf": [ + { + "items": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Temperature Forecast", + "description": "An array of floats representing the temperature forecast in degrees Celsius for different time intervals." + }, + "start_solution": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Start Solution", + "description": "Can be `null` or contain a previous solution (if available)." + }, + "pv_akku": { + "anyOf": [ + { + "$ref": "#/components/schemas/Genetic0SolarPanelBatteryParameters" + }, + { + "type": "null" + } + ], + "description": "Deprecated: Use pv_battery instead.", + "deprecated": true, + "readOnly": true + }, + "eauto": { + "anyOf": [ + { + "$ref": "#/components/schemas/Genetic0ElectricVehicleParameters" + }, + { + "type": "null" + } + ], + "description": "Deprecated: Use ev instead.", + "deprecated": true, + "readOnly": true + } + }, + "type": "object", + "required": [ + "ems", + "pv_battery", + "inverter", + "ev", + "pv_akku", + "eauto" + ], + "title": "Genetic0OptimizationParameters", + "description": "Main parameter class for running the genetic energy optimization.\n\nCollects all model and configuration parameters necessary to run the\noptimization process, such as forecasts, pricing, battery and appliance models." + }, "Genetic0SimulationResult": { "properties": { "load_wh_per_hour": { @@ -6176,6 +6863,212 @@ "description": "PV battery device simulation configuration." }, "Genetic0Solution": { + "properties": { + "parameters": { + "$ref": "#/components/schemas/Genetic0OptimizationParameters-Output", + "description": "Optimization parameters used to generate solution." + }, + "ac_charge": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Ac Charge", + "description": "Array with AC charging values as relative power (0.0-1.0), other values set to 0." + }, + "dc_charge": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Dc Charge", + "description": "Array with DC charging values as relative power (0-1), other values set to 0." + }, + "discharge_allowed": { + "items": { + "type": "integer" + }, + "type": "array", + "title": "Discharge Allowed", + "description": "Array with discharge values (1 for discharge, 0 otherwise)." + }, + "ev_charge_hours_float": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Ev Charge Hours Float", + "description": "Array with EV charging values as relative power (0.0-1.0), or `null` if no EV is optimized." + }, + "result": { + "$ref": "#/components/schemas/Genetic0SimulationResult" + }, + "ev_obj": { + "anyOf": [ + { + "$ref": "#/components/schemas/Genetic0ElectricVehicleResult" + }, + { + "type": "null" + } + ], + "description": "Electric vehicle state after optimization." + }, + "start_hour": { + "type": "integer", + "title": "Start Hour", + "description": "Start hour.", + "default": 0 + }, + "start_solution": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Start Solution", + "description": "An array of binary values (0 or 1) representing a possible starting solution for the simulation." + }, + "washingstart": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Washingstart", + "description": "Can be `null` or contain an object representing the start of washing (if applicable)." + }, + "extra_data": { + "anyOf": [ + { + "additionalProperties": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "items": { + "type": "number" + }, + "type": "array" + } + ] + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Extra Data", + "description": "Dictionary of balance: TBD, losses: TBD, constraints: TBD." + }, + "fitness_history": { + "anyOf": [ + { + "additionalProperties": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "items": { + "type": "number" + }, + "type": "array" + } + ] + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Fitness History", + "description": "Dictionary of gen: Generation numbers (X-axis), avg: Average fitness for each generation (Y-axis), max: Maximum fitness for each generation (Y-axis), min: Minimum fitness for each generation (Y-axis)." + }, + "fixed_seed": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Fixed Seed", + "description": "Fixed seed." + }, + "eautocharge_hours_float": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Eautocharge Hours Float", + "description": "Deprecated: Use ev_charge_hours_float instead.", + "deprecated": true, + "readOnly": true + }, + "eauto_obj": { + "anyOf": [ + { + "$ref": "#/components/schemas/Genetic0ElectricVehicleResult" + }, + { + "type": "null" + } + ], + "description": "Deprecated: Use ev_obj instead.", + "deprecated": true, + "readOnly": true + } + }, + "type": "object", + "required": [ + "parameters", + "ac_charge", + "dc_charge", + "discharge_allowed", + "ev_charge_hours_float", + "result", + "ev_obj", + "eautocharge_hours_float", + "eauto_obj" + ], + "title": "Genetic0Solution", + "description": "**Note**: The first value of \"load_wh_per_hour\", \"grid_feed_in_wh_per_hour\", and \"grid_consumption_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." + }, + "Genetic0SolutionLegacy": { "properties": { "ac_charge": { "items": { @@ -6299,8 +7192,8 @@ "eautocharge_hours_float", "eauto_obj" ], - "title": "Genetic0Solution", - "description": "**Note**: The first value of \"load_wh_per_hour\", \"grid_feed_in_wh_per_hour\", and \"grid_consumption_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." + "title": "Genetic0SolutionLegacy", + "description": "GENETIC0 solution legacy dataset." }, "GeneticCommonSettings-Input": { "properties": { @@ -6517,6 +7410,246 @@ "title": "GeneticCommonSettings", "description": "GENETIC Optimization Algorithm Configuration." }, + "GeneticEnergyManagementParameters": { + "properties": { + "pv_forecast_wh": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Pv Forecast Wh", + "description": "An array of floats representing the forecasted photovoltaic output in watts for different time intervals." + }, + "electricity_price_per_wh": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Electricity Price Per Wh", + "description": "An array of floats representing the electricity price per watt-hour for different time intervals." + }, + "feed_in_tariff_per_wh": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "number" + } + ], + "title": "Feed In Tariff Per Wh", + "description": "A float or array of floats representing the feed-in compensation per watt-hour." + }, + "price_per_wh_battery": { + "type": "number", + "title": "Price Per Wh Battery", + "description": "A float representing the cost of battery energy per watt-hour." + }, + "total_load": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Total Load", + "description": "An array of floats representing the total load (consumption) in watts for different time intervals." + }, + "pv_prognose_wh": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Pv Prognose Wh", + "description": "Deprecated: Use pv_forecast_wh instead.", + "deprecated": true, + "readOnly": true + }, + "strompreis_euro_pro_wh": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Strompreis Euro Pro Wh", + "description": "Deprecated: Use electricity_price_per_wh instead.", + "deprecated": true, + "readOnly": true + }, + "einspeiseverguetung_euro_pro_wh": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "number" + } + ], + "title": "Einspeiseverguetung Euro Pro Wh", + "description": "Deprecated: Use feed_in_tariff_per_wh instead.", + "deprecated": true, + "readOnly": true + }, + "preis_euro_pro_wh_akku": { + "type": "number", + "title": "Preis Euro Pro Wh Akku", + "description": "Deprecated: Use price_per_wh_battery instead.", + "deprecated": true, + "readOnly": true + }, + "gesamtlast": { + "items": { + "type": "number" + }, + "type": "array", + "title": "Gesamtlast", + "description": "Deprecated: Use total_load instead.", + "deprecated": true, + "readOnly": true + } + }, + "type": "object", + "required": [ + "pv_forecast_wh", + "electricity_price_per_wh", + "feed_in_tariff_per_wh", + "price_per_wh_battery", + "total_load", + "pv_prognose_wh", + "strompreis_euro_pro_wh", + "einspeiseverguetung_euro_pro_wh", + "preis_euro_pro_wh_akku", + "gesamtlast" + ], + "title": "GeneticEnergyManagementParameters", + "description": "Encapsulates energy-related forecasts and costs used in GENETIC optimization." + }, + "GeneticOptimizationParameters": { + "properties": { + "ems": { + "$ref": "#/components/schemas/GeneticEnergyManagementParameters" + }, + "pv_battery": { + "anyOf": [ + { + "$ref": "#/components/schemas/SolarPanelBatteryParameters" + }, + { + "type": "null" + } + ], + "description": "PV battery parameters." + }, + "inverter": { + "anyOf": [ + { + "$ref": "#/components/schemas/InverterParameters" + }, + { + "type": "null" + } + ] + }, + "ev": { + "anyOf": [ + { + "$ref": "#/components/schemas/ElectricVehicleParameters" + }, + { + "type": "null" + } + ], + "description": "Electric vehicle parameters." + }, + "dishwasher": { + "anyOf": [ + { + "$ref": "#/components/schemas/HomeApplianceParameters" + }, + { + "type": "null" + } + ] + }, + "temperature_forecast": { + "anyOf": [ + { + "items": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Temperature Forecast", + "description": "An array of floats representing the temperature forecast in degrees Celsius for different time intervals." + }, + "start_solution": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Start Solution", + "description": "Can be `null` or contain a previous solution (if available)." + }, + "pv_akku": { + "anyOf": [ + { + "$ref": "#/components/schemas/SolarPanelBatteryParameters" + }, + { + "type": "null" + } + ], + "description": "Deprecated: Use pv_battery instead.", + "deprecated": true, + "readOnly": true + }, + "eauto": { + "anyOf": [ + { + "$ref": "#/components/schemas/ElectricVehicleParameters" + }, + { + "type": "null" + } + ], + "description": "Deprecated: Use ev instead.", + "deprecated": true, + "readOnly": true + } + }, + "type": "object", + "required": [ + "ems", + "pv_battery", + "inverter", + "ev", + "pv_akku", + "eauto" + ], + "title": "GeneticOptimizationParameters", + "description": "Main parameter class for running the genetic energy optimization.\n\nCollects all model and configuration parameters necessary to run the\noptimization process, such as forecasts, pricing, battery and appliance models." + }, "GeneticSimulationResult": { "properties": { "load_wh_per_hour": { @@ -6798,6 +7931,10 @@ }, "GeneticSolution": { "properties": { + "parameters": { + "$ref": "#/components/schemas/GeneticOptimizationParameters", + "description": "Optimization parameters used to generate solution." + }, "ac_charge": { "items": { "type": "number" @@ -6851,6 +7988,12 @@ ], "description": "Electric vehicle state after optimization." }, + "start_hour": { + "type": "integer", + "title": "Start Hour", + "description": "Start hour.", + "default": 0 + }, "start_solution": { "anyOf": [ { @@ -6878,6 +8021,74 @@ "title": "Washingstart", "description": "Can be `null` or contain an object representing the start of washing (if applicable)." }, + "extra_data": { + "anyOf": [ + { + "additionalProperties": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "items": { + "type": "number" + }, + "type": "array" + } + ] + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Extra Data", + "description": "Dictionary of balance: TBD, losses: TBD, constraints: TBD." + }, + "fitness_history": { + "anyOf": [ + { + "additionalProperties": { + "anyOf": [ + { + "items": { + "type": "integer" + }, + "type": "array" + }, + { + "items": { + "type": "number" + }, + "type": "array" + } + ] + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Fitness History", + "description": "Dictionary of gen: Generation numbers (X-axis), avg: Average fitness for each generation (Y-axis), max: Maximum fitness for each generation (Y-axis), min: Minimum fitness for each generation (Y-axis)." + }, + "fixed_seed": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Fixed Seed", + "description": "Fixed seed." + }, "eautocharge_hours_float": { "anyOf": [ { @@ -6911,6 +8122,7 @@ }, "type": "object", "required": [ + "parameters", "ac_charge", "dc_charge", "discharge_allowed", @@ -7106,6 +8318,80 @@ "title": "HomeApplianceCommonSettings", "description": "Home Appliance devices base settings." }, + "HomeApplianceParameters": { + "properties": { + "device_id": { + "type": "string", + "title": "Device Id", + "description": "ID of home appliance", + "examples": [ + "dishwasher" + ] + }, + "hours": { + "anyOf": [ + { + "type": "integer", + "exclusiveMinimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Hours", + "description": "Number of prediction hours. Defaults to global config prediction hours.", + "examples": [ + null + ] + }, + "consumption_wh": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Consumption Wh", + "description": "An integer representing the energy consumption of a household device in watt-hours.", + "examples": [ + 2000 + ] + }, + "duration_h": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Duration H", + "description": "An integer representing the usage duration of a household device in hours.", + "examples": [ + 3 + ] + }, + "time_windows": { + "anyOf": [ + { + "$ref": "#/components/schemas/TimeWindowSequence-Output" + }, + { + "type": "null" + } + ], + "description": "List of allowed time windows. Defaults to optimization general time window.", + "examples": [ + [ + { + "duration": "3 hours", + "start_time": "10:00" + } + ] + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "device_id", + "consumption_wh", + "duration_h" + ], + "title": "HomeApplianceParameters", + "description": "Home Appliance Device Simulation Configuration." + }, "HomeAssistantAdapterCommonSettings-Input": { "properties": { "config_entity_ids": { @@ -7697,6 +8983,109 @@ "title": "InverterCommonSettings", "description": "Inverter devices base settings." }, + "InverterParameters": { + "properties": { + "device_id": { + "type": "string", + "title": "Device Id", + "description": "ID of inverter", + "examples": [ + "inverter1" + ] + }, + "hours": { + "anyOf": [ + { + "type": "integer", + "exclusiveMinimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Hours", + "description": "Number of prediction hours. Defaults to global config prediction hours.", + "examples": [ + null + ] + }, + "max_power_wh": { + "type": "number", + "exclusiveMinimum": 0.0, + "title": "Max Power Wh", + "examples": [ + 10000 + ] + }, + "battery_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Battery Id", + "description": "ID of battery", + "examples": [ + null, + "battery1" + ] + }, + "ac_to_dc_efficiency": { + "type": "number", + "maximum": 1.0, + "minimum": 0.0, + "title": "Ac To Dc Efficiency", + "description": "Efficiency of AC to DC conversion (for AC/grid charging of battery). Set to 0 to disable AC charging via inverter. Default 1.0 for backward compatibility (no additional inverter loss).", + "default": 1.0, + "examples": [ + 0.95, + 1.0, + 0.0 + ] + }, + "dc_to_ac_efficiency": { + "type": "number", + "maximum": 1.0, + "exclusiveMinimum": 0.0, + "title": "Dc To Ac Efficiency", + "description": "Efficiency of DC to AC conversion (for battery discharging to AC load/grid). Default 1.0 for backward compatibility (no additional inverter loss).", + "default": 1.0, + "examples": [ + 0.95, + 1.0 + ] + }, + "max_ac_charge_power_w": { + "anyOf": [ + { + "type": "number", + "minimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Max Ac Charge Power W", + "description": "Maximum AC charging power in watts. None means no additional limit (battery's own max_charge_power_w applies). Set to 0 to disable AC charging.", + "examples": [ + null, + 0, + 5000 + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "device_id", + "max_power_wh" + ], + "title": "InverterParameters", + "description": "Inverter Device Simulation Configuration." + }, "LoadAkkudoktorCommonSettings": { "properties": { "loadakkudoktor_year_energy_kwh": { @@ -7868,6 +9257,26 @@ }, "LoggingCommonSettings-Input": { "properties": { + "api_level": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Api Level", + "description": "Logging level for API response.", + "examples": [ + "TRACE", + "DEBUG", + "INFO", + "WARNING", + "ERROR", + "CRITICAL" + ] + }, "console_level": { "anyOf": [ { @@ -7878,7 +9287,7 @@ } ], "title": "Console Level", - "description": "Logging level when logging to console.", + "description": "Logging level for logging to console.", "examples": [ "TRACE", "DEBUG", @@ -7898,7 +9307,7 @@ } ], "title": "File Level", - "description": "Logging level when logging to file.", + "description": "Logging level for logging to file.", "examples": [ "TRACE", "DEBUG", @@ -7915,6 +9324,26 @@ }, "LoggingCommonSettings-Output": { "properties": { + "api_level": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Api Level", + "description": "Logging level for API response.", + "examples": [ + "TRACE", + "DEBUG", + "INFO", + "WARNING", + "ERROR", + "CRITICAL" + ] + }, "console_level": { "anyOf": [ { @@ -7925,7 +9354,7 @@ } ], "title": "Console Level", - "description": "Logging level when logging to console.", + "description": "Logging level for logging to console.", "examples": [ "TRACE", "DEBUG", @@ -7945,7 +9374,7 @@ } ], "title": "File Level", - "description": "Logging level when logging to file.", + "description": "Logging level for logging to file.", "examples": [ "TRACE", "DEBUG", @@ -9868,6 +11297,14 @@ "title": "PydanticDateTimeSeries", "description": "Pydantic model for validating pandas Series with datetime index in JSON format.\n\nThis model handles Series data serialized with orient='index', where the keys are\ndatetime strings and values are the series values. Provides validation and\nconversion between JSON and pandas Series with datetime index.\n\nAttributes:\n data (Dict[str, Any]): Dictionary mapping datetime strings to values.\n dtype (str): The data type of the series values.\n tz (str | None): Timezone name if the datetime index is timezone-aware." }, + "SeriesProcessing": { + "type": "string", + "enum": [ + "raw", + "resampled" + ], + "title": "SeriesProcessing" + }, "ServerCommonSettings": { "properties": { "host": { @@ -10175,6 +11612,136 @@ "title": "SettingsEOS", "description": "Settings for all EOS.\n\nOnly used to update the configuration with specific settings." }, + "SolarPanelBatteryParameters": { + "properties": { + "device_id": { + "type": "string", + "title": "Device Id", + "description": "ID of battery", + "examples": [ + "battery1" + ] + }, + "hours": { + "anyOf": [ + { + "type": "integer", + "exclusiveMinimum": 0.0 + }, + { + "type": "null" + } + ], + "title": "Hours", + "description": "Number of prediction hours. Defaults to global config prediction hours.", + "examples": [ + null + ] + }, + "capacity_wh": { + "type": "integer", + "exclusiveMinimum": 0.0, + "title": "Capacity Wh", + "description": "An integer representing the capacity of the battery in watt-hours.", + "examples": [ + 8000 + ] + }, + "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, + "examples": [ + 42 + ] + }, + "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, + "examples": [ + 10 + ] + }, + "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 + }, + "charge_rates": { + "anyOf": [ + { + "items": { + "type": "number" + }, + "type": "array" + }, + { + "type": "null" + } + ], + "title": "Charge Rates", + "description": "Charge rates as factor of maximum charging power [0.00 ... 1.00]. None denotes all charge rates are available.", + "examples": [ + [ + 0.0, + 0.25, + 0.5, + 0.75, + 1.0 + ], + null + ] + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "device_id", + "capacity_wh" + ], + "title": "SolarPanelBatteryParameters", + "description": "PV battery device simulation configuration." + }, "TimeWindow-Input": { "properties": { "start_time": { diff --git a/pyproject.toml b/pyproject.toml index 26a3deaa..3bd47821 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,6 +84,7 @@ dev = [ "pytest-cov==7.1.0", "pytest-xprocess==1.0.2", "coverage==7.15.2", + "pypdf==6.14.2", ] [project.urls] diff --git a/src/akkudoktoreos/core/dataabc.py b/src/akkudoktoreos/core/dataabc.py index cedb0cce..b6ad50ee 100644 --- a/src/akkudoktoreos/core/dataabc.py +++ b/src/akkudoktoreos/core/dataabc.py @@ -1009,7 +1009,7 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): key: str, start_datetime: Optional[DateTime] = None, end_datetime: Optional[DateTime] = None, - dropna: Optional[bool] = None, + dropna: bool = True, ) -> Dict[DateTime, Any]: """Extract a dictionary indexed by the date_time field of the DataRecords. @@ -1020,7 +1020,7 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): key (str): The field name in the DataRecord from which to extract values. start_datetime (datetime, optional): The start date to filter records (inclusive). end_datetime (datetime, optional): The end date to filter records (exclusive). - dropna: (bool, optional): Whether to drop NAN/ None values before processing. Defaults to True. + dropna: (bool): Whether to drop NAN/ None values before processing. Defaults to True. Returns: Dict[datetime, Any]: A dictionary with the date_time of each record as the key @@ -1038,8 +1038,6 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): end_timestamp = DatabaseTimestamp.from_datetime(end_datetime) if end_datetime else None # Create a dictionary to hold date_time and corresponding values - if dropna is None: - dropna = True filtered_data = {} async for record in self.db_iterate_records(start_timestamp, end_timestamp): if ( @@ -1090,7 +1088,7 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): key: str, start_datetime: Optional[DateTime] = None, end_datetime: Optional[DateTime] = None, - dropna: Optional[bool] = None, + dropna: bool = True, ) -> Tuple[list[DateTime], list[Optional[float]]]: """Extracts two lists from data records within an optional date range. @@ -1102,7 +1100,7 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): key (str): The key of the attribute in DataRecord to extract. start_datetime (datetime, optional): The start date for filtering the records (inclusive). end_datetime (datetime, optional): The end date for filtering the records (exclusive). - dropna: (bool, optional): Whether to drop NAN/ None values before processing. Defaults to True. + dropna: (bool): Whether to drop NAN/ None values before processing. Defaults to True. Returns: tuple: A tuple containing a list of datetime values and a list of extracted values. @@ -1119,8 +1117,6 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): end_timestamp = DatabaseTimestamp.from_datetime(end_datetime) if end_datetime else None # Create two lists to hold date_time and corresponding values - if dropna is None: - dropna = True filtered_records = [] async for record in self.db_iterate_records(start_timestamp, end_timestamp): if ( @@ -1155,48 +1151,51 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): async with self._record_lock: await self._key_from_lists(key, dates, values) - async def key_to_series( + async def key_to_raw_series( self, key: str, start_datetime: Optional[DateTime] = None, end_datetime: Optional[DateTime] = None, - dropna: Optional[bool] = None, + dropna: bool = True, ) -> pd.Series: - """Extract a series indexed by the date_time field from data records within an optional date range. + """Return the raw time series stored for a key. + + Retrieves the timestamps and values exactly as stored by the underlying data provider. + The returned index therefore represents the original timestamps of the stored records. Args: - key (str): The field name in the DataRecord from which to extract values. - start_datetime (datetime, optional): The start date for filtering the records (inclusive). - end_datetime (datetime, optional): The end date for filtering the records (exclusive). - dropna: (bool, optional): Whether to drop NAN/ None values before processing. Defaults to True. + key: Field name to extract from the stored records. + start_datetime: Inclusive lower bound for timestamps. + end_datetime: Exclusive upper bound for timestamps. + dropna: Whether to discard records whose value is None or NaN. Returns: - pd.Series: A Pandas Series with the index as the date_time of each record - and the values extracted from the specified key. + A pandas Series indexed by the original timestamps. Raises: - KeyError: If the specified key is not found in any of the DataRecords. + KeyError: If the key does not exist. """ + # Normalize datetime arguments + start_datetime = to_datetime(start_datetime, to_maxtime=False) if start_datetime else None + end_datetime = to_datetime(end_datetime, to_maxtime=False) if end_datetime else None + dates, values = await self.key_to_lists( key=key, start_datetime=start_datetime, end_datetime=end_datetime, dropna=dropna ) - series = pd.Series(data=values, index=pd.DatetimeIndex(dates), name=key) + + # Construct series + index = pd.to_datetime(dates, utc=True) + series = pd.Series(values, index=index, name=key) + + # Check for correct series + if not isinstance(series.index, pd.DatetimeIndex): + raise TypeError(f"Expected pd.DatetimeIndex, but got {type(series.index).__name__}.") + if series.index.tz is None: + raise TypeError("Expected timezone-aware DatetimeIndex.") + return series - async def key_from_series(self, key: str, series: pd.Series) -> None: - """Update the DataSequence from a Pandas Series. - - The series index should represent the date_time of each DataRecord, and the series values - should represent the corresponding data values for the specified key. - - Args: - series (pd.Series): A Pandas Series containing data to update the DataSequence. - key (str): The field name in the DataRecord that corresponds to the values in the Series. - """ - async with self._record_lock: - await self._key_from_series(key, series) - - async def key_to_array( + async def key_to_series( self, key: str, start_datetime: Optional[DateTime] = None, @@ -1204,11 +1203,25 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): interval: Optional[Duration] = None, fill_method: Optional[FillMethod] = None, resample_method: ResampleMethod = "mean", - dropna: Optional[bool] = True, + dropna: bool = True, boundary: BoundaryMode = "context", align_to_interval: bool = False, - ) -> NDArray[Shape["*"], Any]: - """Extract an array indexed by fixed time intervals from data records within an optional date range. + ) -> pd.Series: + """Return a pandas Series for a data key. + + The method performs all preprocessing required for resampling: + + - validates the input arguments, + - extends the query window when context resampling is requested, + - loads the raw database values, + - inserts boundary values when necessary, + - determines the resampling origin, + - performs aggregation, + - fills missing values, + - truncates the result back to the requested interval. + + The DatetimeIndex represents the actual bucket timestamps and should therefore be + considered the authoritative timestamp information for the resampled data. Args: key (str): The field name in the DataRecord from which to extract values. @@ -1232,7 +1245,7 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): value remains valid until the next timestamp (piecewise-constant signal). - dropna: (bool, optional): Whether to drop NAN/ None values before processing. + dropna: (bool): Whether to drop NAN/ None values before processing. Defaults to True. boundary (Literal["strict", "context"]): resampling boundary "strict" → only values inside [start, end) @@ -1255,14 +1268,12 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): more important than clock-round boundaries. Returns: - np.ndarray: A NumPy Array of the values at the chosen frequency extracted from the - specified key. + A resampled pandas Series whose index contains the correct timestamps + for every returned value. Raises: KeyError: If the specified key is not found in any of the DataRecords. """ - self._validate_key(key) - # Validate fill method if fill_method not in ("ffill", "bfill", "linear", "time", "none", None): raise ValueError(f"Unsupported fill method: {fill_method}") @@ -1307,36 +1318,40 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): else: query_end = DatabaseTimestamp.to_datetime(query_end_timestamp).add(seconds=1) - # Load raw lists (already sorted & filtered) - dates, values = await self.key_to_lists( + # Load raw series + series = await self.key_to_raw_series( key=key, start_datetime=query_start, end_datetime=query_end, dropna=dropna ) - values_len = len(values) - # Bring lists into shape - if values_len < 1: - # No values, assume at least one value set to None - if query_start is not None: - dates.append(query_start - interval) - else: - dates.append(to_datetime(to_maxtime=False)) - values.append(None) + # Ensure we have at least one value + if series.empty: + dummy_time = ( + query_start - interval if query_start is not None else to_datetime(to_maxtime=False) + ) + series = pd.Series( + [None], + index=pd.DatetimeIndex([dummy_time], tz="UTC"), + name=key, + ) if query_start is not None: - start_index = 0 - while start_index < values_len: - if compare_datetimes(dates[start_index], query_start).ge: - break - start_index += 1 + idx = series.index + + # Number of samples before query_start + start_index = idx.searchsorted(pd.Timestamp(query_start), side="left") + if start_index == 0: - # No value before start - # Add dummy value - dates.insert(0, query_start - interval) - values.insert(0, values[0]) + # No value before query_start -> prepend dummy + prepend = pd.Series( + [series.iloc[0]], + index=pd.DatetimeIndex([query_start - interval], tz="UTC"), + name=key, + ) + series = pd.concat([prepend, series]) + elif start_index > 1: - # Truncate all values before latest value before query_start - dates = dates[start_index - 1 :] - values = values[start_index - 1 :] + # Keep only the last sample before query_start + series = series.iloc[start_index - 1 :] # Determine resample origin if align_to_interval: @@ -1360,23 +1375,17 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): resample_origin = "start_day" if query_end is not None: - if compare_datetimes(dates[-1], query_end).lt: - # Add dummy value at query_end - dates.append(query_end) - values.append(values[-1]) - - # Construct series - index = pd.to_datetime(dates, utc=True) - series = pd.Series(values, index=index, name=key) - if series.index.inferred_type != "datetime64": - raise TypeError( - f"Expected DatetimeIndex, but got {type(series.index)} " - f"infered to {series.index.inferred_type}: {series}" - ) + if compare_datetimes(to_datetime(series.index[-1]), query_end).lt: + append = pd.Series( + [series.iloc[-1]], + index=pd.DatetimeIndex([query_end], tz="UTC"), + name=key, + ) + series = pd.concat([series, append]) # Check for numeric values - numeric = pd.to_numeric(series.dropna(), errors="coerce") - is_numeric = numeric.notna().all() + numeric_series = pd.to_numeric(series, errors="coerce") # ensures float64, not object dtype + is_numeric = numeric_series.dropna().notna().all() # Determine default fill method depending on dtype if fill_method is None: @@ -1388,10 +1397,7 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): # Perform the resampling if is_numeric: # Step 1: aggregate — collapses sub-interval data (e.g. 4x 15min → 1h mean). - # Produces NaN for buckets where no data existed at all. - numeric_series = pd.to_numeric( - series, errors="coerce" - ) # ← ensures float64, not object dtype + # numeric_series has NaN for buckets where no data existed at all. if resample_method == "first": resampled = numeric_series.resample( @@ -1433,22 +1439,114 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): elif fill_method == "bfill": resampled = resampled.bfill() + if start_datetime is not None and len(resampled) > 0: + resampled = resampled.truncate(before=start_datetime) + if end_datetime is not None and len(resampled) > 0: + resampled = resampled.truncate(after=end_datetime.subtract(seconds=1)) + logger.debug( - "Resampled for '{}' with length {}: {}...{}", + "Resampled series for '{}' with length {}: {}...{}", key, len(resampled), resampled[:10], resampled[-10:], ) - # Convert the resampled series to a NumPy array - if start_datetime is not None and len(resampled) > 0: - resampled = resampled.truncate(before=start_datetime) - if end_datetime is not None and len(resampled) > 0: - resampled = resampled.truncate(after=end_datetime.subtract(seconds=1)) - array = resampled.values + return resampled - # Convert NaN to None if there are actually NaNs + async def key_from_series(self, key: str, series: pd.Series) -> None: + """Update the DataSequence from a Pandas Series. + + The series index should represent the date_time of each DataRecord, and the series values + should represent the corresponding data values for the specified key. + + Args: + series (pd.Series): A Pandas Series containing data to update the DataSequence. + key (str): The field name in the DataRecord that corresponds to the values in the Series. + """ + async with self._record_lock: + await self._key_from_series(key, series) + + async def key_to_array( + self, + key: str, + start_datetime: Optional[DateTime] = None, + end_datetime: Optional[DateTime] = None, + interval: Optional[Duration] = None, + fill_method: Optional[FillMethod] = None, + resample_method: ResampleMethod = "mean", + dropna: bool = True, + boundary: BoundaryMode = "context", + align_to_interval: bool = False, + ) -> NDArray[Shape["*"], Any]: + """Extract an array indexed by fixed time intervals from data records within an optional date range. + + Args: + key (str): The field name in the DataRecord from which to extract values. + start_datetime (datetime, optional): The start date for filtering the records (inclusive). + end_datetime (datetime, optional): The end date for filtering the records (exclusive). + interval (duration, optional): The fixed time interval. Defaults to 1 hour. + fill_method (str): Method to handle missing values during resampling. + + - 'linear': Linearly interpolate missing values (for numeric data only). + - 'time': Interpolate missing values (for numeric data only). + - 'ffill': Forward fill missing values. + - 'bfill': Backward fill missing values. + - Defaults to 'linear' for numeric values, otherwise 'ffill'. + + resample_method (str): + Method used to aggregate values within a resampling interval. + + - "first": Use the first value in each interval. + - "mean": Compute the arithmetic mean of all samples in each interval. + - "interval_mean": Compute the time-weighted mean assuming each + value remains valid until the next timestamp (piecewise-constant + signal). + + dropna: (bool): Whether to drop NAN/ None values before processing. + Defaults to True. + boundary (Literal["strict", "context"]): resampling boundary + "strict" → only values inside [start, end) + "context" → include one value before and after for proper resampling + align_to_interval (bool): When True, snap the resample origin to the nearest + UTC epoch-aligned boundary of ``interval`` before resampling. This ensures + that bucket timestamps always fall on wall-clock-round times regardless of + when ``start_datetime`` falls: + + - 15-minute interval → buckets on :00, :15, :30, :45 + - 1-hour interval → buckets on the hour + + When False (default), the origin is ``query_start`` (or ``"start_day"`` when + no start is given), preserving the existing behaviour where buckets are + aligned to the query window rather than the clock. + + Set to True when storing compacted records back to the database so that the + resulting timestamps are predictable and human-readable. Leave False for + forecast or reporting queries where alignment to the exact query window is + more important than clock-round boundaries. + + Returns: + np.ndarray: A NumPy Array of the values at the chosen frequency extracted from the + specified key. + + Raises: + KeyError: If the specified key is not found in any of the DataRecords. + """ + series = await self.key_to_series( + key=key, + start_datetime=start_datetime, + end_datetime=end_datetime, + interval=interval, + fill_method=fill_method, + resample_method=resample_method, + dropna=dropna, + boundary=boundary, + align_to_interval=align_to_interval, + ) + + array = series.to_numpy() + + # Convert NaN to None if necessary if ( isinstance(array, np.ndarray) and np.issubdtype(array.dtype.type, np.floating) @@ -1458,7 +1556,11 @@ class DataSequence(DataABC, DatabaseRecordProtocolMixin[DataRecord]): array[pd.isna(array)] = None logger.debug( - "Array for '{}' with length {}: {}...{}", key, len(array), array[:10], array[-10:] + "Array for '{}' with length {}: {}...{}", + key, + len(array), + array[:10], + array[-10:], ) return array @@ -2380,12 +2482,12 @@ class DataContainer(SingletonMixin, DataABC): # Log as warning and continue so the remaining providers still run. logger.warning(error) - async def key_to_series( + async def key_to_raw_series( self, key: str, start_datetime: Optional[DateTime] = None, end_datetime: Optional[DateTime] = None, - dropna: Optional[bool] = None, + dropna: bool = True, ) -> pd.Series: """Extract a series indexed by the date_time field from data records within an optional date range. @@ -2395,7 +2497,93 @@ class DataContainer(SingletonMixin, DataABC): key (str): The field name in the DataRecord from which to extract values. start_datetime (datetime, optional): The start date for filtering the records (inclusive). end_datetime (datetime, optional): The end date for filtering the records (exclusive). - dropna: (bool, optional): Whether to drop NAN/ None values before processing. Defaults to True. + dropna: (bool): Whether to drop NAN/ None values before processing. + Defaults to True. + + Returns: + pd.Series: A Pandas Series with the index as the date_time of each record + and the values extracted from the specified key. + + Raises: + KeyError: If the specified key is not found in any of the DataRecords. + """ + series = None + for provider in self.enabled_providers: + try: + series = await provider.key_to_raw_series( + key=key, + start_datetime=start_datetime, + end_datetime=end_datetime, + dropna=dropna, + ) + break + except KeyError: + continue + + if series is None: + raise KeyError(f"No data found for key '{key}'.") + + return series + + async def key_to_series( + self, + key: str, + start_datetime: Optional[DateTime] = None, + end_datetime: Optional[DateTime] = None, + interval: Optional[Duration] = None, + fill_method: Optional[FillMethod] = None, + resample_method: ResampleMethod = "mean", + dropna: bool = True, + boundary: BoundaryMode = "context", + align_to_interval: bool = False, + ) -> pd.Series: + """Extract a series indexed by the date_time field from data records within an optional date range. + + Iterates through providers to find and return the first available series for the specified key. + + Args: + key (str): The field name in the DataRecord from which to extract values. + start_datetime (datetime, optional): The start date for filtering the records (inclusive). + end_datetime (datetime, optional): The end date for filtering the records (exclusive). + interval (duration, optional): The fixed time interval. Defaults to 1 hour. + fill_method (str): Method to handle missing values during resampling. + + - 'linear': Linearly interpolate missing values (for numeric data only). + - 'time': Interpolate missing values (for numeric data only). + - 'ffill': Forward fill missing values. + - 'bfill': Backward fill missing values. + - Defaults to 'linear' for numeric values, otherwise 'ffill'. + + resample_method (str): + Method used to aggregate values within a resampling interval. + + - "first": Use the first value in each interval. + - "mean": Compute the arithmetic mean of all samples in each interval. + - "interval_mean": Compute the time-weighted mean assuming each + value remains valid until the next timestamp (piecewise-constant + signal). + + dropna: (bool): Whether to drop NAN/ None values before processing. + Defaults to True. + boundary (Literal["strict", "context"]): + "strict" → only values inside [start, end) + "context" → include one value before and after for proper resampling + align_to_interval (bool): When True, snap the resample origin to the nearest + UTC epoch-aligned boundary of ``interval`` before resampling. This ensures + that bucket timestamps always fall on wall-clock-round times regardless of + when ``start_datetime`` falls: + + - 15-minute interval → buckets on :00, :15, :30, :45 + - 1-hour interval → buckets on the hour + + When False (default), the origin is ``query_start`` (or ``"start_day"`` when + no start is given), preserving the existing behaviour where buckets are + aligned to the query window rather than the clock. + + Set to True when storing compacted records back to the database so that the + resulting timestamps are predictable and human-readable. Leave False for + forecast or reporting queries where alignment to the exact query window is + more important than clock-round boundaries. Returns: pd.Series: A Pandas Series with the index as the date_time of each record @@ -2408,10 +2596,15 @@ class DataContainer(SingletonMixin, DataABC): for provider in self.enabled_providers: try: series = await provider.key_to_series( - key, + key=key, start_datetime=start_datetime, end_datetime=end_datetime, + interval=interval, + fill_method=fill_method, + resample_method=resample_method, dropna=dropna, + boundary=boundary, + align_to_interval=align_to_interval, ) break except KeyError: @@ -2430,7 +2623,7 @@ class DataContainer(SingletonMixin, DataABC): interval: Optional[Duration] = None, fill_method: Optional[FillMethod] = None, resample_method: ResampleMethod = "mean", - dropna: Optional[bool] = True, + dropna: bool = True, boundary: BoundaryMode = "context", align_to_interval: bool = False, ) -> NDArray[Shape["*"], Any]: @@ -2460,7 +2653,7 @@ class DataContainer(SingletonMixin, DataABC): value remains valid until the next timestamp (piecewise-constant signal). - dropna: (bool, optional): Whether to drop NAN/ None values before processing. + dropna: (bool): Whether to drop NAN/ None values before processing. Defaults to True. boundary (Literal["strict", "context"]): "strict" → only values inside [start, end) @@ -2495,7 +2688,7 @@ class DataContainer(SingletonMixin, DataABC): for provider in self.enabled_providers: try: array = await provider.key_to_array( - key, + key=key, start_datetime=start_datetime, end_datetime=end_datetime, interval=interval, @@ -2522,13 +2715,13 @@ class DataContainer(SingletonMixin, DataABC): interval: Optional[Duration] = None, fill_method: Optional[FillMethod] = None, resample_method: ResampleMethod = "mean", - dropna: Optional[bool] = True, + dropna: bool = True, boundary: BoundaryMode = "context", align_to_interval: bool = False, ) -> pd.DataFrame: """Retrieve a dataframe indexed by fixed time intervals for specified keys from the data in each DataProvider. - Generates a pandas DataFrame using the NumPy arrays for each specified key, ensuring a common time index. + Generates a pandas DataFrame of data for each specified key, ensuring a common time index. Args: keys (list[str]): The field names in the DataRecords from which to extract values. @@ -2552,7 +2745,7 @@ class DataContainer(SingletonMixin, DataABC): value remains valid until the next timestamp (piecewise-constant signal). - dropna: (bool, optional): Whether to drop NAN/ None values before processing. + dropna: (bool): Whether to drop NAN/ None values before processing. Defaults to True. boundary (Literal["strict", "context"]): "strict" → only values inside [start, end) @@ -2575,17 +2768,23 @@ class DataContainer(SingletonMixin, DataABC): more important than clock-round boundaries. Returns: - pd.DataFrame: A DataFrame where each column represents a key's array with a common time index. + pd.DataFrame: A DataFrame whose columns contain the (resampled) values + for each requested key and whose index is the common (resampled) DatetimeIndex. Raises: KeyError: If no valid data is found for any of the requested keys. - ValueError: If any retrieved array has a different time index than the first one. + ValueError: If any retrieved series has a different DatetimeIndex than the first one. """ + if not keys: + raise ValueError("keys must not be empty.") + # Ensure datetime objects are normalized start_datetime = to_datetime(start_datetime, to_maxtime=False) if start_datetime else None end_datetime = to_datetime(end_datetime, to_maxtime=False) if end_datetime else None + if interval is None: interval = to_duration("1 hour") + if start_datetime is None: # Take earliest datetime of all providers that are enabled for provider in self.enabled_providers: @@ -2601,7 +2800,6 @@ class DataContainer(SingletonMixin, DataABC): if end_datetime is None: end_datetime = max_dt elif max_dt and compare_datetimes(max_dt, end_datetime).gt: - min_dt = await provider.min_datetime() end_datetime = max_dt if end_datetime: end_datetime = end_datetime.add(seconds=1) @@ -2611,17 +2809,13 @@ class DataContainer(SingletonMixin, DataABC): raise ValueError( f"Can not determine datetime range. Got '{start_datetime}'..'{end_datetime}'." ) - reference_index = pd.date_range( - start=start_datetime, - end=end_datetime, - freq=interval, - inclusive="left", - ) - data = {} + reference_index: Optional[pd.DatetimeIndex] = None + data: dict[str, pd.Series] = {} + for key in keys: try: - array = await self.key_to_array( + series = await self.key_to_series( key=key, start_datetime=start_datetime, end_datetime=end_datetime, @@ -2632,20 +2826,24 @@ class DataContainer(SingletonMixin, DataABC): boundary=boundary, align_to_interval=align_to_interval, ) - - if len(array) != len(reference_index): - raise ValueError( - f"Array length mismatch for key '{key}' (expected {len(reference_index)}, got {len(array)})" + if not isinstance(series.index, pd.DatetimeIndex): # make mypy happy + raise TypeError( + f"Expected DatetimeIndex for key '{key}', got {type(series.index).__name__}" ) - data[key] = array + if reference_index is None: + reference_index = series.index + elif not series.index.equals(reference_index): + raise ValueError(f"Time index mismatch for key '{key}'.") + + data[key] = series except KeyError as e: - raise KeyError(f"Failed to retrieve data for key '{key}': {e}") + raise KeyError(f"Failed to retrieve data for key '{key}': {e}") from e if not data: raise KeyError(f"No valid data found for the requested keys {keys}.") - return pd.DataFrame(data, index=reference_index) + return pd.DataFrame(data) # Pandas aligns the series in data by their index async def key_delete_by_datetime( self, diff --git a/src/akkudoktoreos/core/databaseabc.py b/src/akkudoktoreos/core/databaseabc.py index 3bbcedcc..4397bee3 100644 --- a/src/akkudoktoreos/core/databaseabc.py +++ b/src/akkudoktoreos/core/databaseabc.py @@ -549,7 +549,7 @@ class DatabaseRecordProtocolMixin( interval: Optional[Duration] = None, fill_method: Optional[FillMethod] = None, resample_method: ResampleMethod = "mean", - dropna: Optional[bool] = True, + dropna: bool = True, boundary: BoundaryMode = "context", align_to_interval: bool = False, ) -> NDArray[Shape["*"], Any]: ... diff --git a/src/akkudoktoreos/core/logsettings.py b/src/akkudoktoreos/core/logsettings.py index 2a08c82b..6a0e6e7a 100644 --- a/src/akkudoktoreos/core/logsettings.py +++ b/src/akkudoktoreos/core/logsettings.py @@ -15,10 +15,18 @@ from akkudoktoreos.core.logabc import LOGGING_LEVELS class LoggingCommonSettings(SettingsBaseModel): """Logging Configuration.""" + api_level: Optional[str] = Field( + default=None, + json_schema_extra={ + "description": "Logging level for API response.", + "examples": LOGGING_LEVELS, + }, + ) + console_level: Optional[str] = Field( default=None, json_schema_extra={ - "description": "Logging level when logging to console.", + "description": "Logging level for logging to console.", "examples": LOGGING_LEVELS, }, ) @@ -26,7 +34,7 @@ class LoggingCommonSettings(SettingsBaseModel): file_level: Optional[str] = Field( default=None, json_schema_extra={ - "description": "Logging level when logging to file.", + "description": "Logging level for logging to file.", "examples": LOGGING_LEVELS, }, ) diff --git a/src/akkudoktoreos/optimization/genetic/genetic.py b/src/akkudoktoreos/optimization/genetic/genetic.py index 6aeee17b..af5b5745 100644 --- a/src/akkudoktoreos/optimization/genetic/genetic.py +++ b/src/akkudoktoreos/optimization/genetic/genetic.py @@ -1221,39 +1221,20 @@ class GeneticOptimization(OptimizationBase): else: discharge = discharge.tolist() - # Visualize the results in PDF - try: - from akkudoktoreos.utils.visualize import prepare_visualize - - visualize = { - "ac_charge": ac_charge_hours, - "dc_charge": dc_charge_hours, - "discharge_allowed": discharge, - "ev_charge_hours_float": ev_charge_hours_float, - "result": GeneticSimulationResult(**simulation_result).model_dump(), - "ev_obj": self.simulation.ev.to_dict() if self.simulation.ev else None, - "start_solution": start_solution, - "washingstart": washingstart_int, - "extra_data": extra_data, - "fitness_history": self.fitness_history, - "fixed_seed": self.fix_seed, - } - - prepare_visualize(parameters, visualize, start_hour=start_hour) - - except Exception as ex: - error_msg = f"Visualization failed: {ex}" - logger.error(error_msg) - return GeneticSolution( **{ + "parameters": parameters, "ac_charge": ac_charge_hours, "dc_charge": dc_charge_hours, "discharge_allowed": discharge, "ev_charge_hours_float": ev_charge_hours_float, "result": GeneticSimulationResult(**simulation_result), "ev_obj": self.simulation.ev, + "start_hour": start_hour, "start_solution": start_solution, "washingstart": washingstart_int, + "extra_data": extra_data, + "fitness_history": self.fitness_history, + "fixed_seed": self.fix_seed, } ) diff --git a/src/akkudoktoreos/optimization/genetic/geneticsolution.py b/src/akkudoktoreos/optimization/genetic/geneticsolution.py index c705efd1..896874af 100644 --- a/src/akkudoktoreos/optimization/genetic/geneticsolution.py +++ b/src/akkudoktoreos/optimization/genetic/geneticsolution.py @@ -1,6 +1,6 @@ """Genetic algorithm optimisation solution.""" -from typing import Any, Optional +from typing import Any, Optional, Union import numpy as np import pandas as pd @@ -27,6 +27,9 @@ from akkudoktoreos.devices.devicesabc import ( ) from akkudoktoreos.devices.genetic.battery import Battery from akkudoktoreos.optimization.genetic.geneticdevices import GeneticParametersBaseModel +from akkudoktoreos.optimization.genetic.geneticparams import ( + GeneticOptimizationParameters, +) from akkudoktoreos.optimization.optimization import OptimizationSolution from akkudoktoreos.utils.datetimeutil import to_datetime, to_duration from akkudoktoreos.utils.utils import NumpyEncoder @@ -247,6 +250,9 @@ class GeneticSolution(ConfigMixin, GeneticParametersBaseModel): model_config = ConfigDict(populate_by_name=True, extra="ignore") + parameters: GeneticOptimizationParameters = Field( + json_schema_extra={"description": "Optimization parameters used to generate solution."} + ) ac_charge: list[float] = Field( json_schema_extra={ "description": "Array with AC charging values as relative power (0.0-1.0), other values set to 0." @@ -273,6 +279,10 @@ class GeneticSolution(ConfigMixin, GeneticParametersBaseModel): validation_alias=AliasChoices("ev_obj", "eauto_obj"), json_schema_extra={"description": "Electric vehicle state after optimization."}, ) + start_hour: int = Field( + default=0, + json_schema_extra={"description": "Start hour."}, + ) start_solution: Optional[list[float]] = Field( default=None, json_schema_extra={ @@ -285,6 +295,28 @@ class GeneticSolution(ConfigMixin, GeneticParametersBaseModel): "description": "Can be `null` or contain an object representing the start of washing (if applicable)." }, ) + extra_data: Optional[dict[str, Union[list[int], list[float]]]] = Field( + default=None, + json_schema_extra={ + "description": ("Dictionary of balance: TBD, losses: TBD, constraints: TBD.") + }, + ) + fitness_history: Optional[dict[str, Union[list[int], list[float]]]] = Field( + default=None, + json_schema_extra={ + "description": ( + "Dictionary of " + "gen: Generation numbers (X-axis), " + "avg: Average fitness for each generation (Y-axis), " + "max: Maximum fitness for each generation (Y-axis), " + "min: Minimum fitness for each generation (Y-axis)." + ) + }, + ) + fixed_seed: Optional[int] = Field( + default=None, + json_schema_extra={"description": "Fixed seed."}, + ) # Computed fields for backward compatibility (deprecated German names) @computed_field(json_schema_extra={"deprecated": True}) diff --git a/src/akkudoktoreos/utils/visualize.py b/src/akkudoktoreos/optimization/genetic/geneticvisualize.py similarity index 80% rename from src/akkudoktoreos/utils/visualize.py rename to src/akkudoktoreos/optimization/genetic/geneticvisualize.py index 61cecb71..3e6d4d85 100644 --- a/src/akkudoktoreos/utils/visualize.py +++ b/src/akkudoktoreos/optimization/genetic/geneticvisualize.py @@ -1,7 +1,7 @@ import json -import os import textwrap from collections.abc import Sequence +from io import BytesIO from typing import Callable, Optional, Union import matplotlib @@ -12,7 +12,7 @@ import pendulum from matplotlib.backends.backend_pdf import PdfPages from akkudoktoreos.core.coreabc import ConfigMixin, get_ems -from akkudoktoreos.optimization.genetic.genetic import GeneticOptimizationParameters +from akkudoktoreos.optimization.genetic.geneticsolution import GeneticSolution from akkudoktoreos.utils.datetimeutil import DateTime, to_datetime matplotlib.use( @@ -23,39 +23,29 @@ matplotlib.use( debug_visualize: bool = False -class VisualizationReport(ConfigMixin): +class GeneticVisualizationReport(ConfigMixin): def __init__( self, - filename: str = "visualization_results.pdf", version: str = "0.0.1", - create_img: bool = True, ) -> None: - # Initialize the report with a given filename and empty groups - self.filename = filename + # Initialize the report with empty groups self.groups: list[list[Callable[[], None]]] = [] # Store groups of charts self.current_group: list[ Callable[[], None] ] = [] # Store current group of charts being created - self.pdf_pages = PdfPages(filename, metadata={}) # Initialize PdfPages without metadata + + # file like buffer to create the PDF + self._buffer = BytesIO() + self.pdf_pages: PdfPages | None = None + self.version = version # overwrite version as test for constant output of pdf for test self.current_time = to_datetime( as_string="YYYY-MM-DD HH:mm:ss", in_timezone=self.config.general.timezone ) - self.create_img = create_img def add_chart_to_group(self, chart_func: Callable[[], None], title: str | None) -> None: """Add a chart function to the current group and save it as a PNG and SVG.""" self.current_group.append(chart_func) - if self.create_img and title: - server_output_dir = self.config.cache.path() - server_output_dir.mkdir(parents=True, exist_ok=True) - fig, ax = plt.subplots() - chart_func() - plt.tight_layout() # Adjust the layout to ensure titles are not cut off - sanitized_title = "".join(c if c.isalnum() else "_" for c in title) - chart_filename_base = os.path.join(server_output_dir, f"chart_{sanitized_title}") - fig.savefig(f"{chart_filename_base}.svg") - plt.close(fig) def finalize_group(self) -> None: """Finalize the current group and prepare for a new group.""" @@ -66,22 +56,15 @@ class VisualizationReport(ConfigMixin): self.current_group = [] # Reset current group for new charts def _initialize_pdf(self) -> None: - """Create the output directory if it doesn't exist and initialize the PDF.""" - output_dir = self.config.general.data_output_path - - # If self.filename is already a valid path, use it; otherwise, combine it with output_dir - if os.path.isabs(self.filename): - output_file = self.filename - else: - output_dir.mkdir(parents=True, exist_ok=True) - output_file = os.path.join(output_dir, self.filename) - - self.pdf_pages = PdfPages( - output_file, metadata={} - ) # Re-initialize PdfPages without metadata + """Initialize the PDF.""" + self._buffer = BytesIO() + self.pdf_pages = PdfPages(self._buffer, metadata={}) # Initialize PdfPages without metadata def _save_group_to_pdf(self, group: list[Callable[[], None]]) -> None: """Save a group of charts to the PDF.""" + if self.pdf_pages is None: # make mypy happy + raise RuntimeError("Report not initialized") + fig_count = len(group) # Number of charts in the group if fig_count == 0: @@ -305,7 +288,7 @@ class VisualizationReport(ConfigMixin): def create_bar_chart( self, labels: list[str], - values_list: Sequence[Union[int, float, list[Union[int, float]]]], + values_list: Sequence[Sequence[int | float]], title: str, ylabel: str, xlabels: Optional[list[str]] = None, @@ -371,6 +354,9 @@ class VisualizationReport(ConfigMixin): """Add a page with text content to the PDF.""" def chart() -> None: + if self.pdf_pages is None: # make mypy happy + raise RuntimeError("Report not initialized") + fig = plt.figure(figsize=(8.5, 11)) # Create a standard page size plt.axis("off") # Turn off axes for a clean page wrapped_text = textwrap.fill(text, width=80) # Wrap text to fit the page width @@ -398,6 +384,9 @@ class VisualizationReport(ConfigMixin): """ def chart() -> None: + if self.pdf_pages is None: # make mypy happy + raise RuntimeError("Report not initialized") + # Convert JSON object to a formatted string json_str = json.dumps(json_obj, indent=4) @@ -424,31 +413,37 @@ class VisualizationReport(ConfigMixin): self.add_chart_to_group(chart, title) # Treat the JSON page as a "chart" in the group - def generate_pdf(self) -> None: - """Generate the PDF report with all the added chart groups.""" + def generate_pdf(self) -> bytes: + """Generate the PDF report with all the added chart groups. + + Returns: + PDF report as bytes object + """ self._initialize_pdf() # Initialize the PDF + buffer = BytesIO() + self.pdf_pages = PdfPages(buffer, metadata={}) + for group in self.groups: - self._save_group_to_pdf(group) # Save each group to the PDF + self._save_group_to_pdf(group) - self.pdf_pages.close() # Close the PDF to finalize the report + self.pdf_pages.close() + return buffer.getvalue() -def prepare_visualize( - parameters: GeneticOptimizationParameters, - results: dict, - filename: str = "visualization_results.pdf", - start_hour: int = 0, -) -> None: +def genetic_prepare_visualize( + solution: GeneticSolution, +) -> bytes: global debug_visualize - report = VisualizationReport(filename) + report = GeneticVisualizationReport() next_full_hour_date = get_ems().start_datetime + start_hour = solution.start_hour # Group 1: report.create_line_chart_date( next_full_hour_date, [ - parameters.ems.total_load[start_hour:], + solution.parameters.ems.total_load[start_hour:], ], title="Load Profile", # xlabel="Hours", # not enough space @@ -458,7 +453,7 @@ def prepare_visualize( report.create_line_chart_date( next_full_hour_date, [ - parameters.ems.pv_forecast_wh[start_hour:], + solution.parameters.ems.pv_forecast_wh[start_hour:], ], title="PV Forecast", # xlabel="Hours", # not enough space @@ -469,10 +464,10 @@ def prepare_visualize( next_full_hour_date, [ np.full( - len(parameters.ems.total_load) - start_hour, - parameters.ems.feed_in_tariff_per_wh[start_hour:] - if isinstance(parameters.ems.feed_in_tariff_per_wh, list) - else parameters.ems.feed_in_tariff_per_wh, + len(solution.parameters.ems.total_load) - start_hour, + solution.parameters.ems.feed_in_tariff_per_wh[start_hour:] + if isinstance(solution.parameters.ems.feed_in_tariff_per_wh, list) + else solution.parameters.ems.feed_in_tariff_per_wh, ) ], title="Remuneration", @@ -480,11 +475,11 @@ def prepare_visualize( ylabel="amount/Wh", x2label=None, # not enough space ) - if parameters.temperature_forecast: + if solution.parameters.temperature_forecast: report.create_line_chart_date( next_full_hour_date, [ - parameters.temperature_forecast[start_hour:], + solution.parameters.temperature_forecast[start_hour:], ], title="Temperature Forecast", # xlabel="Hours", # not enough space @@ -495,13 +490,13 @@ def prepare_visualize( # Group 2: report.create_line_chart_date( - next_full_hour_date, # start_date - [ - results["result"]["load_wh_per_hour"], - results["result"]["home_appliance_wh_per_hour"], - results["result"]["grid_feed_in_wh_per_hour"], - results["result"]["grid_consumption_wh_per_hour"], - results["result"]["losses_per_hour"], + start_date=next_full_hour_date, # start_date + y_list=[ + solution.result.load_wh_per_hour, + solution.result.home_appliance_wh_per_hour, + solution.result.grid_feed_in_wh_per_hour, + solution.result.grid_consumption_wh_per_hour, + solution.result.losses_per_hour, ], title="Energy Flow per Hour", # xlabel="Date", # not enough space @@ -520,8 +515,8 @@ def prepare_visualize( # Group 3: report.create_line_chart_date( - next_full_hour_date, # start_date - [results["result"]["battery_soc_per_hour"], results["result"]["ev_soc_per_hour"]], + start_date=next_full_hour_date, + y_list=[solution.result.battery_soc_per_hour, solution.result.ev_soc_per_hour], title="Battery SOC", # xlabel="Date", # not enough space ylabel="%", @@ -532,8 +527,8 @@ def prepare_visualize( markers=["o", "x"], ) report.create_line_chart_date( - next_full_hour_date, # start_date - [parameters.ems.electricity_price_per_wh[start_hour:]], + start_date=next_full_hour_date, # start_date + y_list=[solution.parameters.ems.electricity_price_per_wh[start_hour:]], # title="Electricity Price", # not enough space # xlabel="Date", # not enough space ylabel="Electricity Price (amount/Wh)", @@ -550,11 +545,11 @@ def prepare_visualize( labels = labels[start_hour:] + labels report.create_bar_chart( - labels, - [ - results["ac_charge"][start_hour:], - results["dc_charge"][start_hour:], - results["discharge_allowed"][start_hour:], + labels=labels, + values_list=[ + solution.ac_charge[start_hour:], + solution.dc_charge[start_hour:], + solution.discharge_allowed[start_hour:], ], title="AC/DC Charging and Discharge Overview", ylabel="Relative Power (0-1) / Discharge (0 or 1)", @@ -570,8 +565,8 @@ def prepare_visualize( report.create_line_chart_date( next_full_hour_date, # start_date [ - results["result"]["costs_per_hour"], - results["result"]["revenue_per_hour"], + solution.result.costs_per_hour, + solution.result.revenue_per_hour, ], title="Financial Balance per Hour", # xlabel="Date", # not enough space @@ -579,21 +574,22 @@ def prepare_visualize( labels=["Costs", "Revenue"], ) - extra_data = results["extra_data"] - report.create_scatter_plot( - extra_data["losses"], - extra_data["balance"], - title="Scatter Plot", - xlabel="losses", - ylabel="balance", - c=extra_data["constraints"], - ) + extra_data = solution.extra_data + if extra_data: + report.create_scatter_plot( + x=np.array(extra_data["losses"]), + y=np.array(extra_data["balance"]), + title="Scatter Plot", + xlabel="losses", + ylabel="balance", + c=np.array(extra_data["constraints"]), + ) values_list = [ [ - results["result"]["total_costs"], - results["result"]["total_revenue"], - results["result"]["total_balance"], + solution.result.total_costs, + solution.result.total_revenue, + solution.result.total_balance, ] ] labels = ["Total Costs [amount]", "Total Revenue [amount]", "Total Balance [amount]"] @@ -609,69 +605,77 @@ def prepare_visualize( report.finalize_group() # Group 1: Scatter plot of losses vs balance with color-coded constraints - f1 = np.array(extra_data["losses"]) # Losses - f2 = np.array(extra_data["balance"]) # Balance - n1 = np.array(extra_data["constraints"]) # Constraints + if extra_data: + f1 = np.array(extra_data["losses"]) # Losses + f2 = np.array(extra_data["balance"]) # Balance + n1 = np.array(extra_data["constraints"]) # Constraints - # Filter data where 'constraints' < 0.01 - filtered_indices = n1 < 0.01 - filtered_losses = f1[filtered_indices] - filtered_balance = f2[filtered_indices] + # Filter data where 'constraints' < 0.01 + filtered_indices = n1 < 0.01 + filtered_losses = f1[filtered_indices] + filtered_balance = f2[filtered_indices] - # Group 2: Violin plot for filtered losses - if filtered_losses.size > 0: - report.create_violin_plot( - data_list=[filtered_losses], # Data for filtered losses - labels=["Filtered Losses"], # Label for the violin plot - title="Violin Plot for Filtered Losses (Constraint < 0.01)", - xlabel="Losses", - ylabel="Values", - ) - else: - print("No data available for filtered losses violin plot (Constraint < 0.01)") + # Group 2: Violin plot for filtered losses + if filtered_losses.size > 0: + report.create_violin_plot( + data_list=[filtered_losses], # Data for filtered losses + labels=["Filtered Losses"], # Label for the violin plot + title="Violin Plot for Filtered Losses (Constraint < 0.01)", + xlabel="Losses", + ylabel="Values", + ) + else: + print("No data available for filtered losses violin plot (Constraint < 0.01)") # Group 3: Violin plot for filtered balance - if filtered_balance.size > 0: - report.create_violin_plot( - data_list=[filtered_balance], # Data for filtered balance - labels=["Filtered Balance"], # Label for the violin plot - title="Violin Plot for Filtered Balance (Constraint < 0.01)", - xlabel="Balance", - ylabel="Values", - ) - else: - print("No data available for filtered balance violin plot (Constraint < 0.01)") + if extra_data: + if filtered_balance.size > 0: + report.create_violin_plot( + data_list=[filtered_balance], # Data for filtered balance + labels=["Filtered Balance"], # Label for the violin plot + title="Violin Plot for Filtered Balance (Constraint < 0.01)", + xlabel="Balance", + ylabel="Values", + ) + else: + print("No data available for filtered balance violin plot (Constraint < 0.01)") + + if filtered_balance.size > 0 or filtered_losses.size > 0: + report.finalize_group() + + if solution.fitness_history: + if debug_visualize or solution.fixed_seed: + report.create_line_chart( + start_hour=0, + y_list=[ + np.array(solution.fitness_history["avg"]), + np.array(solution.fitness_history["max"]), + np.array(solution.fitness_history["min"]), + ], + title=f"DEBUG: Generation Fitness for seed {solution.fixed_seed}", + xlabel="Generation", + ylabel="Fitness", + labels=[ + "avg", + "max", + "min", + ], + markers=[".", ".", "."], + ) + report.finalize_group() - if filtered_balance.size > 0 or filtered_losses.size > 0: - report.finalize_group() - if debug_visualize or results["fixed_seed"]: - report.create_line_chart( - 0, - [ - results["fitness_history"]["avg"], - results["fitness_history"]["max"], - results["fitness_history"]["min"], - ], - title=f"DEBUG: Generation Fitness for seed {results['fixed_seed']}", - xlabel="Generation", - ylabel="Fitness", - labels=[ - "avg", - "max", - "min", - ], - markers=[".", ".", "."], - ) - report.finalize_group() # Generate the PDF report - report.generate_pdf() + pdf = report.generate_pdf() + + # Return pdf (a bytes object) + return pdf -def generate_example_report(filename: str = "example_report.pdf") -> None: +def genetic_generate_example_report(filename: str = "example_report.pdf") -> None: """Generate example visualization report.""" global debug_visualize - report = VisualizationReport(filename, "test") + report = GeneticVisualizationReport("test") x_hours = 0 # Define x-axis start values (e.g., hours) # Group 1: Adding charts to be displayed on the same page @@ -785,9 +789,13 @@ def generate_example_report(filename: str = "example_report.pdf") -> None: ylabel="test", ) report.finalize_group() - # Generate the PDF report - report.generate_pdf() + # Generate the PDF report (a bytes object) + pdf = report.generate_pdf() + + # write to file + with open(filename, "wb") as f: + f.write(pdf) if __name__ == "__main__": - generate_example_report() + genetic_generate_example_report() diff --git a/src/akkudoktoreos/optimization/genetic0/genetic0.py b/src/akkudoktoreos/optimization/genetic0/genetic0.py index 053a3386..8e0cac4a 100644 --- a/src/akkudoktoreos/optimization/genetic0/genetic0.py +++ b/src/akkudoktoreos/optimization/genetic0/genetic0.py @@ -1067,7 +1067,7 @@ class Genetic0Optimization(OptimizationBase): # Start hour has to be in sync with energy management if start_hour != self.ems.start_datetime.hour: raise ValueError( - f"Start hour not synced. EMS {self.ems.start_datetime.hour} vs. GENETIC {start_hour}." + f"Start hour not synced. EMS {self.ems.start_datetime.hour} vs. GENETIC0 {start_hour}." ) # Set the number of generations @@ -1224,39 +1224,20 @@ class Genetic0Optimization(OptimizationBase): else: discharge = discharge.tolist() - # Visualize the results in PDF - try: - from akkudoktoreos.utils.visualize import prepare_visualize - - visualize = { - "ac_charge": ac_charge_hours, - "dc_charge": dc_charge_hours, - "discharge_allowed": discharge, - "ev_charge_hours_float": ev_charge_hours_float, - "result": Genetic0SimulationResult(**simulation_result).model_dump(), - "ev_obj": self.simulation.ev.to_dict() if self.simulation.ev else None, - "start_solution": start_solution, - "washingstart": washingstart_int, - "extra_data": extra_data, - "fitness_history": self.fitness_history, - "fixed_seed": self.fix_seed, - } - - prepare_visualize(parameters, visualize, start_hour=start_hour) - - except Exception as ex: - error_msg = f"Visualization failed: {ex}" - logger.error(error_msg) - return Genetic0Solution( **{ + "parameters": parameters, "ac_charge": ac_charge_hours, "dc_charge": dc_charge_hours, "discharge_allowed": discharge, "ev_charge_hours_float": ev_charge_hours_float, "result": Genetic0SimulationResult(**simulation_result), "ev_obj": self.simulation.ev, + "start_hour": start_hour, "start_solution": start_solution, "washingstart": washingstart_int, + "extra_data": extra_data, + "fitness_history": self.fitness_history, + "fixed_seed": self.fix_seed, } ) diff --git a/src/akkudoktoreos/optimization/genetic0/genetic0solution.py b/src/akkudoktoreos/optimization/genetic0/genetic0solution.py index e9a1e42a..6c1d2f09 100644 --- a/src/akkudoktoreos/optimization/genetic0/genetic0solution.py +++ b/src/akkudoktoreos/optimization/genetic0/genetic0solution.py @@ -1,6 +1,6 @@ """Genetic0 algorithm optimisation solution.""" -from typing import Any, Optional +from typing import Any, Optional, Union import numpy as np import pandas as pd @@ -29,6 +29,9 @@ from akkudoktoreos.devices.genetic0.genetic0battery import Genetic0Battery from akkudoktoreos.optimization.genetic0.genetic0devices import ( Genetic0ParametersBaseModel, ) +from akkudoktoreos.optimization.genetic0.genetic0params import ( + Genetic0OptimizationParameters, +) from akkudoktoreos.optimization.optimization import OptimizationSolution from akkudoktoreos.utils.datetimeutil import to_datetime, to_duration from akkudoktoreos.utils.utils import NumpyEncoder @@ -244,8 +247,9 @@ class Genetic0SimulationResult(Genetic0ParametersBaseModel): return NumpyEncoder.convert_numpy(field)[0] -class Genetic0Solution(ConfigMixin, Genetic0ParametersBaseModel): - """**Note**: The first value of "load_wh_per_hour", "grid_feed_in_wh_per_hour", and "grid_consumption_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.""" +# Legacy GENETIC solution dataset. To keep the /optimize endpoint compatible. +class Genetic0SolutionLegacy(Genetic0ParametersBaseModel): + """GENETIC0 solution legacy dataset.""" model_config = ConfigDict(populate_by_name=True, extra="ignore") @@ -317,6 +321,109 @@ class Genetic0Solution(ConfigMixin, Genetic0ParametersBaseModel): return Genetic0ElectricVehicleResult(**field.to_dict()) return field + +class Genetic0Solution(ConfigMixin, Genetic0ParametersBaseModel): + """**Note**: The first value of "load_wh_per_hour", "grid_feed_in_wh_per_hour", and "grid_consumption_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.""" + + model_config = ConfigDict(populate_by_name=True, extra="ignore") + + parameters: Genetic0OptimizationParameters = Field( + json_schema_extra={"description": "Optimization parameters used to generate solution."} + ) + ac_charge: list[float] = Field( + json_schema_extra={ + "description": "Array with AC charging values as relative power (0.0-1.0), other values set to 0." + } + ) + dc_charge: list[float] = Field( + json_schema_extra={ + "description": "Array with DC charging values as relative power (0-1), other values set to 0." + } + ) + discharge_allowed: list[int] = Field( + json_schema_extra={ + "description": "Array with discharge values (1 for discharge, 0 otherwise)." + } + ) + ev_charge_hours_float: Optional[list[float]] = Field( + validation_alias=AliasChoices("ev_charge_hours_float", "eautocharge_hours_float"), + json_schema_extra={ + "description": "Array with EV charging values as relative power (0.0-1.0), or `null` if no EV is optimized." + }, + ) + result: Genetic0SimulationResult + ev_obj: Optional[Genetic0ElectricVehicleResult] = Field( + validation_alias=AliasChoices("ev_obj", "eauto_obj"), + json_schema_extra={"description": "Electric vehicle state after optimization."}, + ) + start_hour: int = Field( + default=0, + json_schema_extra={"description": "Start hour."}, + ) + start_solution: Optional[list[float]] = Field( + default=None, + json_schema_extra={ + "description": "An array of binary values (0 or 1) representing a possible starting solution for the simulation." + }, + ) + washingstart: Optional[int] = Field( + default=None, + json_schema_extra={ + "description": "Can be `null` or contain an object representing the start of washing (if applicable)." + }, + ) + extra_data: Optional[dict[str, Union[list[int], list[float]]]] = Field( + default=None, + json_schema_extra={ + "description": ("Dictionary of balance: TBD, losses: TBD, constraints: TBD.") + }, + ) + fitness_history: Optional[dict[str, Union[list[int], list[float]]]] = Field( + default=None, + json_schema_extra={ + "description": ( + "Dictionary of " + "gen: Generation numbers (X-axis), " + "avg: Average fitness for each generation (Y-axis), " + "max: Maximum fitness for each generation (Y-axis), " + "min: Minimum fitness for each generation (Y-axis)." + ) + }, + ) + fixed_seed: Optional[int] = Field( + default=None, + json_schema_extra={"description": "Fixed seed."}, + ) + + # Computed fields for backward compatibility (deprecated German names) + @computed_field(json_schema_extra={"deprecated": True}) + def eautocharge_hours_float(self) -> Optional[list[float]]: + """Deprecated: Use ev_charge_hours_float instead.""" + return self.ev_charge_hours_float + + @computed_field(json_schema_extra={"deprecated": True}) + def eauto_obj(self) -> Optional[Genetic0ElectricVehicleResult]: + """Deprecated: Use ev_obj instead.""" + return self.ev_obj + + @field_validator( + "ac_charge", + "dc_charge", + "discharge_allowed", + mode="before", + ) + def convert_numpy(cls, field: Any) -> Any: + return NumpyEncoder.convert_numpy(field)[0] + + @field_validator( + "ev_obj", + mode="before", + ) + def convert_eauto(cls, field: Any) -> Any: + if isinstance(field, Genetic0Battery): + return Genetic0ElectricVehicleResult(**field.to_dict()) + return field + def _battery_device_id(self) -> str: """Get battery device id.""" try: diff --git a/src/akkudoktoreos/optimization/genetic0/genetic0visualize.py b/src/akkudoktoreos/optimization/genetic0/genetic0visualize.py new file mode 100644 index 00000000..0856fa75 --- /dev/null +++ b/src/akkudoktoreos/optimization/genetic0/genetic0visualize.py @@ -0,0 +1,801 @@ +import json +import textwrap +from collections.abc import Sequence +from io import BytesIO +from typing import Callable, Optional, Union + +import matplotlib +import matplotlib.dates as mdates +import matplotlib.pyplot as plt +import numpy as np +import pendulum +from matplotlib.backends.backend_pdf import PdfPages + +from akkudoktoreos.core.coreabc import ConfigMixin, get_ems +from akkudoktoreos.optimization.genetic0.genetic0solution import Genetic0Solution +from akkudoktoreos.utils.datetimeutil import DateTime, to_datetime + +matplotlib.use( + "Agg" +) # non-interactive backend that can only write to files, backend needed to stay in main thread. + + +debug_visualize: bool = False + + +class Genetic0VisualizationReport(ConfigMixin): + def __init__( + self, + version: str = "0.0.1", + ) -> None: + # Initialize the report with empty groups + self.groups: list[list[Callable[[], None]]] = [] # Store groups of charts + self.current_group: list[ + Callable[[], None] + ] = [] # Store current group of charts being created + + # file like buffer to create the PDF + self._buffer = BytesIO() + self.pdf_pages: PdfPages | None = None + + self.version = version # overwrite version as test for constant output of pdf for test + self.current_time = to_datetime( + as_string="YYYY-MM-DD HH:mm:ss", in_timezone=self.config.general.timezone + ) + + def add_chart_to_group(self, chart_func: Callable[[], None], title: str | None) -> None: + """Add a chart function to the current group and save it as a PNG and SVG.""" + self.current_group.append(chart_func) + + def finalize_group(self) -> None: + """Finalize the current group and prepare for a new group.""" + if self.current_group: # Check if current group has charts + self.groups.append(self.current_group) # Add current group to groups + else: + print("Finalizing an empty group!") # Warn if group is empty + self.current_group = [] # Reset current group for new charts + + def _initialize_pdf(self) -> None: + """Initialize the PDF.""" + self._buffer = BytesIO() + self.pdf_pages = PdfPages(self._buffer, metadata={}) # Initialize PdfPages without metadata + + def _save_group_to_pdf(self, group: list[Callable[[], None]]) -> None: + """Save a group of charts to the PDF.""" + if self.pdf_pages is None: # make mypy happy + raise RuntimeError("Report not initialized") + + fig_count = len(group) # Number of charts in the group + + if fig_count == 0: + print("Attempted to save an empty group to PDF!") + return + + # Check for special charts before creating layout + special_keywords = {"add_text_page", "add_json_page"} + for chart_func in group: + if any(keyword in chart_func.__qualname__ for keyword in special_keywords): + chart_func() # Special chart functions handle their own rendering + return + + # Create layout only if no special charts are detected + if fig_count == 3: + fig = plt.figure(figsize=(14, 10)) + ax1 = fig.add_subplot(2, 1, 1) + ax2 = fig.add_subplot(2, 2, 3) + ax3 = fig.add_subplot(2, 2, 4) + axs = [ax1, ax2, ax3] + else: + cols = 2 if fig_count > 1 else 1 + rows = (fig_count + 1) // 2 + fig, axs = plt.subplots(rows, cols, figsize=(14, 7 * rows)) + axs = list(np.array(axs).reshape(-1)) + + # Add footer text with current time to each page + if self.version == "test": + current_time = "test" + else: + current_time = self.current_time + fig.text( + 0.5, + 0.02, + f"Generated on: {current_time} with version: {self.version}", + ha="center", + va="center", + fontsize=10, + ) + + # Render each chart in its corresponding axis + for idx, chart_func in enumerate(group): + plt.sca(axs[idx]) # Set current axis + chart_func() # Render the chart + + # Save the figure to the PDF and clean up + for idx in range(fig_count, len(axs)): + axs[idx].set_visible(False) + + self.pdf_pages.savefig(fig) # Save the figure to the PDF + plt.close(fig) + + def create_line_chart_date( + self, + start_date: DateTime, + y_list: list[Union[np.ndarray, list[Optional[float]], list[float]]], + ylabel: str, + xlabel: Optional[str] = None, + title: Optional[str] = None, + labels: Optional[list[str]] = None, + markers: Optional[list[str]] = None, + line_styles: Optional[list[str]] = None, + x2label: Optional[Union[str, None]] = "Hours Since Start", + ) -> None: + """Create a line chart and add it to the current group.""" + + def chart() -> None: + timestamps = [ + start_date.add(hours=i) for i in range(len(y_list[0])) + ] # 840 timestamps at 1-hour intervals + + for idx, y_data in enumerate(y_list): + label = labels[idx] if labels else None # Chart label + marker = markers[idx] if markers and idx < len(markers) else "o" # Marker style + line_style = line_styles[idx] if line_styles and idx < len(line_styles) else "-" + plt.plot( + timestamps, y_data, label=label, marker=marker, linestyle=line_style + ) # Plot line + + # Format the time axis + plt.gca().xaxis.set_major_formatter( + mdates.DateFormatter("%Y-%m-%d", tz=self.config.general.timezone) + ) # Show date and time + plt.gca().xaxis.set_major_locator( + mdates.DayLocator(interval=1, tz=self.config.general.timezone) + ) # Major ticks every day + plt.gca().xaxis.set_minor_locator( + mdates.HourLocator(interval=2, tz=self.config.general.timezone) + ) + # Minor ticks every 6 hours + plt.gca().xaxis.set_minor_formatter( + mdates.DateFormatter("%H", tz=self.config.general.timezone) + ) + # plt.gcf().autofmt_xdate(rotation=45, which="major") + # Auto-format the x-axis for readability + + # Move major tick labels further down to avoid collision with minor tick labels + for plt_label in plt.gca().get_xticklabels(which="major"): + plt_label.set_y(-0.04) + + # Add labels, title, and legend + if xlabel: + plt.xlabel(xlabel) + plt.ylabel(ylabel) + if title: + plt.title(title) + if labels: + plt.legend() + plt.grid(True) + + # Add vertical line for the current date if within the axis range + current_time = pendulum.now(self.config.general.timezone) + if timestamps[0].subtract(hours=2) <= current_time <= timestamps[-1]: + plt.axvline(current_time, color="r", linestyle="--", label="Now") + plt.text(current_time, plt.ylim()[1], "Now", color="r", ha="center", va="bottom") + + # Add a second x-axis on top + ax1 = plt.gca() + ax2 = ax1.twiny() + ax2.set_xlim(ax1.get_xlim()) # Align the second axis with the first + + # Generate integer hour labels + hours_since_start = [(t - timestamps[0]).total_seconds() / 3600 for t in timestamps] + # ax2.set_xticks(timestamps[::48]) # Set ticks every 12 hours + # ax2.set_xticklabels([f"{int(h)}" for h in hours_since_start[::48]]) + # ax2.set_xticks(timestamps[:: len(timestamps) // 24]) # Select 10 evenly spaced ticks + ax2.set_xticks(timestamps[:: len(timestamps) // 12]) # Select 10 evenly spaced ticks + # ax2.set_xticklabels([f"{int(h)}" for h in hours_since_start[:: len(timestamps) // 24]]) + ax2.set_xticklabels([f"{int(h)}" for h in hours_since_start[:: len(timestamps) // 12]]) + if x2label: + ax2.set_xlabel(x2label) + + # Ensure ax1 and ax2 are aligned + # assert ax1.get_xlim() == ax2.get_xlim(), "ax1 and ax2 are not aligned" + + self.add_chart_to_group(chart, title) # Add chart function to current group + + def create_line_chart( + self, + start_hour: Optional[int], + y_list: list[Union[np.ndarray, list[Optional[float]], list[float]]], + title: str, + xlabel: str, + ylabel: str, + labels: Optional[list[str]] = None, + markers: Optional[list[str]] = None, + line_styles: Optional[list[str]] = None, + ) -> None: + """Create a line chart and add it to the current group.""" + + def chart() -> None: + nonlocal start_hour # Allow modifying `x` within the nested function + if start_hour is None: + start_hour = 0 + first_element = y_list[0] + x: np.ndarray + # Case 1: y_list contains np.ndarray elements + if isinstance(first_element, np.ndarray): + x = np.arange( + start_hour, start_hour + len(first_element) + ) # Start at x and extend by ndarray length + # Case 2: y_list contains float elements (1D list) + elif isinstance(first_element, float): + x = np.arange( + start_hour, start_hour + len(y_list) + ) # Start at x and extend by list length + # Case 3: y_list is a nested list of floats + elif isinstance(first_element, list) and all( + isinstance(i, float) for i in first_element + ): + max_len = max(len(sublist) for sublist in y_list) + x = np.arange( + start_hour, start_hour + max_len + ) # Start at x and extend by max sublist length + else: + print(f"Unsupported y_list structure: {type(y_list)}, {y_list}") + raise TypeError( + "y_list elements must be np.ndarray, float, or a nested list of floats" + ) + + for idx, y_data in enumerate(y_list): + label = labels[idx] if labels else None # Chart label + marker = markers[idx] if markers and idx < len(markers) else "o" # Marker style + line_style = ( + line_styles[idx] if line_styles and idx < len(line_styles) else "-" + ) # Line style + plt.plot(x, y_data, label=label, marker=marker, linestyle=line_style) # Plot line + + plt.title(title) # Set title + plt.xlabel(xlabel) # Set x-axis label + plt.ylabel(ylabel) # Set y-axis label + if labels: + plt.legend() # Show legend if labels are provided + plt.grid(True) # Show grid + plt.xlim(x[0] - 0.5, x[-1] + 0.5) # Adjust x-limits + + self.add_chart_to_group(chart, title) # Add chart function to current group + + def create_scatter_plot( + self, + x: np.ndarray, + y: np.ndarray, + title: str, + xlabel: str, + ylabel: str, + c: Optional[np.ndarray] = None, + ) -> None: + """Create a scatter plot and add it to the current group.""" + + def chart() -> None: + scatter = plt.scatter(x, y, c=c, cmap="viridis") # Create scatter plot + plt.title(title) # Set title + plt.xlabel(xlabel) # Set x-axis label + plt.ylabel(ylabel) # Set y-axis label + if c is not None: + plt.colorbar(scatter, label="Constraint") # Add colorbar if color data is provided + plt.grid(True) # Show grid + + self.add_chart_to_group(chart, title) # Add chart function to current group + + def create_bar_chart( + self, + labels: Sequence[str], + values_list: Sequence[Sequence[int | float]], + title: str, + ylabel: str, + xlabels: Optional[list[str]] = None, + label_names: Optional[list[str]] = None, + colors: Optional[list[str]] = None, + bar_width: float = 0.35, + bottom: Optional[int] = None, + ) -> None: + """Create a bar chart and add it to the current group.""" + + def chart() -> None: + num_groups = len(values_list) # Number of data groups + num_bars = len(labels) # Number of bars (categories) + # Calculate the positions for each bar group on the x-axis + x = np.arange(num_bars) # x positions for bars + offset = np.linspace( + -bar_width * (num_groups - 1) / 2, bar_width * (num_groups - 1) / 2, num_groups + ) # Bar offsets + for i, values in enumerate(values_list): + bottom_use = None + if bottom == i + 1: # Set bottom if specified + bottom_use = 1 + color = colors[i] if colors and i < len(colors) else None # Bar color + label_name = label_names[i] if label_names else None # Bar label + plt.bar( + x + offset[i], + values, + bar_width, + label=label_name, + color=color, + zorder=2, + alpha=0.6, + bottom=bottom_use, + ) # Create bar + if xlabels: + plt.xticks(x, labels) # Add custom labels to the x-axis + plt.title(title) # Set title + plt.ylabel(ylabel) # Set y-axis label + + if colors and label_names: + plt.legend() # Show legend if colors are provided + plt.grid(True, zorder=0) # Show grid in the background + plt.xlim(-0.5, len(labels) - 0.5) # Set x-axis limits + + self.add_chart_to_group(chart, title) # Add chart function to current group + + def create_violin_plot( + self, data_list: list[np.ndarray], labels: list[str], title: str, xlabel: str, ylabel: str + ) -> None: + """Create a violin plot and add it to the current group.""" + + def chart() -> None: + plt.violinplot(data_list, showmeans=True, showmedians=True) # Create violin plot + plt.xticks(np.arange(1, len(labels) + 1), labels) # Set x-ticks and labels + plt.title(title) # Set title + plt.xlabel(xlabel) # Set x-axis label + plt.ylabel(ylabel) # Set y-axis label + plt.grid(True) # Show grid + + self.add_chart_to_group(chart, title) # Add chart function to current group + + def add_text_page(self, text: str, title: Optional[str] = None, fontsize: int = 12) -> None: + """Add a page with text content to the PDF.""" + + def chart() -> None: + if self.pdf_pages is None: # make mypy happy + raise RuntimeError("Report not initialized") + + fig = plt.figure(figsize=(8.5, 11)) # Create a standard page size + plt.axis("off") # Turn off axes for a clean page + wrapped_text = textwrap.fill(text, width=80) # Wrap text to fit the page width + y = 0.95 # Start at the top of the page + + if title: + plt.text(0.5, y, title, ha="center", va="top", fontsize=fontsize + 4, weight="bold") + y -= 0.05 # Add space after the title + + plt.text(0.5, y, wrapped_text, ha="center", va="top", fontsize=fontsize, wrap=True) + self.pdf_pages.savefig(fig) # Save the figure as a page in the PDF + plt.close(fig) # Close the figure to free up memory + + self.add_chart_to_group(chart, title) # Treat the text page as a "chart" in the group + + def add_json_page( + self, json_obj: dict, title: Optional[str] = None, fontsize: int = 12 + ) -> None: + """Add a page with a formatted JSON object to the PDF. + + Args: + json_obj (dict): The JSON object to display. + title (Optional[str]): An optional title for the page. + fontsize (int): The font size for the JSON text. + """ + + def chart() -> None: + if self.pdf_pages is None: # make mypy happy + raise RuntimeError("Report not initialized") + + # Convert JSON object to a formatted string + json_str = json.dumps(json_obj, indent=4) + + fig = plt.figure(figsize=(8.5, 11)) # Standard page size + plt.axis("off") # Turn off axes for a clean page + + y = 0.95 # Start at the top of the page + if title: + plt.text(0.5, y, title, ha="center", va="top", fontsize=fontsize + 4, weight="bold") + y -= 0.05 # Add space after the title + + # Split the JSON string into lines and render them + lines = json_str.splitlines() + for line in lines: + plt.text(0.05, y, line, ha="left", va="top", fontsize=fontsize, family="monospace") + y -= 0.02 # Move down for the next line + + # Stop if the text exceeds the page + if y < 0.05: + break + + self.pdf_pages.savefig(fig) # Save the figure as a page in the PDF + plt.close(fig) # Close the figure to free up memory + + self.add_chart_to_group(chart, title) # Treat the JSON page as a "chart" in the group + + def generate_pdf(self) -> bytes: + """Generate the PDF report with all the added chart groups. + + Returns: + PDF report as bytes object + """ + self._initialize_pdf() # Initialize the PDF + + buffer = BytesIO() + self.pdf_pages = PdfPages(buffer, metadata={}) + + for group in self.groups: + self._save_group_to_pdf(group) + + self.pdf_pages.close() + return buffer.getvalue() + + +def genetic0_prepare_visualize( + solution: Genetic0Solution, +) -> bytes: + global debug_visualize + + report = Genetic0VisualizationReport() + next_full_hour_date = get_ems().start_datetime + start_hour = solution.start_hour + # Group 1: + report.create_line_chart_date( + next_full_hour_date, + [ + solution.parameters.ems.total_load[start_hour:], + ], + title="Load Profile", + # xlabel="Hours", # not enough space + ylabel="Load (Wh)", + labels=["Total Load (Wh)"], + ) + report.create_line_chart_date( + next_full_hour_date, + [ + solution.parameters.ems.pv_forecast_wh[start_hour:], + ], + title="PV Forecast", + # xlabel="Hours", # not enough space + ylabel="PV Generation (Wh)", + ) + + report.create_line_chart_date( + next_full_hour_date, + [ + np.full( + len(solution.parameters.ems.total_load) - start_hour, + solution.parameters.ems.feed_in_tariff_per_wh[start_hour:] + if isinstance(solution.parameters.ems.feed_in_tariff_per_wh, list) + else solution.parameters.ems.feed_in_tariff_per_wh, + ) + ], + title="Remuneration", + # xlabel="Hours", # not enough space + ylabel="amount/Wh", + x2label=None, # not enough space + ) + if solution.parameters.temperature_forecast: + report.create_line_chart_date( + next_full_hour_date, + [ + solution.parameters.temperature_forecast[start_hour:], + ], + title="Temperature Forecast", + # xlabel="Hours", # not enough space + ylabel="°C", + x2label=None, # not enough space + ) + report.finalize_group() + + # Group 2: + report.create_line_chart_date( + start_date=next_full_hour_date, # start_date + y_list=[ + solution.result.load_wh_per_hour, + solution.result.home_appliance_wh_per_hour, + solution.result.grid_feed_in_wh_per_hour, + solution.result.grid_consumption_wh_per_hour, + solution.result.losses_per_hour, + ], + title="Energy Flow per Hour", + # xlabel="Date", # not enough space + ylabel="Energy (Wh)", + labels=[ + "Load (Wh)", + "Household Device (Wh)", + "Grid Feed-in (Wh)", + "Grid Consumption (Wh)", + "Losses (Wh)", + ], + markers=["o", "o", "x", "^", "^"], + line_styles=["-", "--", ":", "-.", "-"], + ) + report.finalize_group() + + # Group 3: + report.create_line_chart_date( + start_date=next_full_hour_date, + y_list=[solution.result.battery_soc_per_hour, solution.result.ev_soc_per_hour], + title="Battery SOC", + # xlabel="Date", # not enough space + ylabel="%", + labels=[ + "Battery SOC (%)", + "Electric Vehicle SOC (%)", + ], + markers=["o", "x"], + ) + report.create_line_chart_date( + start_date=next_full_hour_date, # start_date + y_list=[solution.parameters.ems.electricity_price_per_wh[start_hour:]], + # title="Electricity Price", # not enough space + # xlabel="Date", # not enough space + ylabel="Electricity Price (amount/Wh)", + x2label=None, # not enough space + ) + + labels = list( + item + for sublist in zip( + list(str(i) for i in range(0, 23, 2)), list(str(" ") for i in range(0, 23, 2)) + ) + for item in sublist + ) + labels = labels[start_hour:] + labels + + report.create_bar_chart( + labels=labels, + values_list=[ + solution.ac_charge[start_hour:], + solution.dc_charge[start_hour:], + solution.discharge_allowed[start_hour:], + ], + title="AC/DC Charging and Discharge Overview", + ylabel="Relative Power (0-1) / Discharge (0 or 1)", + label_names=["AC Charging (relative)", "DC Charging (relative)", "Discharge Allowed"], + colors=["blue", "green", "red"], + bottom=3, + xlabels=labels, + ) + report.finalize_group() + + # Group 4: + + report.create_line_chart_date( + next_full_hour_date, # start_date + [ + solution.result.costs_per_hour, + solution.result.revenue_per_hour, + ], + title="Financial Balance per Hour", + # xlabel="Date", # not enough space + ylabel="Amount", + labels=["Costs", "Revenue"], + ) + + extra_data = solution.extra_data + if extra_data: + report.create_scatter_plot( + x=np.array(extra_data["losses"]), + y=np.array(extra_data["balance"]), + title="Scatter Plot", + xlabel="losses", + ylabel="balance", + c=np.array(extra_data["constraints"]), + ) + + values_list = [ + [ + solution.result.total_costs, + solution.result.total_revenue, + solution.result.total_balance, + ] + ] + labels = ["Total Costs [amount]", "Total Revenue [amount]", "Total Balance [amount]"] + + report.create_bar_chart( + labels=labels, + values_list=values_list, + title="Financial Overview", + ylabel="Amount", + xlabels=["Total Costs [amount]", "Total Revenue [amount]", "Total Balance [amount]"], + ) + + report.finalize_group() + + # Group 1: Scatter plot of losses vs balance with color-coded constraints + if extra_data: + f1 = np.array(extra_data["losses"]) # Losses + f2 = np.array(extra_data["balance"]) # Balance + n1 = np.array(extra_data["constraints"]) # Constraints + + # Filter data where 'constraints' < 0.01 + filtered_indices = n1 < 0.01 + filtered_losses = f1[filtered_indices] + filtered_balance = f2[filtered_indices] + + # Group 2: Violin plot for filtered losses + if filtered_losses.size > 0: + report.create_violin_plot( + data_list=[filtered_losses], # Data for filtered losses + labels=["Filtered Losses"], # Label for the violin plot + title="Violin Plot for Filtered Losses (Constraint < 0.01)", + xlabel="Losses", + ylabel="Values", + ) + else: + print("No data available for filtered losses violin plot (Constraint < 0.01)") + + # Group 3: Violin plot for filtered balance + if extra_data: + if filtered_balance.size > 0: + report.create_violin_plot( + data_list=[filtered_balance], # Data for filtered balance + labels=["Filtered Balance"], # Label for the violin plot + title="Violin Plot for Filtered Balance (Constraint < 0.01)", + xlabel="Balance", + ylabel="Values", + ) + else: + print("No data available for filtered balance violin plot (Constraint < 0.01)") + + if filtered_balance.size > 0 or filtered_losses.size > 0: + report.finalize_group() + + if solution.fitness_history: + if debug_visualize or solution.fixed_seed: + report.create_line_chart( + start_hour=0, + y_list=[ + np.array(solution.fitness_history["avg"]), + np.array(solution.fitness_history["max"]), + np.array(solution.fitness_history["min"]), + ], + title=f"DEBUG: Generation Fitness for seed {solution.fixed_seed}", + xlabel="Generation", + ylabel="Fitness", + labels=[ + "avg", + "max", + "min", + ], + markers=[".", ".", "."], + ) + report.finalize_group() + + # Generate the PDF report + pdf = report.generate_pdf() + + # Return pdf (a bytes object) + return pdf + + +def genetic0_generate_example_report(filename: str = "example_report.pdf") -> None: + """Generate example visualization report.""" + global debug_visualize + + report = Genetic0VisualizationReport("test") + x_hours = 0 # Define x-axis start values (e.g., hours) + + # Group 1: Adding charts to be displayed on the same page + report.create_line_chart( + x_hours, + [np.array([10, 20, 30, 40])], + title="Load Profile", + xlabel="Hours", + ylabel="Load (Wh)", + ) + report.create_line_chart( + x_hours, + [np.array([5, 15, 25, 35])], + title="PV Forecast", + xlabel="Hours", + ylabel="PV Generation (Wh)", + ) + report.create_line_chart( + x_hours, + [np.array([5, 15, 25, 35])], + title="PV Forecast", + xlabel="Hours", + ylabel="PV Generation (Wh)", + ) + # Note: If there are only 3 charts per page, the first is as wide as the page + + report.finalize_group() # Finalize the first group of charts + + # Group 2: Adding more charts to be displayed on another page + report.create_line_chart( + x_hours, + [np.array([0.2, 0.25, 0.3, 0.35])], + title="Electricity Price", + xlabel="Hours", + ylabel="Price (€/Wh)", + ) + report.create_bar_chart( + ["Costs", "Revenue", "Balance"], + [[500.0], [600.0], [100.0]], + title="Financial Overview", + ylabel="Euro", + label_names=["AC Charging (relative)", "DC Charging (relative)", "Discharge Allowed"], + colors=["red", "green", "blue"], + ) + report.create_scatter_plot( + np.array([5, 6, 7, 8]), + np.array([100, 200, 150, 250]), + title="Scatter Plot", + xlabel="Losses", + ylabel="Balance", + c=np.array([0.1, 0.2, 0.3, 0.4]), + ) + report.finalize_group() # Finalize the second group of charts + + # Group 3: Adding a violin plot + data = [np.random.normal(0, std, 100) for std in range(1, 5)] # Example data for violin plot + report.create_violin_plot( + data, + labels=["Group 1", "Group 2", "Group 3", "Group 4"], + title="Violin Plot", + xlabel="Groups", + ylabel="Values", + ) + data = [np.random.normal(0, 1, 100)] # Example data for violin plot + report.create_violin_plot( + data, labels=["Group 1"], title="Violin Plot", xlabel="Group", ylabel="Values" + ) + + report.finalize_group() # Finalize the third group of charts + + debug_visualize = True # set level for example report + + if debug_visualize: + report.create_line_chart( + x_hours, + [np.array([0.2, 0.25, 0.3, 0.35])], + title="DEBUG", + xlabel="DEBUG", + ylabel="DEBUG", + ) + report.finalize_group() # Finalize the third group of charts + + report.add_text_page( + text=" Bisher passierte folgendes:" + "Am Anfang wurde das Universum erschaffen." + "Das machte viele Leute sehr wütend und wurde allent-" + "halben als Schritt in die falsche Richtung angesehen...", + title="Don't Panic!", + fontsize=14, + ) + report.finalize_group() + + sample_json = { + "name": "Visualization Report", + "version": 1.0, + "charts": [ + {"type": "line", "data_points": 50}, + {"type": "bar", "categories": 10}, + ], + "metadata": {"author": "AI Assistant", "date": "2025-01-11"}, + } + + report.add_json_page(json_obj=sample_json, title="Formatted JSON Data", fontsize=10) + report.finalize_group() + + report.create_line_chart_date( + pendulum.now().subtract(hours=0), + [list(np.random.random(840))], + title="test", + xlabel="test", + ylabel="test", + ) + report.finalize_group() + # Generate the PDF report (a bytes object) + pdf = report.generate_pdf() + + # write to file + with open(filename, "wb") as f: + f.write(pdf) + + +if __name__ == "__main__": + genetic0_generate_example_report() diff --git a/src/akkudoktoreos/prediction/elecpriceenergycharts.py b/src/akkudoktoreos/prediction/elecpriceenergycharts.py index 4334ad3e..eee6d401 100644 --- a/src/akkudoktoreos/prediction/elecpriceenergycharts.py +++ b/src/akkudoktoreos/prediction/elecpriceenergycharts.py @@ -217,7 +217,7 @@ class ElecPriceEnergyCharts(ElecPriceProvider): # Determine if update is needed and how many days past_days = 35 if self.highest_orig_datetime: - history_series = await self.key_to_series( + history_series = await self.key_to_raw_series( key="elecprice_marketprice_wh", start_datetime=self.ems_start_datetime ) # If history lower, then start_datetime diff --git a/src/akkudoktoreos/prediction/feedintariffenergycharts.py b/src/akkudoktoreos/prediction/feedintariffenergycharts.py index 2ab9392e..52bb8cd3 100644 --- a/src/akkudoktoreos/prediction/feedintariffenergycharts.py +++ b/src/akkudoktoreos/prediction/feedintariffenergycharts.py @@ -170,7 +170,7 @@ class FeedInTariffEnergyCharts(FeedInTariffProvider): past_days = 35 needs_history_refresh = False if self.highest_orig_datetime: - raw_history = await self.key_to_series( + raw_history = await self.key_to_raw_series( key="feed_in_tariff_wh", end_datetime=to_datetime(self.highest_orig_datetime).add(seconds=1), ) @@ -243,7 +243,7 @@ class FeedInTariffEnergyCharts(FeedInTariffProvider): logger.error(error_msg) raise ValueError(error_msg) - raw_series = await self.key_to_series( + raw_series = await self.key_to_raw_series( key="feed_in_tariff_wh", end_datetime=to_datetime(self.highest_orig_datetime).add(seconds=1), ) diff --git a/src/akkudoktoreos/prediction/weatherbrightsky.py b/src/akkudoktoreos/prediction/weatherbrightsky.py index b62a4c7c..7cc9f460 100644 --- a/src/akkudoktoreos/prediction/weatherbrightsky.py +++ b/src/akkudoktoreos/prediction/weatherbrightsky.py @@ -132,7 +132,7 @@ class WeatherBrightSky(WeatherProvider): error_msg = f"No WeatherDataRecord key for '{description}'" logger.error(error_msg) raise ValueError(error_msg) - series = await self.key_to_series(key) + series = await self.key_to_raw_series(key) return series async def _description_from_series(self, description: str, data: pd.Series) -> None: diff --git a/src/akkudoktoreos/prediction/weatheropenmeteo.py b/src/akkudoktoreos/prediction/weatheropenmeteo.py index 3b29d1f7..1c493cea 100644 --- a/src/akkudoktoreos/prediction/weatheropenmeteo.py +++ b/src/akkudoktoreos/prediction/weatheropenmeteo.py @@ -218,7 +218,7 @@ class WeatherOpenMeteo(WeatherProvider): error_msg = f"No WeatherDataRecord key for '{description}'" logger.error(error_msg) raise ValueError(error_msg) - series = await self.key_to_series(key) + series = await self.key_to_raw_series(key) return series async def _description_from_series(self, description: str, data: pd.Series) -> None: diff --git a/src/akkudoktoreos/server/eos.py b/src/akkudoktoreos/server/eos.py index 23abd4ab..8faee8c2 100755 --- a/src/akkudoktoreos/server/eos.py +++ b/src/akkudoktoreos/server/eos.py @@ -8,16 +8,16 @@ import subprocess import sys import traceback from contextlib import asynccontextmanager +from enum import Enum from typing import Annotated, Any, AsyncGenerator, Dict, List, Optional, Union import psutil import uvicorn from fastapi import Body, FastAPI from fastapi import Path as FastapiPath -from fastapi import Query, Request, status +from fastapi import Query, Request from fastapi.exceptions import HTTPException from fastapi.responses import ( - FileResponse, HTMLResponse, JSONResponse, RedirectResponse, @@ -56,14 +56,24 @@ from akkudoktoreos.devices.devices import ResourceKey from akkudoktoreos.optimization.genetic0.genetic0params import ( Genetic0OptimizationParameters, ) -from akkudoktoreos.optimization.genetic0.genetic0solution import Genetic0Solution +from akkudoktoreos.optimization.genetic0.genetic0solution import ( + Genetic0Solution, + Genetic0SolutionLegacy, +) +from akkudoktoreos.optimization.genetic0.genetic0visualize import ( + genetic0_prepare_visualize, +) from akkudoktoreos.optimization.genetic.geneticsolution import GeneticSolution from akkudoktoreos.optimization.optimization import OptimizationSolution from akkudoktoreos.prediction.elecprice import ElecPriceCommonSettings from akkudoktoreos.prediction.load import LoadCommonSettings from akkudoktoreos.prediction.loadakkudoktor import LoadAkkudoktorCommonSettings from akkudoktoreos.prediction.pvforecast import PVForecastCommonSettings -from akkudoktoreos.server.rest.error import create_error_page +from akkudoktoreos.server.rest.error import ( + EOSProblem, + create_error_page, + register_problem_handlers, +) from akkudoktoreos.server.rest.starteosdash import supervise_eosdash from akkudoktoreos.server.retentionmanager import RetentionManager from akkudoktoreos.server.server import ( @@ -236,8 +246,21 @@ The genetic optimization API fields were renamed from German to English. For bac ) -class PdfResponse(FileResponse): - media_type = "application/pdf" +# ---------------------- +# Application generic exception handling +# ---------------------- + +register_problem_handlers(app) + + +# ---------------------- +# Application API +# ---------------------- + + +class SeriesProcessing(str, Enum): + RAW = "raw" + RESAMPLED = "resampled" @app.post("/v1/admin/cache/clear", tags=["admin"]) @@ -253,7 +276,7 @@ def fastapi_admin_cache_clear_post() -> dict: cache_clear(clear_all=True) data = CacheFileStore().current_store() except Exception as e: - raise HTTPException(status_code=400, detail=f"Error on cache clear: {e}") + raise EOSProblem(status=400, title="Error on cache clear", detail=str(e), cause=e) from e return data @@ -270,7 +293,9 @@ def fastapi_admin_cache_clear_expired_post() -> dict: cache_clear(clear_all=False) data = CacheFileStore().current_store() except Exception as e: - raise HTTPException(status_code=400, detail=f"Error on cache clear expired: {e}") + raise EOSProblem( + status=400, title="Error on cache clear expired", detail=str(e), cause=e + ) from e return data @@ -284,7 +309,7 @@ def fastapi_admin_cache_save_post() -> dict: try: data = cache_save() except Exception as e: - raise HTTPException(status_code=400, detail=f"Error on cache save: {e}") + raise EOSProblem(status=400, title="Error on cache save", detail=str(e), cause=e) from e return data @@ -298,7 +323,7 @@ def fastapi_admin_cache_load_post() -> dict: try: data = cache_save() except Exception as e: - raise HTTPException(status_code=400, detail=f"Error on cache load: {e}") + raise EOSProblem(status=400, title="Error on cache load", detail=str(e), cause=e) from e return data @@ -312,7 +337,9 @@ def fastapi_admin_cache_get() -> dict: try: data = CacheFileStore().current_store() except Exception as e: - raise HTTPException(status_code=400, detail=f"Error on cache data retrieval: {e}") + raise EOSProblem( + status=400, title="Error on cache data retrieval", detail=str(e), cause=e + ) from e return data @@ -329,10 +356,9 @@ async def fastapi_admin_database_stats_get() -> dict: data[get_measurement().db_namespace()] = await get_measurement().db_get_stats() data[get_prediction().__class__.__name__] = await get_prediction().db_get_stats() except Exception as e: - trace = "".join(traceback.TracebackException.from_exception(e).format()) - raise HTTPException( - status_code=400, detail=f"Error on database statistic retrieval:\n{e}\n{trace}" - ) + raise EOSProblem( + status=400, title="Error on database statistic retrieval", detail=str(e), cause=e + ) from e return data @@ -351,8 +377,7 @@ async def fastapi_admin_database_save_post() -> dict: data[get_measurement().db_namespace()] = await get_measurement().db_get_stats() data[get_prediction().__class__.__name__] = await get_prediction().db_get_stats() except Exception as e: - trace = "".join(traceback.TracebackException.from_exception(e).format()) - raise HTTPException(status_code=400, detail=f"Error on database save: {e}\n{trace}") + raise EOSProblem(status=400, title="Error on database save", detail=str(e), cause=e) from e return data @@ -373,8 +398,9 @@ async def fastapi_admin_database_vacuum_post() -> dict: prediction_stats = await get_prediction().db_get_stats() data[get_prediction().__class__.__name__] = prediction_stats except Exception as e: - trace = "".join(traceback.TracebackException.from_exception(e).format()) - raise HTTPException(status_code=400, detail=f"Error on database vacuum: {e}\n{trace}") + raise EOSProblem( + status=400, title="Error on database vacuum", detail=str(e), cause=e + ) from e return data @@ -473,13 +499,7 @@ def fastapi_config_reset_post() -> ConfigEOS: Returns: configuration (ConfigEOS): The current configuration after update. """ - try: - get_config().reset_settings() - except Exception as e: - raise HTTPException( - status_code=404, - detail=f"Cannot reset configuration: {e}", - ) + get_config().reset_settings() return get_config() @@ -490,14 +510,7 @@ def fastapi_config_backup_get() -> dict[str, dict[str, Any]]: Returns: dict[str, dict[str, Any]]: Mapping of backup identifiers to metadata. """ - try: - result = get_config().list_backups() - except Exception as e: - raise HTTPException( - status_code=404, - detail=f"Can not list configuration backups: {e}", - ) - return result + return get_config().list_backups() @app.put("/v1/config/revert", tags=["config"]) @@ -509,14 +522,8 @@ def fastapi_config_revert_put( Returns: configuration (ConfigEOS): The current configuration after revert. """ - try: - get_config().revert_settings(backup_id) - return get_config() - except Exception as e: - raise HTTPException( - status_code=400, - detail=f"Error on reverting of configuration: {e}", - ) + get_config().revert_settings(backup_id) + return get_config() @app.put("/v1/config/file", tags=["config"]) @@ -526,14 +533,8 @@ def fastapi_config_file_put() -> ConfigEOS: Returns: configuration (ConfigEOS): The current configuration that was saved. """ - try: - get_config().to_config_file() - return get_config() - except Exception as e: - raise HTTPException( - status_code=404, - detail=f"Cannot save configuration to file '{get_config().config_file_path}': {e}", - ) + get_config().to_config_file() + return get_config() @app.get("/v1/config", tags=["config"]) @@ -543,10 +544,7 @@ def fastapi_config_get() -> ConfigEOS: Returns: configuration (ConfigEOS): The current configuration. """ - try: - return get_config() - except Exception as e: - raise HTTPException(status_code=400, detail=f"Error on configuration retrieval: {e}") + return get_config() @app.put("/v1/config", tags=["config"]) @@ -567,11 +565,12 @@ def fastapi_config_put(settings: SettingsEOS) -> ConfigEOS: get_config().merge_settings(settings) return get_config() except Exception as e: - trace = "".join(traceback.TracebackException.from_exception(e).format()) - raise HTTPException( - status_code=400, - detail=f"Error on update of configuration '{settings}':\n{e}\n{trace}", - ) + raise EOSProblem( + status=400, + title="Error on update of configuration", + detail=f"{str(e)}, {str(settings)}", + cause=e, + ) from e @app.put("/v1/config/{path:path}", tags=["config"]) @@ -596,11 +595,12 @@ def fastapi_config_put_key( get_config().set_nested_value(path, value) return get_config() except Exception as e: - trace = "".join(traceback.TracebackException.from_exception(e).format()) - raise HTTPException( - status_code=400, - detail=f"Error on update of configuration '{path}','{value}':\n{e}\n{trace}", - ) + raise EOSProblem( + status=400, + title="Error on update of configuration", + detail=f"{str(e)},'{path}','{value}'", + cause=e, + ) from e @app.get("/v1/config/{path:path}", tags=["config"]) @@ -619,12 +619,20 @@ def fastapi_config_get_key( """ try: return get_config().get_nested_value(path) - except IndexError as e: - raise HTTPException(status_code=400, detail=str(e)) except KeyError as e: - raise HTTPException(status_code=404, detail=str(e)) + raise EOSProblem( + status=404, + title="Error on config value retrieval", + detail=f"{str(e)},'{path}', ", + cause=e, + ) from e except Exception as e: - raise HTTPException(status_code=400, detail=str(e)) + raise EOSProblem( + status=400, + title="Error on config value retrieval", + detail=f"{str(e)},'{path}', ", + cause=e, + ) from e @app.get("/v1/logging/log", tags=["logging"]) @@ -669,8 +677,13 @@ async def fastapi_logging_get_log( tail=tail, ) return JSONResponse(content=logs) - except Exception as e: - return JSONResponse(content={"error": str(e)}, status_code=500) + except ValueError as e: + raise EOSProblem( + status=400, + title="Error on log retrieval", + detail=str(e), + cause=e, + ) from e @app.get("/v1/resource/status", tags=["resource"]) @@ -719,41 +732,167 @@ def fastapi_devices_status_put( @app.get("/v1/measurement/keys", tags=["measurement"]) def fastapi_measurement_keys_get() -> list[str]: - try: - """Get a list of available measurement keys.""" - return sorted(get_measurement().record_keys) - except Exception as e: - # Log unexpected errors - trace = "".join(traceback.TracebackException.from_exception(e).format()) - logger.exception("Unexpected error retieving measurement keys") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Internal server error:\n{e}\n{trace}", - ) + """Get a list of available measurement keys.""" + return sorted(get_measurement().record_keys) @app.get("/v1/measurement/series", tags=["measurement"]) async def fastapi_measurement_series_get( key: Annotated[str, Query(description="Measurement key.")], + start_datetime: Annotated[ + Optional[str], + Query(description="Starting datetime (inclusive)."), + ] = None, + end_datetime: Annotated[ + Optional[str], + Query(description="Ending datetime (exclusive)."), + ] = None, + interval: Annotated[ + Optional[str], + Query(description="Time duration for each interval. Defaults to 1 hour."), + ] = None, + fill_method: Annotated[ + Optional[FillMethod], + Query(description="Method to handle missing values during resampling."), + ] = None, + resample_method: Annotated[ + ResampleMethod, + Query(description="Method used to aggregate values within a resampling interval."), + ] = "mean", + dropna: Annotated[ + bool, + Query(description="Drop NAN/ None values before processing."), + ] = True, + boundary: Annotated[ + BoundaryMode, + Query(description="Resampling boundary mode."), + ] = "context", + align_to_interval: Annotated[ + bool, + Query( + description="Snap resample origin to the nearest UTC epoch-aligned boundary of interval." + ), + ] = False, + processing: Annotated[ + SeriesProcessing, + Query( + description="Processing mode. 'raw' returns original measurement data without resampling or filling." + ), + ] = SeriesProcessing.RAW, ) -> PydanticDateTimeSeries: - """Get the measurements of given key as series.""" - try: - if key not in get_measurement().record_keys: - raise HTTPException(status_code=404, detail=f"Key '{key}' is not available.") - pdseries = await get_measurement().key_to_series(key=key) - return PydanticDateTimeSeries.from_series(pdseries) - except HTTPException: - # Re-raise HTTP exceptions - raise - except Exception as e: - # Log unexpected errors - trace = "".join(traceback.TracebackException.from_exception(e).format()) - logger.exception(f"Unexpected error retieving measurement: {key}") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Internal server error:\n{e}\n{trace}", + """Get measurements for given key within given date range as series. + + Args: + key (str): Measurement key + start_datetime (Optional[str]): Starting datetime (inclusive). + Defaults to datetime of first measurement. + end_datetime (Optional[str]: Ending datetime (exclusive). + Defaults to datetime after latest measurement. + interval (Optional[str]): Time duration for each interval. + Defaults to 1 hour. + fill_method (str): Method to handle missing values during resampling. + + - 'linear': Linearly interpolate missing values (for numeric data only). + - 'time': Interpolate missing values (for numeric data only). + - 'ffill': Forward fill missing values. + - 'bfill': Backward fill missing values. + - Defaults to 'linear' for numeric values, otherwise 'ffill'. + + resample_method (str): + Method used to aggregate values within a resampling interval. + + - "first": Use the first value in each interval. + - "mean": Compute the arithmetic mean of all samples in each interval. + - "interval_mean": Compute the time-weighted mean assuming each + value remains valid until the next timestamp (piecewise-constant + signal). + + dropna: (bool): Whether to drop NAN/ None values before processing. + Defaults to True. + boundary (Literal["strict", "context"]): resampling boundary + "strict" → only values inside [start, end) + "context" → include one value before and after for proper resampling + align_to_interval (bool): When True, snap the resample origin to the nearest + UTC epoch-aligned boundary of ``interval`` before resampling. This ensures + that bucket timestamps always fall on wall-clock-round times regardless of + when ``start_datetime`` falls: + + - 15-minute interval → buckets on :00, :15, :30, :45 + - 1-hour interval → buckets on the hour + + When False (default), the origin is ``query_start`` (or ``"start_day"`` when + no start is given), preserving the existing behaviour where buckets are + aligned to the query window rather than the clock. + + Set to True when storing compacted records back to the database so that the + resulting timestamps are predictable and human-readable. Leave False for + forecast or reporting queries where alignment to the exact query window is + more important than clock-round boundaries. + processing (SeriesProcessing): + Processing mode for the returned series. + + - ``SeriesProcessing.RESAMPLED``: Return a processed series. + Measurements are first filtered by ``start_datetime``, + ``end_datetime``, and ``dropna``, then resampled according to + ``interval`` and ``resample_method``, and finally missing values + are filled using ``fill_method``. + - ``SeriesProcessing.RAW``: Return the original measurement series. + Measurements are filtered by ``start_datetime``, + ``end_datetime``, and ``dropna`` only. No resampling or filling is + performed, and ``interval``, ``fill_method``, + ``resample_method``, ``boundary``, and + ``align_to_interval`` are ignored. + + Defaults to ``SeriesProcessing.RAW``. + + Returns: + Series + """ + if key not in get_measurement().record_keys: + raise EOSProblem( + status=404, + title="Measurement series retrieval failed", + detail=f"Key '{key}' not found in measurementss", ) + try: + if processing == SeriesProcessing.RAW: + pdseries = await get_measurement().key_to_raw_series( + key=key, + start_datetime=start_datetime, + end_datetime=end_datetime, + dropna=dropna, + ) + else: + pdseries = await get_measurement().key_to_series( + key=key, + start_datetime=start_datetime, + end_datetime=end_datetime, + interval=interval, + fill_method=fill_method, + resample_method=resample_method, + dropna=dropna, + boundary=boundary, + align_to_interval=align_to_interval, + ) + return PydanticDateTimeSeries.from_series(pdseries) + except KeyError as e: + raise EOSProblem( + status=404, + title="Measurement series retrieval failed", + detail=str(e), + cause=e, + ) from e + except (TypeError, ValueError) as e: + raise EOSProblem( + status=400, + title="Measurement series retrieval failed", + detail=str(e), + cause=e, + ) from e + + return PydanticDateTimeSeries.from_series(pdseries) + @app.put("/v1/measurement/value", tags=["measurement"]) async def fastapi_measurement_value_put( @@ -762,67 +901,86 @@ async def fastapi_measurement_value_put( value: Union[float | str], ) -> PydanticDateTimeSeries: """Merge the measurement of given key and value into EOS measurements at given datetime.""" - try: - if isinstance(value, str): - try: - value = float(value) - except ValueError: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Value '{value}' cannot be converted to float", - ) - - if key not in get_measurement().record_keys: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Key '{key}' not found in measurements", - ) - + if isinstance(value, str): try: - dt = to_datetime(datetime) + value = float(value) except Exception as e: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Invalid datetime '{datetime}': {e}", - ) + raise EOSProblem( + status=400, + title="Measurement value merge failed", + detail=f"Value '{value}' cannot be converted to float", + cause=e, + ) from e - await get_measurement().update_value(dt, key, value) - pdseries = await get_measurement().key_to_series(key=key) - return PydanticDateTimeSeries.from_series(pdseries) - except HTTPException: - raise - except Exception as e: - trace = "".join(traceback.TracebackException.from_exception(e).format()) - logger.exception(f"Unexpected error updating measurement: {datetime}, {key}, {value}") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Internal server error:\n{e}\n{trace}", + if key not in get_measurement().record_keys: + raise EOSProblem( + status=404, + title="Measurement value merge failed", + detail=f"Key '{key}' not found in measurements", ) + try: + dt = to_datetime(datetime) + except Exception as e: + raise EOSProblem( + status=400, + title="Measurement value merge failed", + detail=f"Invalid datetime '{datetime}'", + cause=e, + ) from e + + try: + await get_measurement().update_value(dt, key, value) + pdseries = await get_measurement().key_to_raw_series(key=key) + except KeyError as e: + raise EOSProblem( + status=400, + title="Measurement value merge failed", + detail=str(e), + cause=e, + ) from e + except (TypeError, ValueError) as e: + raise EOSProblem( + status=400, + title="Measurement value merge failed", + detail=str(e), + cause=e, + ) from e + + return PydanticDateTimeSeries.from_series(pdseries) + @app.put("/v1/measurement/series", tags=["measurement"]) async def fastapi_measurement_series_put( key: Annotated[str, Query(description="Measurement key.")], series: PydanticDateTimeSeries ) -> PydanticDateTimeSeries: """Merge measurement given as series into given key.""" + if key not in get_measurement().record_keys: + raise EOSProblem( + status=404, + title="Measurement series merge failed", + detail=f"Key '{key}' not found in measurements", + ) + try: - if key not in get_measurement().record_keys: - raise HTTPException(status_code=404, detail=f"Key '{key}' is not available.") pdseries = series.to_series() # make pandas series from PydanticDateTimeSeries await get_measurement().key_from_series(key=key, series=pdseries) - pdseries = await get_measurement().key_to_series(key=key) + pdseries = await get_measurement().key_to_raw_series(key=key) return PydanticDateTimeSeries.from_series(pdseries) - except HTTPException: - # Re-raise HTTP exceptions - raise - except Exception as e: - # Log unexpected errors - trace = "".join(traceback.TracebackException.from_exception(e).format()) - logger.exception(f"Unexpected error updating measurement: {key}, {series}") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Internal server error:\n{e}\n{trace}", - ) + except KeyError as e: + raise EOSProblem( + status=404, + title="Measurement series merge failed", + detail=str(e), + cause=e, + ) from e + except (TypeError, ValueError) as e: + raise EOSProblem( + status=400, + title="Measurement series merge failed", + detail=str(e), + cause=e, + ) from e @app.put("/v1/measurement/dataframe", tags=["measurement"]) @@ -830,15 +988,30 @@ async def fastapi_measurement_dataframe_put(data: PydanticDateTimeDataFrame) -> """Merge the measurement data given as dataframe into EOS measurements.""" try: dataframe = data.to_dataframe() - await get_measurement().import_from_dataframe(dataframe) except Exception as e: - # Log unexpected errors - trace = "".join(traceback.TracebackException.from_exception(e).format()) - logger.exception(f"Unexpected error updating measurement: {data}") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Internal server error:\n{e}\n{trace}", - ) + raise EOSProblem( + status=400, + title="Measurement dataframe merge failed", + detail=str(e), + cause=e, + ) from e + + try: + await get_measurement().import_from_dataframe(dataframe) + except KeyError as e: + raise EOSProblem( + status=404, + title="Measurement dataframe merge failed", + detail=str(e), + cause=e, + ) from e + except (TypeError, ValueError) as e: + raise EOSProblem( + status=400, + title="Measurement dataframe merge failed", + detail=str(e), + cause=e, + ) from e @app.put("/v1/measurement/data", tags=["measurement"]) @@ -846,15 +1019,30 @@ async def fastapi_measurement_data_put(data: PydanticDateTimeData) -> None: """Merge the measurement data given as datetime data into EOS measurements.""" try: datetimedata = data.to_dict() - await get_measurement().import_from_dict(datetimedata) except Exception as e: - # Log unexpected errors - trace = "".join(traceback.TracebackException.from_exception(e).format()) - logger.exception(f"Unexpected error updating measurement: {data}") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Internal server error:\n{e}\n{trace}", - ) + raise EOSProblem( + status=400, + title="Measurement data merge failed", + detail=str(e), + cause=e, + ) from e + + try: + await get_measurement().import_from_dict(datetimedata) + except KeyError as e: + raise EOSProblem( + status=404, + title="Measurement data merge failed", + detail=str(e), + cause=e, + ) from e + except (TypeError, ValueError) as e: + raise EOSProblem( + status=400, + title="Measurement data merge failed", + detail=str(e), + cause=e, + ) from e @app.delete("/v1/measurement/range", tags=["measurement"]) @@ -864,40 +1052,56 @@ async def fastapi_measurement_range_delete( end_datetime: Annotated[Optional[str], Query(description="End datetime.")] = None, ) -> PydanticDateTimeSeries: """Delete measurement values for a key within a datetime range.""" + if key not in get_measurement().record_keys: + raise EOSProblem( + status=404, + title="Measurement value delete range failed", + detail=f"Key '{key}' not found in measurements", + ) + try: - if key not in get_measurement().record_keys: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Key '{key}' not found in measurements", - ) + start_dt = to_datetime(start_datetime) if start_datetime else None + except Exception as e: + raise EOSProblem( + status=400, + title="Measurement value delete range failed", + detail=f"Invalid start datetime '{start_datetime}'", + cause=e, + ) from e - try: - start_dt = to_datetime(start_datetime) if start_datetime else None - end_dt = to_datetime(end_datetime) if end_datetime else None - except Exception as e: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Invalid datetime: {e}", - ) + try: + end_dt = to_datetime(end_datetime) if end_datetime else None + except Exception as e: + raise EOSProblem( + status=400, + title="Measurement value delete range failed", + detail=f"Invalid end datetime '{end_datetime}'", + cause=e, + ) from e + try: await get_measurement().key_delete_by_datetime( key=key, start_datetime=start_dt, end_datetime=end_dt, ) - pdseries = await get_measurement().key_to_series(key=key) + pdseries = await get_measurement().key_to_raw_series(key=key) return PydanticDateTimeSeries.from_series(pdseries) - - except HTTPException: - raise - except Exception as e: - trace = "".join(traceback.TracebackException.from_exception(e).format()) - logger.exception(f"Unexpected error deleting measurement range: {key}") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Internal server error:\n{e}\n{trace}", - ) + except KeyError as e: + raise EOSProblem( + status=404, + title="Measurement value delete range failed", + detail=str(e), + cause=e, + ) from e + except (TypeError, ValueError) as e: + raise EOSProblem( + status=400, + title="Measurement value delete range failed", + detail=str(e), + cause=e, + ) from e @app.get("/v1/prediction/providers", tags=["prediction"]) @@ -937,6 +1141,38 @@ async def fastapi_prediction_series_get( Optional[str], Query(description="Ending datetime (exclusive)."), ] = None, + interval: Annotated[ + Optional[str], + Query(description="Time duration for each interval. Defaults to 1 hour."), + ] = None, + fill_method: Annotated[ + Optional[FillMethod], + Query(description="Method to handle missing values during resampling."), + ] = None, + resample_method: Annotated[ + ResampleMethod, + Query(description="Method used to aggregate values within a resampling interval."), + ] = "mean", + dropna: Annotated[ + bool, + Query(description="Drop NAN/ None values before processing."), + ] = True, + boundary: Annotated[ + BoundaryMode, + Query(description="Resampling boundary mode."), + ] = "context", + align_to_interval: Annotated[ + bool, + Query( + description="Snap resample origin to the nearest UTC epoch-aligned boundary of interval." + ), + ] = False, + processing: Annotated[ + SeriesProcessing, + Query( + description="Processing mode. 'raw' returns original measurement data without resampling or filling." + ), + ] = SeriesProcessing.RAW, ) -> PydanticDateTimeSeries: """Get prediction for given key within given date range as series. @@ -946,21 +1182,147 @@ async def fastapi_prediction_series_get( Defaults to start datetime of latest prediction. end_datetime (Optional[str]: Ending datetime (exclusive). Defaults to end datetime of latest prediction. + interval (Optional[str]): Time duration for each interval. + Defaults to 1 hour. + fill_method (str): Method to handle missing values during resampling. + + - 'linear': Linearly interpolate missing values (for numeric data only). + - 'time': Interpolate missing values (for numeric data only). + - 'ffill': Forward fill missing values. + - 'bfill': Backward fill missing values. + - Defaults to 'linear' for numeric values, otherwise 'ffill'. + + resample_method (str): + Method used to aggregate values within a resampling interval. + + - "first": Use the first value in each interval. + - "mean": Compute the arithmetic mean of all samples in each interval. + - "interval_mean": Compute the time-weighted mean assuming each + value remains valid until the next timestamp (piecewise-constant + signal). + + dropna: (bool): Whether to drop NAN/ None values before processing. + Defaults to True. + boundary (Literal["strict", "context"]): resampling boundary + "strict" → only values inside [start, end) + "context" → include one value before and after for proper resampling + align_to_interval (bool): When True, snap the resample origin to the nearest + UTC epoch-aligned boundary of ``interval`` before resampling. This ensures + that bucket timestamps always fall on wall-clock-round times regardless of + when ``start_datetime`` falls: + + - 15-minute interval → buckets on :00, :15, :30, :45 + - 1-hour interval → buckets on the hour + + When False (default), the origin is ``query_start`` (or ``"start_day"`` when + no start is given), preserving the existing behaviour where buckets are + aligned to the query window rather than the clock. + + Set to True when storing compacted records back to the database so that the + resulting timestamps are predictable and human-readable. Leave False for + forecast or reporting queries where alignment to the exact query window is + more important than clock-round boundaries. + processing (SeriesProcessing): + Processing mode for the returned series. + + - ``SeriesProcessing.RESAMPLED``: Return a processed series. + Measurements are first filtered by ``start_datetime``, + ``end_datetime``, and ``dropna``, then resampled according to + ``interval`` and ``resample_method``, and finally missing values + are filled using ``fill_method``. + - ``SeriesProcessing.RAW``: Return the original measurement series. + Measurements are filtered by ``start_datetime``, + ``end_datetime``, and ``dropna`` only. No resampling or filling is + performed, and ``interval``, ``fill_method``, + ``resample_method``, ``boundary``, and + ``align_to_interval`` are ignored. + + Defaults to ``SeriesProcessing.RAW``. + + Returns: + Array """ if key not in get_prediction().record_keys: - raise HTTPException(status_code=404, detail=f"Key '{key}' is not available.") + raise EOSProblem( + status=404, + title="Prediction datetime range series retrieval failed", + detail=f"Key '{key}' not found in predictions", + ) + if start_datetime is None: start_datetime = get_prediction().ems_start_datetime else: - start_datetime = to_datetime(start_datetime) + try: + start_datetime = to_datetime(start_datetime) + except Exception as e: + raise EOSProblem( + status=400, + title="Prediction datetime range series retrieval failed", + detail=f"Invalid start datetime '{start_datetime}'", + cause=e, + ) from e + if end_datetime is None: end_datetime = get_prediction().end_datetime else: - end_datetime = to_datetime(end_datetime) - pdseries = await get_prediction().key_to_series( - key=key, start_datetime=start_datetime, end_datetime=end_datetime - ) - return PydanticDateTimeSeries.from_series(pdseries) + try: + end_datetime = to_datetime(end_datetime) + except Exception as e: + raise EOSProblem( + status=400, + title="Prediction datetime range series retrieval failed", + detail=f"Invalid end datetime '{end_datetime}'", + cause=e, + ) from e + + if interval is None: + interval = to_duration("1 hour") + else: + try: + interval = to_duration(interval) + except Exception as e: + raise EOSProblem( + status=400, + title="Prediction datetime range series retrieval failed", + detail=f"Invalid interval '{interval}'", + cause=e, + ) from e + + try: + if processing == SeriesProcessing.RAW: + pdseries = await get_prediction().key_to_raw_series( + key=key, + start_datetime=start_datetime, + end_datetime=end_datetime, + dropna=dropna, + ) + else: + pdseries = await get_prediction().key_to_series( + key=key, + start_datetime=start_datetime, + end_datetime=end_datetime, + interval=interval, + fill_method=fill_method, + resample_method=resample_method, + dropna=dropna, + boundary=boundary, + align_to_interval=align_to_interval, + ) + return PydanticDateTimeSeries.from_series(pdseries) + except KeyError as e: + raise EOSProblem( + status=404, + title="Prediction datetime range series retrieval failed", + detail=str(e), + cause=e, + ) from e + except (TypeError, ValueError) as e: + raise EOSProblem( + status=400, + title="Prediction datetime range series retrieval failed", + detail=str(e), + cause=e, + ) from e @app.get("/v1/prediction/dataframe", tags=["prediction"]) @@ -987,9 +1349,9 @@ async def fastapi_prediction_dataframe_get( Query(description="Method used to aggregate values within a resampling interval."), ] = "mean", dropna: Annotated[ - Optional[bool], + bool, Query(description="Drop NAN/ None values before processing."), - ] = None, + ] = True, boundary: Annotated[ BoundaryMode, Query(description="Resampling boundary mode."), @@ -1028,7 +1390,7 @@ async def fastapi_prediction_dataframe_get( value remains valid until the next timestamp (piecewise-constant signal). - dropna: (bool, optional): Whether to drop NAN/ None values before processing. + dropna: (bool): Whether to drop NAN/ None values before processing. Defaults to True. boundary (Literal["strict", "context"]): resampling boundary "strict" → only values inside [start, end) @@ -1106,9 +1468,9 @@ async def fastapi_prediction_list_get( Query(description="Method used to aggregate values within a resampling interval."), ] = "mean", dropna: Annotated[ - Optional[bool], + bool, Query(description="Drop NAN/ None values before processing."), - ] = None, + ] = True, boundary: Annotated[ BoundaryMode, Query(description="Resampling boundary mode."), @@ -1147,7 +1509,7 @@ async def fastapi_prediction_list_get( value remains valid until the next timestamp (piecewise-constant signal). - dropna: (bool, optional): Whether to drop NAN/ None values before processing. + dropna: (bool): Whether to drop NAN/ None values before processing. Defaults to True. boundary (Literal["strict", "context"]): resampling boundary "strict" → only values inside [start, end) @@ -1170,19 +1532,50 @@ async def fastapi_prediction_list_get( more important than clock-round boundaries. """ if key not in get_prediction().record_keys: - raise HTTPException(status_code=404, detail=f"Key '{key}' is not available.") + raise EOSProblem( + status=404, + title="Prediction list retrieval failed", + detail=f"Key '{key}' not found in predictions", + ) + if start_datetime is None: start_datetime = get_prediction().ems_start_datetime else: - start_datetime = to_datetime(start_datetime) + try: + start_datetime = to_datetime(start_datetime) + except Exception as e: + raise EOSProblem( + status=400, + title="Prediction list retrieval failed", + detail=f"Invalid start datetime '{start_datetime}'", + cause=e, + ) from e + if end_datetime is None: end_datetime = get_prediction().end_datetime else: - end_datetime = to_datetime(end_datetime) + try: + end_datetime = to_datetime(end_datetime) + except Exception as e: + raise EOSProblem( + status=400, + title="Prediction list retrieval failed", + detail=f"Invalid end datetime '{end_datetime}'", + cause=e, + ) from e + if interval is None: interval = to_duration("1 hour") else: - interval = to_duration(interval) + try: + interval = to_duration(interval) + except Exception as e: + raise EOSProblem( + status=400, + title="Prediction list retrieval failed", + detail=f"Invalid interval '{interval}'", + cause=e, + ) from e try: prediction_array = await get_prediction().key_to_array( @@ -1196,11 +1589,21 @@ async def fastapi_prediction_list_get( boundary=boundary, align_to_interval=align_to_interval, ) - prediction_list = prediction_array.tolist() - except Exception as e: - raise HTTPException(status_code=400, detail=f"Error on prediction list for '{key}': {e}") - - return prediction_list + return prediction_array.tolist() + except KeyError as e: + raise EOSProblem( + status=404, + title="Prediction list retrieval failed", + detail=str(e), + cause=e, + ) from e + except (TypeError, ValueError) as e: + raise EOSProblem( + status=400, + title="Prediction list retrieval failed", + detail=str(e), + cause=e, + ) from e @app.put("/v1/prediction/import/{provider_id}", tags=["prediction"]) @@ -1219,21 +1622,37 @@ async def fastapi_prediction_import_provider( """ try: provider = get_prediction().provider_by_id(provider_id) - except ValueError: - raise HTTPException(status_code=404, detail=f"Provider '{provider_id}' not found.") + except Exception as e: + raise EOSProblem( + status=404, + title="Prediction import failed", + detail=f"Provider '{provider_id}' not found.", + cause=e, + ) from e + if not provider.enabled() and not force_enable: - raise HTTPException(status_code=404, detail=f"Provider '{provider_id}' not enabled.") + raise EOSProblem( + status=400, + title="Prediction import failed", + detail=f"Provider '{provider_id}' not enabled.", + ) + try: if isinstance(data, BaseModel): json_str = data.model_dump_json() else: json_str = json.dumps(data) - await provider.import_from_json(json_str=json_str) - provider.update_datetime = to_datetime(in_timezone=get_config().general.timezone) except Exception as e: - raise HTTPException( - status_code=400, detail=f"Error on import for provider '{provider_id}': {e}" - ) + raise EOSProblem( + status=400, + title="Prediction import failed", + detail=str(e), + cause=e, + ) from e + + await provider.import_from_json(json_str=json_str) + provider.update_datetime = to_datetime(in_timezone=get_config().general.timezone) + return Response() @@ -1250,18 +1669,11 @@ async def fastapi_prediction_update( Defaults to False. """ # Ensure there is only one optimization/ energy management run at a time - try: - await get_ems().run( - mode=EnergyManagementMode.PREDICTION, - force_update=force_update, - force_enable=force_enable, - ) - except Exception as e: - trace = "".join(traceback.TracebackException.from_exception(e).format()) - raise HTTPException( - status_code=400, - detail=f"Error on prediction update:\n{e}\n{trace}", - ) + await get_ems().run( + mode=EnergyManagementMode.PREDICTION, + force_update=force_update, + force_enable=force_enable, + ) return Response() @@ -1281,22 +1693,20 @@ async def fastapi_prediction_update_provider( """ try: provider = get_prediction().provider_by_id(provider_id) - except ValueError: - raise HTTPException(status_code=404, detail=f"Provider '{provider_id}' not found.") + except Exception as e: + raise EOSProblem( + status=404, + title="Prediction update failed", + detail=f"Provider '{provider_id}' not found.", + cause=e, + ) from e # Ensure there is only one optimization/ energy management run at a time - try: - await get_ems().run( - mode=EnergyManagementMode.PREDICTION, - force_update=force_update, - force_enable=force_enable, - ) - except Exception as e: - trace = "".join(traceback.TracebackException.from_exception(e).format()) - raise HTTPException( - status_code=400, - detail=f"Error on prediction update:\n{e}\n{trace}", - ) + await get_ems().run( + mode=EnergyManagementMode.PREDICTION, + force_update=force_update, + force_enable=force_enable, + ) return Response() @@ -1308,55 +1718,73 @@ async def fastapi_prediction_range_delete( end_datetime: Annotated[Optional[str], Query(description="End datetime.")] = None, ) -> PydanticDateTimeSeries: """Delete prediction values for a key within a datetime range.""" - try: - if key not in get_prediction().record_keys: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Key '{key}' not found in predictions", - ) - - try: - start_dt = to_datetime(start_datetime) if start_datetime else None - end_dt = to_datetime(end_datetime) if end_datetime else None - except Exception as e: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail=f"Invalid datetime: {e}", - ) - - try: - await get_prediction().key_delete_by_datetime( - key=key, - start_datetime=start_dt, - end_datetime=end_dt, - ) - except KeyError: - # No data for key in predictions - pass - - pdseries = await get_prediction().key_to_series(key=key) - return PydanticDateTimeSeries.from_series(pdseries) - - except HTTPException: - raise - except Exception as e: - trace = "".join(traceback.TracebackException.from_exception(e).format()) - logger.exception(f"Unexpected error deleting prediction range: {key}") - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Internal server error:\n{e}\n{trace}", + if key not in get_prediction().record_keys: + raise EOSProblem( + status=404, + title="Prediction value delete range failed", + detail=f"Key '{key}' not found in predictions", ) + try: + start_dt = to_datetime(start_datetime) if start_datetime else None + except Exception as e: + raise EOSProblem( + status=400, + title="Prediction value delete range failed", + detail=f"Invalid start datetime '{start_datetime}'", + cause=e, + ) from e + + try: + end_dt = to_datetime(end_datetime) if end_datetime else None + except Exception as e: + raise EOSProblem( + status=400, + title="Prediction value delete range failed", + detail=f"Invalid end datetime '{end_datetime}'", + cause=e, + ) from e + + try: + await get_prediction().key_delete_by_datetime( + key=key, + start_datetime=start_dt, + end_datetime=end_dt, + ) + + pdseries = await get_prediction().key_to_raw_series(key=key) + return PydanticDateTimeSeries.from_series(pdseries) + except KeyError as e: + raise EOSProblem( + status=404, + title="Prediction value delete range failed", + detail=str(e), + cause=e, + ) from e + except (TypeError, ValueError) as e: + raise EOSProblem( + status=400, + title="Prediction value delete range failed", + detail=str(e), + cause=e, + ) from e + @app.get("/v1/energy-management/optimization/solution", tags=["energy-management"]) def fastapi_energy_management_optimization_solution_get() -> OptimizationSolution: """Get the latest solution of the optimization.""" solution = get_ems().optimization_solution() if solution is None: - raise HTTPException( - status_code=404, - detail="Can not get the optimization solution.\nDid you configure automatic optimization?", + raise EOSProblem( + status=404, + title="Optimization solution retrieval failed", + detail=( + "Can not get the optimization solution.\n" + "Did you configure automatic optimization?\n" + "Did you run the optimization/ energy management?" + ), ) + return solution @@ -1369,27 +1797,33 @@ async def fastapi_energy_management_optimization_solution_algorithm_get( Args: algorithm: Optimization algorithm """ + solution: Optional[Union[GeneticSolution, Genetic0Solution]] = None + algorithm = algorithm.upper() if algorithm not in get_config().optimization.algorithms: - raise HTTPException( - status_code=404, detail=f"Optimization algorithm '{algorithm}' unknown." + raise EOSProblem( + status=404, + title="Algorithm specific optimization solution retrieval failed", + detail=f"Optimization algorithm '{algorithm}' unknown.", ) + if algorithm == "GENETIC": - genetic_solution = get_ems().genetic_solution() - if genetic_solution is None: - raise HTTPException( - status_code=404, detail=f"'{algorithm}' optimization solution not available." - ) - return genetic_solution - if algorithm == "GENETIC0": - genetic0_solution = get_ems().genetic0_solution() - if genetic0_solution is None: - raise HTTPException( - status_code=404, detail=f"'{algorithm}' optimization solution not available." - ) - return genetic0_solution - # Should never happen - raise HTTPException(status_code=500, detail=f"'{algorithm}' validated but not handled.") + solution = get_ems().genetic_solution() + elif algorithm == "GENETIC0": + solution = get_ems().genetic0_solution() + + if solution is None: + raise EOSProblem( + status=404, + title="Algorithm specific optimization solution retrieval failed", + detail=( + f"Can not get the '{algorithm}' optimization solution.\n" + f"Did you configure automatic '{algorithm}' optimization?\n" + "Did you run the optimization/ energy management?" + ), + ) + + return solution @app.get("/v1/energy-management/plan", tags=["energy-management"]) @@ -1397,10 +1831,16 @@ def fastapi_energy_management_plan_get() -> EnergyManagementPlan: """Get the latest energy management plan.""" plan = get_ems().plan() if plan is None: - raise HTTPException( - status_code=404, - detail="Can not get the energy management plan.\nDid you configure automatic optimization?", + raise EOSProblem( + status=404, + title="Optimization plan retrieval failed", + detail=( + "Can not get the optimization plan.\n" + "Did you configure automatic optimization?\n" + "Did you run the optimization/ energy management?" + ), ) + return plan @@ -1430,37 +1870,24 @@ async def fastapi_strompreis() -> list[float]: get_config().merge_settings(settings=settings) # Ensure there is only one optimization/ energy management run at a time - try: - await get_ems().run( - mode=EnergyManagementMode.PREDICTION, - force_update=True, - ) - except Exception as e: - raise HTTPException( - status_code=404, - detail=f"Can not update predictions: {e}", - ) + await get_ems().run( + mode=EnergyManagementMode.PREDICTION, + force_update=True, + ) + # Get the current date and the end date based on prediction hours # Fetch prices for the specified date range start_datetime = to_datetime().start_of("day") end_datetime = start_datetime.add(days=2) - try: - elecprice_array = await get_prediction().key_to_array( - key="elecprice_marketprice_wh", - start_datetime=start_datetime, - end_datetime=end_datetime, - interval=to_duration("1 hour"), - fill_method="ffill", - resample_method="interval_mean", - ) - elecprice_list = elecprice_array.tolist() - except Exception as e: - raise HTTPException( - status_code=404, - detail=f"Can not get the electricity price forecast: {e}.\nDid you configure the electricity price forecast provider?", - ) - - return elecprice_list + elecprice_array = await get_prediction().key_to_array( + key="elecprice_marketprice_wh", + start_datetime=start_datetime, + end_datetime=end_datetime, + interval=to_duration("1 hour"), + fill_method="ffill", + resample_method="interval_mean", + ) + return elecprice_array.tolist() class GesamtlastRequest(PydanticBaseModel): @@ -1515,11 +1942,21 @@ async def fastapi_gesamtlast(request: GesamtlastRequest) -> list[float]: dt_str = to_datetime(data_dict["time"], as_string=True) value = float(data_dict["Last"]) energy[dt_str] = value - except Exception as e: - raise HTTPException( - status_code=404, - detail=f"Invalid measured data: {e}.", - ) + except KeyError as e: + raise EOSProblem( + status=404, + title="Total load prediction failed", + detail=str(e), + cause=e, + ) from e + except (TypeError, ValueError) as e: + raise EOSProblem( + status=400, + title="Total load prediction failed", + detail=str(e), + cause=e, + ) from e + energy_mr_dates = [] energy_mr_values = [] energy_mr = 0.0 @@ -1536,39 +1973,26 @@ async def fastapi_gesamtlast(request: GesamtlastRequest) -> list[float]: await get_measurement().key_from_lists(measurement_key, energy_mr_dates, energy_mr_values) # Ensure there is only one optimization/ energy management run at a time - try: - await get_ems().run( - mode=EnergyManagementMode.PREDICTION, - force_update=True, - ) - except Exception as e: - raise HTTPException( - status_code=404, - detail=f"Can not update predictions: {e}", - ) + await get_ems().run( + mode=EnergyManagementMode.PREDICTION, + force_update=True, + ) # Get the forecast starting at start of day start_datetime = to_datetime().start_of("day") end_datetime = start_datetime.add(days=2) - try: - prediction_array = await get_prediction().key_to_array( - key="loadforecast_power_w", - start_datetime=start_datetime, - end_datetime=end_datetime, - interval=to_duration("1 hour"), - fill_method="ffill", - resample_method="interval_mean", - dropna=True, - boundary="context", - ) - prediction_list = prediction_array.tolist() - except Exception as e: - raise HTTPException( - status_code=404, - detail=f"Can not get the total load forecast: {e}.\nDid you configure the load forecast provider?", - ) + prediction_array = await get_prediction().key_to_array( + key="loadforecast_power_w", + start_datetime=start_datetime, + end_datetime=end_datetime, + interval=to_duration("1 hour"), + fill_method="ffill", + resample_method="interval_mean", + dropna=True, + boundary="context", + ) - return prediction_list + return prediction_array.tolist() @app.get("/gesamtlast_simple", tags=["prediction"], deprecated=True) @@ -1718,7 +2142,7 @@ async def fastapi_optimize( ngen: Annotated[ Optional[int], Query(description="Number of indivuals to generate for genetic algorithm.") ] = None, -) -> Genetic0Solution: +) -> Genetic0SolutionLegacy: """Deprecated: Optimize. Endpoint to handle optimization. @@ -1735,34 +2159,56 @@ async def fastapi_optimize( start_datetime = to_datetime().set(hour=start_hour) # Ensure there is only one optimization/ energy management run at a time - try: - await get_ems().run( - start_datetime=start_datetime, - mode=EnergyManagementMode.OPTIMIZATION, - algorithm="GENETIC0", - genetic0_parameters=parameters, - genetic0_generations=ngen, - ) - except Exception as e: - raise HTTPException(status_code=400, detail=f"Optimize error: {e}.") + await get_ems().run( + start_datetime=start_datetime, + mode=EnergyManagementMode.OPTIMIZATION, + algorithm="GENETIC0", + genetic0_parameters=parameters, + genetic0_generations=ngen, + ) solution = get_ems().genetic0_solution() if solution is None: - raise HTTPException(status_code=400, detail="Optimize error: no solution stored by run.") + raise EOSProblem( + status=404, + title="Optimization solution retrieval failed", + detail="Can not get the 'GENETIC0' optimization solution.", + ) - return solution + # Create compatible solution. + legacy_solution = Genetic0SolutionLegacy( + **{ + "ac_charge": solution.ac_charge, + "dc_charge": solution.dc_charge, + "discharge_allowed": solution.discharge_allowed, + "ev_charge_hours_float": solution.ev_charge_hours_float, + "result": solution.result, + "ev_obj": solution.ev_obj, + "start_solution": solution.start_solution, + "washingstart": solution.washingstart, + } + ) + return legacy_solution -@app.get("/visualization_results.pdf", response_class=PdfResponse, tags=["optimize"]) -def get_pdf() -> PdfResponse: +@app.get("/visualization_results.pdf", tags=["optimize"]) +def get_pdf() -> Response: # Endpoint to serve the generated PDF with visualization results - output_path = get_config().general.data_output_path - if output_path is None or not output_path.is_dir(): - raise HTTPException(status_code=404, detail=f"Output path does not exist: {output_path}.") - file_path = output_path / "visualization_results.pdf" - if not file_path.is_file(): - raise HTTPException(status_code=404, detail="No visualization result available.") - return PdfResponse(file_path) + genetic0_solution = get_ems().genetic0_solution() + if genetic0_solution is None: + raise EOSProblem( + status=404, + title="Optimization solution report retrieval failed", + detail="Can not get the 'GENETIC0' optimization solution.", + ) + + pdf = genetic0_prepare_visualize(solution=genetic0_solution) + + return Response( + content=pdf, + media_type="application/pdf", + headers={"Content-Disposition": 'inline; filename="optimization-report.pdf"'}, + ) @app.get("/site-map", include_in_schema=False) diff --git a/src/akkudoktoreos/server/rest/error.py b/src/akkudoktoreos/server/rest/error.py index a0320d58..e106c534 100644 --- a/src/akkudoktoreos/server/rest/error.py +++ b/src/akkudoktoreos/server/rest/error.py @@ -1,4 +1,109 @@ import html +import traceback +from dataclasses import dataclass + +from fastapi import FastAPI, Request +from fastapi.exceptions import HTTPException, RequestValidationError +from fastapi.responses import JSONResponse +from loguru import logger + +from akkudoktoreos.core.coreabc import get_config + + +@dataclass(slots=True) +class EOSProblem(Exception): + """Application exception returned as RFC 7807 Problem Details.""" + + status: int + title: str + detail: str + type: str = "about:blank" + cause: Exception | None = None + + def __str__(self) -> str: + return self.detail + + +def _problem_response( + *, + request: Request, + status: int, + title: str, + detail: str, + type: str, + cause: Exception | None = None, +) -> JSONResponse: + body = { + "type": type, + "title": title, + "status": status, + "detail": detail, + "instance": request.url.path, + } + + if cause is not None: + logger.exception(cause) + + if get_config().logging.api_level in ("TRACE", "DEBUG"): + body["traceback"] = traceback.format_exception(cause) + + return JSONResponse( + status_code=status, + media_type="application/problem+json", + content=body, + ) + + +async def eos_problem_handler(request: Request, exc: EOSProblem) -> JSONResponse: + return _problem_response( + request=request, + status=exc.status, + title=exc.title, + detail=exc.detail, + cause=exc.cause, + type=exc.type, + ) + + +async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse: + return _problem_response( + request=request, + status=exc.status_code, + title="HTTP Error", + detail=str(exc.detail), + cause=exc, + type="about:blank", + ) + + +async def unexpected_exception_handler(request: Request, exc: Exception) -> JSONResponse: + return _problem_response( + request=request, + status=500, + title="Internal Server Error", + detail=str(exc), + cause=exc, + type="/problems/internal-server-error", + ) + + +async def validation_handler(request: Request, exc: RequestValidationError) -> JSONResponse: + return _problem_response( + request=request, + status=422, + title="Validation Error", + detail="Request validation failed.", + cause=exc, + type="/problems/validation", + ) + + +def register_problem_handlers(app: FastAPI) -> None: + app.add_exception_handler(EOSProblem, eos_problem_handler) + app.add_exception_handler(HTTPException, http_exception_handler) + app.add_exception_handler(Exception, unexpected_exception_handler) + app.add_exception_handler(RequestValidationError, validation_handler) + ERROR_PAGE_TEMPLATE = """ diff --git a/tests/test_dataabcsequence.py b/tests/test_dataabcsequence.py index fe23aa62..34538896 100644 --- a/tests/test_dataabcsequence.py +++ b/tests/test_dataabcsequence.py @@ -180,11 +180,11 @@ class TestDataSequence: retrieved_record = await sequence.get_by_datetime(dt1) assert retrieved_record.data_value == 0.9 # Record should have merged with new value - async def test_key_to_series(self, sequence): + async def test_key_to_raw_series(self, sequence): dt = to_datetime(datetime(2023, 11, 6)) record = self.create_test_record(dt, 0.8) await sequence.insert_by_datetime(record) - series = await sequence.key_to_series("data_value") + series = await sequence.key_to_raw_series("data_value") assert isinstance(series, pd.Series) retrieved_record = await sequence.get_by_datetime(dt) @@ -228,7 +228,7 @@ class TestDataSequence: assert retrieved_record2 is not None assert retrieved_record2.data_value == 8.0 - series = await sequence.key_to_series( + series = await sequence.key_to_raw_series( key="data_value", start_datetime=start_datetime, end_datetime=end_datetime ) assert len(series) == 2 diff --git a/tests/test_databaseabc.py b/tests/test_databaseabc.py index 406db251..5dc571d4 100644 --- a/tests/test_databaseabc.py +++ b/tests/test_databaseabc.py @@ -265,7 +265,7 @@ class SampleSequence(DatabaseRecordProtocolMixin[SampleRecord]): interval: Optional[Duration] = None, fill_method: Optional[FillMethod] = None, resample_method: ResampleMethod = "mean", - dropna: Optional[bool] = True, + dropna: bool = True, boundary: BoundaryMode = "context", align_to_interval: bool = False, ) -> NDArray[Shape["*"], Any]: diff --git a/tests/test_elecpricetibber.py b/tests/test_elecpricetibber.py index 99873188..d297f9f0 100644 --- a/tests/test_elecpricetibber.py +++ b/tests/test_elecpricetibber.py @@ -393,7 +393,7 @@ class TestElecPriceTibber: assert forecast_call["forecast_slots"] == 22 # (a)+(c) Stored records keep the native 15-min grid across today and the forecast. - stored = await tibber_provider.key_to_series( + stored = await tibber_provider.key_to_raw_series( "elecprice_marketprice_wh", start_datetime=to_datetime("2026-07-09T00:00:00+00:00"), end_datetime=to_datetime("2026-07-09T06:15:00+00:00"), diff --git a/tests/test_feedintariffenergycharts.py b/tests/test_feedintariffenergycharts.py index 00fa7f25..c8786550 100644 --- a/tests/test_feedintariffenergycharts.py +++ b/tests/test_feedintariffenergycharts.py @@ -93,7 +93,7 @@ class TestFeedInTariffEnergyCharts: with patch.object(provider, "_request_forecast", return_value=energy_charts_data): await provider._update_data(force_update=True) - result = await provider.key_to_series( + result = await provider.key_to_raw_series( key="feed_in_tariff_wh", start_datetime=start, end_datetime=start.add(hours=provider.config.prediction.hours), diff --git a/tests/test_genetic0optimize.py b/tests/test_genetic0optimize.py index 067aa031..71710300 100644 --- a/tests/test_genetic0optimize.py +++ b/tests/test_genetic0optimize.py @@ -1,9 +1,12 @@ import json +from io import BytesIO from pathlib import Path from typing import Any from unittest.mock import patch import pytest +from pydantic import ValidationError +from pypdf import PdfReader from akkudoktoreos.config.config import ConfigEOS from akkudoktoreos.core.cache import CacheEnergyManagementStore @@ -13,14 +16,14 @@ from akkudoktoreos.optimization.genetic0.genetic0params import ( Genetic0OptimizationParameters, ) from akkudoktoreos.optimization.genetic0.genetic0solution import Genetic0Solution -from akkudoktoreos.utils.datetimeutil import to_datetime -from akkudoktoreos.utils.visualize import ( - prepare_visualize, # Import the new prepare_visualize +from akkudoktoreos.optimization.genetic0.genetic0visualize import ( + genetic0_prepare_visualize, ) +from akkudoktoreos.utils.datetimeutil import to_datetime ems_eos = get_ems(init=True) # init once -DIR_TESTDATA = Path(__file__).parent / "testdata" +DIR_TESTDATA = Path(__file__).parent / "testdata" / "genetic0" def compare_dict(actual: dict[str, Any], expected: dict[str, Any]): @@ -40,11 +43,11 @@ def compare_dict(actual: dict[str, Any], expected: dict[str, Any]): @pytest.mark.parametrize( "fn_in, fn_out, ngen, break_even", [ - ("genetic0optimize_input_1.json", "genetic0optimize_result_1.json", 3, 0), - ("genetic0optimize_input_2.json", "genetic0optimize_result_2.json", 3, 0), - ("genetic0optimize_input_2.json", "genetic0optimize_result_2_full.json", 400, 0), - ("genetic0optimize_input_1.json", "genetic0optimize_result_1_be.json", 3, 1), - ("genetic0optimize_input_2.json", "genetic0optimize_result_2_be.json", 3, 1), + ("optimize_input_1.json", "optimize_result_1.json", 3, 0), + ("optimize_input_2.json", "optimize_result_2.json", 3, 0), + ("optimize_input_2.json", "optimize_result_2_full.json", 400, 0), + ("optimize_input_1.json", "optimize_result_1_be.json", 3, 1), + ("optimize_input_2.json", "optimize_result_2_be.json", 3, 1), ], ) async def test_optimize( @@ -90,20 +93,10 @@ async def test_optimize( ) # Load input and output data - file = DIR_TESTDATA / fn_in - with file.open("r") as f_in: + parameter_file = DIR_TESTDATA / fn_in + with parameter_file.open("r") as f_in: input_data = Genetic0OptimizationParameters(**json.load(f_in)) - file = DIR_TESTDATA / fn_out - # In case a new test case is added, we don't want to fail here, so the new output is written - # to disk before - try: - with file.open("r") as f_out: - expected_data = json.load(f_out) - expected_result = Genetic0Solution(**expected_data) - except FileNotFoundError: - pass - # Fake energy management run start datetime ems_eos.set_start_datetime(to_datetime().set(hour=fixed_start_hour)) @@ -116,27 +109,37 @@ async def test_optimize( if ngen > 10 and not is_finalize: pytest.skip() - visualize_filename = str((DIR_TESTDATA / f"new_{fn_out}").with_suffix(".pdf")) - - with patch( - "akkudoktoreos.utils.visualize.prepare_visualize", - side_effect=lambda parameters, results, *args, **kwargs: prepare_visualize( - parameters, results, filename=visualize_filename, **kwargs - ), - ) as prepare_visualize_patch: - # Call the optimization function - genetic0_solution = genetic0_optimization.optimize_ems( - parameters=input_data, start_hour=fixed_start_hour, ngen=ngen - ) - # The function creates a visualization result PDF as a side-effect. - prepare_visualize_patch.assert_called_once() - assert Path(visualize_filename).exists() + # Call the optimization function + genetic0_solution = genetic0_optimization.optimize_ems( + parameters=input_data, start_hour=fixed_start_hour, ngen=ngen + ) # Write test output to file, so we can take it as new data on intended change TESTDATA_FILE = DIR_TESTDATA / f"new_{fn_out}" with TESTDATA_FILE.open("w", encoding="utf-8", newline="\n") as f_out: f_out.write(genetic0_solution.model_dump_json(indent=4, exclude_unset=True)) + solution_file = DIR_TESTDATA / fn_out + # In case a new test case is added, we don't want to fail here, so the new output is written + # to disk before + try: + with solution_file.open("r") as f_out: + expected_data = json.load(f_out) + expected_result = Genetic0Solution(**expected_data) + except ValidationError: + # Expected genetic solution data does not fit to Genetic0Solution data schema + # Possibly the Genetic0Solution class changed. + pytest.fail( + f"ValidationError: Can not load expected solution from {solution_file}\n" + f"cp {TESTDATA_FILE} {solution_file}\n" + ) + except FileNotFoundError: + # Should not happen + pytest.fail( + f"FileNotFoundError: Can not load expected solution from {solution_file}\n" + f"cp {TESTDATA_FILE} {solution_file}\n" + ) + assert genetic0_solution.result.Gesamtbilanz_Euro == pytest.approx( expected_result.result.Gesamtbilanz_Euro ) @@ -153,3 +156,16 @@ async def test_optimize( # Check the correct generic energy management plan is created plan = genetic0_solution.energy_management_plan() # @TODO + + # Check visualization works + pdf = genetic0_prepare_visualize( + solution=genetic0_solution, + ) + assert pdf.startswith(b"%PDF-") + + reader = PdfReader(BytesIO(pdf)) + assert len(reader.pages) == 6 + + + # Everything passed, remove generated files + TESTDATA_FILE.unlink() diff --git a/tests/test_genetic0visualize.py b/tests/test_genetic0visualize.py new file mode 100644 index 00000000..b833a103 --- /dev/null +++ b/tests/test_genetic0visualize.py @@ -0,0 +1,37 @@ +from pathlib import Path + +from matplotlib.testing.compare import compare_images + +from akkudoktoreos.optimization.genetic0.genetic0visualize import ( + genetic0_generate_example_report, +) + +filename = "example_report.pdf" + + +DIR_TESTDATA = Path(__file__).parent / "testdata" / "genetic0" +reference_file = DIR_TESTDATA / "test_example_report.pdf" +reference_png_file = DIR_TESTDATA / "test_example_report_pdf.png" +output_file = DIR_TESTDATA / "test_example_report_new.pdf" +output_png_file = DIR_TESTDATA / "test_example_report_new_pdf.png" + + + +def test_generate_pdf_example(config_eos): + """Test generation of example visualization report.""" + # Generate PDF + genetic0_generate_example_report(filename=str(output_file)) + + # Check if the file exists + assert output_file.exists() + + # Compare the generated file with the reference file + comparison = compare_images(str(reference_file), str(output_file), tol=0) + + # Assert that there are no differences + assert comparison is None, f"Images differ: {comparison}" + + # Everything passed, remove compare file + reference_png_file.unlink() + output_file.unlink() + output_png_file.unlink() diff --git a/tests/test_geneticoptimize.py b/tests/test_geneticoptimize.py index fce2d63e..cf0b8b91 100644 --- a/tests/test_geneticoptimize.py +++ b/tests/test_geneticoptimize.py @@ -1,9 +1,12 @@ import json +from io import BytesIO from pathlib import Path from typing import Any from unittest.mock import patch import pytest +from pydantic import ValidationError +from pypdf import PdfReader from akkudoktoreos.config.config import ConfigEOS from akkudoktoreos.core.cache import CacheEnergyManagementStore @@ -13,14 +16,14 @@ from akkudoktoreos.optimization.genetic.geneticparams import ( GeneticOptimizationParameters, ) from akkudoktoreos.optimization.genetic.geneticsolution import GeneticSolution -from akkudoktoreos.utils.datetimeutil import to_datetime -from akkudoktoreos.utils.visualize import ( - prepare_visualize, # Import the new prepare_visualize +from akkudoktoreos.optimization.genetic.geneticvisualize import ( + genetic_prepare_visualize, ) +from akkudoktoreos.utils.datetimeutil import to_datetime ems_eos = get_ems(init=True) # init once -DIR_TESTDATA = Path(__file__).parent / "testdata" +DIR_TESTDATA = Path(__file__).parent / "testdata" / "genetic" def compare_dict(actual: dict[str, Any], expected: dict[str, Any]): @@ -90,20 +93,10 @@ async def test_optimize( ) # Load input and output data - file = DIR_TESTDATA / fn_in - with file.open("r") as f_in: + parameter_file = DIR_TESTDATA / fn_in + with parameter_file.open("r") as f_in: input_data = GeneticOptimizationParameters(**json.load(f_in)) - file = DIR_TESTDATA / fn_out - # In case a new test case is added, we don't want to fail here, so the new output is written - # to disk before - try: - with file.open("r") as f_out: - expected_data = json.load(f_out) - expected_result = GeneticSolution(**expected_data) - except FileNotFoundError: - pass - # Fake energy management run start datetime ems_eos.set_start_datetime(to_datetime().set(hour=fixed_start_hour)) @@ -116,27 +109,37 @@ async def test_optimize( if ngen > 10 and not is_finalize: pytest.skip() - visualize_filename = str((DIR_TESTDATA / f"new_{fn_out}").with_suffix(".pdf")) - - with patch( - "akkudoktoreos.utils.visualize.prepare_visualize", - side_effect=lambda parameters, results, *args, **kwargs: prepare_visualize( - parameters, results, filename=visualize_filename, **kwargs - ), - ) as prepare_visualize_patch: - # Call the optimization function - genetic_solution = genetic_optimization.optimize_ems( - parameters=input_data, start_hour=fixed_start_hour, ngen=ngen - ) - # The function creates a visualization result PDF as a side-effect. - prepare_visualize_patch.assert_called_once() - assert Path(visualize_filename).exists() + # Call the optimization function + genetic_solution = genetic_optimization.optimize_ems( + parameters=input_data, start_hour=fixed_start_hour, ngen=ngen + ) # Write test output to file, so we can take it as new data on intended change TESTDATA_FILE = DIR_TESTDATA / f"new_{fn_out}" with TESTDATA_FILE.open("w", encoding="utf-8", newline="\n") as f_out: f_out.write(genetic_solution.model_dump_json(indent=4, exclude_unset=True)) + solution_file = DIR_TESTDATA / fn_out + # In case a new test case is added, we don't want to fail here, so the new output is written + # to disk before + try: + with solution_file.open("r") as f_out: + expected_data = json.load(f_out) + expected_result = GeneticSolution(**expected_data) + except ValidationError: + # Expected genetic solution data does not fit to GeneticSolution data schema + # Possibly the GeneticSolution class changed. + pytest.fail( + f"ValidationError: Can not load expected solution from {solution_file}\n" + f"cp {TESTDATA_FILE} {solution_file}\n" + ) + except FileNotFoundError: + # Should not happen + pytest.fail( + f"FileNotFoundError: Can not load expected solution from {solution_file}\n" + f"cp {TESTDATA_FILE} {solution_file}\n" + ) + assert genetic_solution.result.Gesamtbilanz_Euro == pytest.approx( expected_result.result.Gesamtbilanz_Euro ) @@ -153,3 +156,16 @@ async def test_optimize( # Check the correct generic energy management plan is created plan = genetic_solution.energy_management_plan() # @TODO + + # Check visualization works + pdf = genetic_prepare_visualize( + solution=genetic_solution, + ) + assert pdf.startswith(b"%PDF-") + + reader = PdfReader(BytesIO(pdf)) + assert len(reader.pages) == 6 + + + # Everything passed, remove generated files + TESTDATA_FILE.unlink() diff --git a/tests/test_visualize.py b/tests/test_geneticvisualize.py similarity index 57% rename from tests/test_visualize.py rename to tests/test_geneticvisualize.py index 6272ea97..66876651 100644 --- a/tests/test_visualize.py +++ b/tests/test_geneticvisualize.py @@ -2,20 +2,24 @@ from pathlib import Path from matplotlib.testing.compare import compare_images -from akkudoktoreos.utils.visualize import generate_example_report +from akkudoktoreos.optimization.genetic.geneticvisualize import ( + genetic_generate_example_report, +) filename = "example_report.pdf" -DIR_TESTDATA = Path(__file__).parent / "testdata" +DIR_TESTDATA = Path(__file__).parent / "testdata" / "genetic" reference_file = DIR_TESTDATA / "test_example_report.pdf" +reference_png_file = DIR_TESTDATA / "test_example_report_pdf.png" output_file = DIR_TESTDATA / "test_example_report_new.pdf" +output_png_file = DIR_TESTDATA / "test_example_report_new_pdf.png" def test_generate_pdf_example(config_eos): """Test generation of example visualization report.""" # Generate PDF - generate_example_report(filename=str(output_file)) + genetic_generate_example_report(filename=str(output_file)) # Check if the file exists assert output_file.exists() @@ -25,3 +29,8 @@ def test_generate_pdf_example(config_eos): # Assert that there are no differences assert comparison is None, f"Images differ: {comparison}" + + # Everything passed, remove compare file + reference_png_file.unlink() + output_file.unlink() + output_png_file.unlink() diff --git a/tests/test_pvforecastakkudoktor.py b/tests/test_pvforecastakkudoktor.py index 454af294..a6d069e2 100644 --- a/tests/test_pvforecastakkudoktor.py +++ b/tests/test_pvforecastakkudoktor.py @@ -346,7 +346,7 @@ async def test_timezone_behaviour( other_end_datetime = other_start_datetime + to_duration("24 hours") expected_end_datetime = to_datetime("2024-10-07T00:00:00+0200", in_timezone=other_timezone) assert compare_datetimes(other_end_datetime, expected_end_datetime).equal - forecast_temps = await provider.key_to_series( + forecast_temps = await provider.key_to_raw_series( "pvforecastakkudoktor_temp_air", other_start_datetime, other_end_datetime ) assert len(forecast_temps) == 23 # 24-1, first temperature is null @@ -355,7 +355,7 @@ async def test_timezone_behaviour( # Test fetching AC power forecast other_end_datetime = other_start_datetime + to_duration("48 hours") - forecast_measured = await provider.key_to_series( + forecast_measured = await provider.key_to_raw_series( "pvforecastakkudoktor_ac_power_measured", other_start_datetime, other_end_datetime ) assert len(forecast_measured) == 1 diff --git a/tests/test_system.py b/tests/test_system.py index e4725534..68c32c62 100644 --- a/tests/test_system.py +++ b/tests/test_system.py @@ -45,7 +45,13 @@ class TestSystem: result = requests.post(f"{server}/v1/prediction/update/BrightSky") assert result.status_code == HTTPStatus.OK - result = requests.get(f"{server}/v1/prediction/series?key=weather_temp_air") + result = requests.get( + f"{server}/v1/prediction/series", + params={ + "key": "weather_temp_air", + "processing": "raw", + } + ) assert result.status_code == HTTPStatus.OK data = result.json() @@ -72,7 +78,13 @@ class TestSystem: result = requests.post(f"{server}/v1/prediction/update/ClearOutside") assert result.status_code == HTTPStatus.OK, f"Failed: {result.headers} {result.text}" - result = requests.get(f"{server}/v1/prediction/series?key=weather_temp_air") + result = requests.get( + f"{server}/v1/prediction/series", + params={ + "key": "weather_temp_air", + "processing": "raw", + } + ) assert result.status_code == HTTPStatus.OK data = result.json() @@ -103,7 +115,13 @@ class TestSystem: result = requests.post(f"{server}/v1/prediction/update/PVForecastAkkudoktor") assert result.status_code == HTTPStatus.OK, f"Failed: {result.headers} {result.text}" - result = requests.get(f"{server}/v1/prediction/series?key=pvforecast_ac_power") + result = requests.get( + f"{server}/v1/prediction/series", + params={ + "key": "pvforecast_ac_power", + "processing": "raw", + } + ) assert result.status_code == HTTPStatus.OK data = result.json() @@ -134,7 +152,13 @@ class TestSystem: result = requests.post(f"{server}/v1/prediction/update/ElecPriceAkkudoktor") assert result.status_code == HTTPStatus.OK - result = requests.get(f"{server}/v1/prediction/series?key=elecprice_marketprice_wh") + result = requests.get( + f"{server}/v1/prediction/series", + params={ + "key": "elecprice_marketprice_wh", + "processing": "raw", + } + ) assert result.status_code == HTTPStatus.OK data = result.json() @@ -161,7 +185,13 @@ class TestSystem: result = requests.post(f"{server}/v1/prediction/update/LoadAkkudoktor") assert result.status_code == HTTPStatus.OK - result = requests.get(f"{server}/v1/prediction/series?key=loadforecast_power_w") + result = requests.get( + f"{server}/v1/prediction/series", + params={ + "key": "loadforecast_power_w", + "processing": "raw", + } + ) assert result.status_code == HTTPStatus.OK data = result.json() @@ -318,7 +348,13 @@ class TestSystem: # ---------------------------------------------------------------------- # 4. GET /v1/measurement/series # ---------------------------------------------------------------------- - result = requests.get(f"{server}/v1/measurement/series", params={"key": "pv1_emr"}) + result = requests.get( + f"{server}/v1/measurement/series", + params={ + "key": "pv1_emr", + "processing": "raw", + } + ) assert result.status_code == HTTPStatus.OK, f"Failed to GET series: {result.text}" series_response = result.json() @@ -330,7 +366,11 @@ class TestSystem: # Non-existent key must be rejected result = requests.get( - f"{server}/v1/measurement/series", params={"key": "non_existent_key"} + f"{server}/v1/measurement/series", + params={ + "key": "non_existent_key", + "processing": "raw", + } ) assert result.status_code == HTTPStatus.NOT_FOUND, ( f"Expected 404 for unknown series key, got {result.status_code}" @@ -362,7 +402,13 @@ class TestSystem: assert len(series_response["data"]) >= 3 # Verify the data round-trips correctly - result = requests.get(f"{server}/v1/measurement/series", params={"key": "pv2_emr"}) + result = requests.get( + f"{server}/v1/measurement/series", + params={ + "key": "pv2_emr", + "processing": "raw", + } + ) assert result.status_code == HTTPStatus.OK fetched = result.json() fetched_values = list(fetched["data"].values()) @@ -400,7 +446,13 @@ class TestSystem: # Verify data was loaded for both columns for key in ("pv1_emr", "load1_emr"): - result = requests.get(f"{server}/v1/measurement/series", params={"key": key}) + result = requests.get( + f"{server}/v1/measurement/series", + params={ + "key": key, + "processing": "raw", + } + ) assert result.status_code == HTTPStatus.OK, f"Failed to verify series for {key}" series_response = result.json() assert len(series_response["data"]) >= 3, f"Expected >=3 data points for {key}" @@ -443,7 +495,13 @@ class TestSystem: ("load1_emr", [60.5, 55.3]), ("pv2_emr", [150.8, 175.2]), ): - result = requests.get(f"{server}/v1/measurement/series", params={"key": key}) + result = requests.get( + f"{server}/v1/measurement/series", + params={ + "key": key, + "processing": "raw", + } + ) assert result.status_code == HTTPStatus.OK, f"Failed to verify {key} after data PUT" fetched = result.json() fetched_values = list(fetched["data"].values()) @@ -581,7 +639,10 @@ class TestSystem: # ---------------------------------------------------------------------- result = requests.get( f"{server}/v1/measurement/series", - params={"key": "pv1_emr_kwh"}, + params={ + "key": "pv1_emr_kwh", + "processing": "raw", + }, ) assert result.status_code == HTTPStatus.OK @@ -727,7 +788,10 @@ class TestSystem: # ---------------------------------------------------------------------- result = requests.get( f"{server}/v1/measurement/series", - params={"key": "pv1_emr_kwh"}, + params={ + "key": "pv1_emr_kwh", + "processing": "raw", + }, ) assert result.status_code == HTTPStatus.OK diff --git a/tests/testdata/docs/_generated/configexample.md b/tests/testdata/docs/_generated/configexample.md index a9d63b5d..235d803c 100644 --- a/tests/testdata/docs/_generated/configexample.md +++ b/tests/testdata/docs/_generated/configexample.md @@ -162,6 +162,7 @@ } }, "logging": { + "api_level": "TRACE", "console_level": "TRACE", "file_level": "TRACE" }, diff --git a/tests/testdata/docs/_generated/configlogging.md b/tests/testdata/docs/_generated/configlogging.md index b5cf0df6..3f997e04 100644 --- a/tests/testdata/docs/_generated/configlogging.md +++ b/tests/testdata/docs/_generated/configlogging.md @@ -7,8 +7,9 @@ | Name | Environment Variable | Type | Read-Only | Default | Description | | ---- | -------------------- | ---- | --------- | ------- | ----------- | -| console_level | `EOS_LOGGING__CONSOLE_LEVEL` | `str | None` | `rw` | `None` | Logging level when logging to console. | -| file_level | `EOS_LOGGING__FILE_LEVEL` | `str | None` | `rw` | `None` | Logging level when logging to file. | +| api_level | `EOS_LOGGING__API_LEVEL` | `str | None` | `rw` | `None` | Logging level for API response. | +| console_level | `EOS_LOGGING__CONSOLE_LEVEL` | `str | None` | `rw` | `None` | Logging level for logging to console. | +| file_level | `EOS_LOGGING__FILE_LEVEL` | `str | None` | `rw` | `None` | Logging level for logging to file. | | file_path | | `pathlib.Path | None` | `ro` | `N/A` | Computed log file path based on data output path. | ::: @@ -21,6 +22,7 @@ ```json { "logging": { + "api_level": "TRACE", "console_level": "TRACE", "file_level": "TRACE" } @@ -36,6 +38,7 @@ ```json { "logging": { + "api_level": "TRACE", "console_level": "TRACE", "file_level": "TRACE", "file_path": "/home/user/.local/share/net.akkudoktor.eos/output/eos.log" diff --git a/tests/testdata/optimize_input_1.json b/tests/testdata/genetic/optimize_input_1.json similarity index 100% rename from tests/testdata/optimize_input_1.json rename to tests/testdata/genetic/optimize_input_1.json diff --git a/tests/testdata/optimize_input_2.json b/tests/testdata/genetic/optimize_input_2.json similarity index 100% rename from tests/testdata/optimize_input_2.json rename to tests/testdata/genetic/optimize_input_2.json diff --git a/tests/testdata/genetic/optimize_result_1.json b/tests/testdata/genetic/optimize_result_1.json new file mode 100644 index 00000000..17fd89d4 --- /dev/null +++ b/tests/testdata/genetic/optimize_result_1.json @@ -0,0 +1,2218 @@ +{ + "parameters": { + "ems": { + "pv_forecast_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "electricity_price_per_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "feed_in_tariff_per_wh": [ + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05 + ], + "price_per_wh_battery": 0.0001, + "total_load": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "pv_prognose_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "strompreis_euro_pro_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "einspeiseverguetung_euro_pro_wh": [ + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05 + ], + "preis_euro_pro_wh_akku": 0.0001, + "gesamtlast": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ] + }, + "pv_battery": { + "device_id": "battery1", + "capacity_wh": 26400, + "max_charge_power_w": 5000.0, + "initial_soc_percentage": 80, + "min_soc_percentage": 15 + }, + "inverter": { + "device_id": "inverter1", + "max_power_wh": 10000.0, + "battery_id": "battery1" + }, + "ev": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "discharging_efficiency": 1.0, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 54, + "min_soc_percentage": 0 + }, + "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.0, + 18.6, + 19.2, + 19.1, + 18.7, + 18.5, + 17.7, + 16.2, + 14.6, + 13.6, + 13.0, + 12.6, + 12.2, + 11.7, + 11.6, + 11.3, + 11.0, + 10.7, + 10.2, + 11.4, + 14.4, + 16.4, + 18.3, + 19.5, + 20.7, + 21.9, + 22.7, + 23.1, + 23.1, + 22.8, + 21.8, + 20.2, + 19.1, + 18.0, + 17.4 + ], + "start_solution": [ + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "pv_akku": { + "device_id": "battery1", + "capacity_wh": 26400, + "max_charge_power_w": 5000.0, + "initial_soc_percentage": 80, + "min_soc_percentage": 15 + }, + "eauto": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "discharging_efficiency": 1.0, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 54, + "min_soc_percentage": 0 + } + }, + "ac_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "dc_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "discharge_allowed": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0 + ], + "ev_charge_hours_float": null, + "result": { + "load_wh_per_hour": [ + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "ev_soc_per_hour": [ + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0 + ], + "revenue_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19391086083906173, + 0.18681973047764083, + 0.12880892587597292, + 0.02404700392596282, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.12500582038027028, + 0.14608958812480227, + 0.09047346757070289, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_losses": 2878.660271824896, + "total_balance": 1.053854835771092, + "total_revenue": 0.9055602150669013, + "total_costs": 1.9594150508379933, + "home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "costs_per_hour": [ + 0.0, + 0.004569992000000018, + 0.0, + 0.0, + 0.0, + 4.482711108977355e-14, + 0.0, + 0.016809914659344942, + 0.0, + 0.05480703000000003, + 0.0, + 0.291163892, + 0.0, + 0.0, + 0.0, + 0.0, + 0.22802125600000003, + 0.0, + 0.182970359, + 0.162995926, + 0.0, + 0.26411047, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.010682755832597498, + 0.0, + 0.0003442778967139274, + 0.0, + 0.028137079449292023, + 0.0, + 0.08231598, + 0.174597189, + 0.293043269, + 0.0, + 0.16484566 + ], + "grid_consumption_wh_per_hour": [ + 0.0, + 20.660000000000082, + 0.0, + 0.0, + 0.0, + 2.236881790906864e-10, + 0.0, + 74.05248748610107, + 0.0, + 171.54000000000008, + 0.0, + 980.68, + 0.0, + 0.0, + 0.0, + 0.0, + 694.34, + 0.0, + 556.31, + 488.89, + 0.0, + 799.85, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 56.853410498124, + 0.0, + 1.7179535764168035, + 0.0, + 123.95189184710142, + 0.0, + 257.64, + 566.69, + 987.01, + 0.0, + 592.97 + ], + "grid_feed_in_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2770.155154843739, + 2668.8532925377262, + 1840.127512513899, + 343.5286275137546, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1785.797434003861, + 2086.994116068604, + 1292.4781081528986, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "losses_per_hour": [ + 16.744090909090914, + 0.0, + 29.157272727272726, + 3.7179773678125034, + 582.6180000000041, + 188.65138141872444, + 10.782457846871594, + 0.0, + 59.810111380126784, + 0.0, + 99.72409090909093, + 0.0, + 124.41545454545451, + 96.08318181818186, + 70.41409090909087, + 118.37045454545455, + 0.0, + 83.01681818181817, + 0.0, + 0.0, + 69.12409090909085, + 0.0, + 109.96227272727276, + 47.952272727272714, + 16.01263877609336, + 55.946263193163475, + 161.62968357967037, + 14.209990740225123, + 538.2984000000038, + 227.42215349036655, + 10.13011689299087, + 0.0, + 44.377460775206174, + 0.0, + 0.0, + 0.0, + 100.08954545454549, + 0.0 + ], + "battery_soc_per_hour": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 78.55109331955923, + 78.57585051614844, + 94.75968384947988, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.85214359504131, + 96.85214359504131, + 92.92488808539943, + 89.89195936639118, + 87.6692923553719, + 83.93285123966942, + 83.93285123966942, + 81.31237086776858, + 81.31237086776858, + 81.31237086776858, + 79.13042355371898, + 79.13042355371898, + 75.65939221763084, + 74.14574724517905, + 74.30696088041155, + 74.82732877552169, + 78.33526266026307, + 78.72998462526934, + 93.68271795860093, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.84060778236915 + ], + "electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "Last_Wh_pro_Stunde": [ + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "EAuto_SoC_pro_Stunde": [ + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0 + ], + "Einnahmen_Euro_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19391086083906173, + 0.18681973047764083, + 0.12880892587597292, + 0.02404700392596282, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.12500582038027028, + 0.14608958812480227, + 0.09047346757070289, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Gesamt_Verluste": 2878.660271824896, + "Gesamtbilanz_Euro": 1.053854835771092, + "Gesamteinnahmen_Euro": 0.9055602150669013, + "Gesamtkosten_Euro": 1.9594150508379933, + "Home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Kosten_Euro_pro_Stunde": [ + 0.0, + 0.004569992000000018, + 0.0, + 0.0, + 0.0, + 4.482711108977355e-14, + 0.0, + 0.016809914659344942, + 0.0, + 0.05480703000000003, + 0.0, + 0.291163892, + 0.0, + 0.0, + 0.0, + 0.0, + 0.22802125600000003, + 0.0, + 0.182970359, + 0.162995926, + 0.0, + 0.26411047, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.010682755832597498, + 0.0, + 0.0003442778967139274, + 0.0, + 0.028137079449292023, + 0.0, + 0.08231598, + 0.174597189, + 0.293043269, + 0.0, + 0.16484566 + ], + "Netzbezug_Wh_pro_Stunde": [ + 0.0, + 20.660000000000082, + 0.0, + 0.0, + 0.0, + 2.236881790906864e-10, + 0.0, + 74.05248748610107, + 0.0, + 171.54000000000008, + 0.0, + 980.68, + 0.0, + 0.0, + 0.0, + 0.0, + 694.34, + 0.0, + 556.31, + 488.89, + 0.0, + 799.85, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 56.853410498124, + 0.0, + 1.7179535764168035, + 0.0, + 123.95189184710142, + 0.0, + 257.64, + 566.69, + 987.01, + 0.0, + 592.97 + ], + "Netzeinspeisung_Wh_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2770.155154843739, + 2668.8532925377262, + 1840.127512513899, + 343.5286275137546, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1785.797434003861, + 2086.994116068604, + 1292.4781081528986, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Verluste_Pro_Stunde": [ + 16.744090909090914, + 0.0, + 29.157272727272726, + 3.7179773678125034, + 582.6180000000041, + 188.65138141872444, + 10.782457846871594, + 0.0, + 59.810111380126784, + 0.0, + 99.72409090909093, + 0.0, + 124.41545454545451, + 96.08318181818186, + 70.41409090909087, + 118.37045454545455, + 0.0, + 83.01681818181817, + 0.0, + 0.0, + 69.12409090909085, + 0.0, + 109.96227272727276, + 47.952272727272714, + 16.01263877609336, + 55.946263193163475, + 161.62968357967037, + 14.209990740225123, + 538.2984000000038, + 227.42215349036655, + 10.13011689299087, + 0.0, + 44.377460775206174, + 0.0, + 0.0, + 0.0, + 100.08954545454549, + 0.0 + ], + "akku_soc_pro_stunde": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 78.55109331955923, + 78.57585051614844, + 94.75968384947988, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.85214359504131, + 96.85214359504131, + 92.92488808539943, + 89.89195936639118, + 87.6692923553719, + 83.93285123966942, + 83.93285123966942, + 81.31237086776858, + 81.31237086776858, + 81.31237086776858, + 79.13042355371898, + 79.13042355371898, + 75.65939221763084, + 74.14574724517905, + 74.30696088041155, + 74.82732877552169, + 78.33526266026307, + 78.72998462526934, + 93.68271795860093, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.84060778236915 + ], + "Electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ] + }, + "ev_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 32400.000000000004, + "initial_soc_percentage": 54 + }, + "start_hour": 10, + "start_solution": [ + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 2.0, + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 2.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "washingstart": null, + "extra_data": { + "balance": [ + 1.5629315062541924, + 2.6256495075632187, + 2.165740335702944, + 1.5629315062541924, + 2.240440405626093, + 1.5629315062541924, + 1.9877015035829169, + 2.22262363104199, + 1.5778657214023133, + 2.208397307667416, + 1.5629315062541924, + 2.4293303489630818, + 2.412448593343341, + 1.5629315062541924, + 2.218927499018621, + 1.5629315062541924, + 2.2587939554734637, + 1.053854835771092, + 1.6043069913102126, + 2.273703414535969, + 2.212860049246504, + 1.5629315062541924, + 2.295724023298086, + 1.5629315062541924, + 2.3276080643501436, + 1.5629315062541924, + 2.638380034677266, + 1.5629315062541924, + 1.6639995536998098, + 1.405068570959087, + 1.5629315062541924, + 1.8051355297292633, + 1.405068570959087, + 2.287814905181311, + 1.5629315062541924, + 2.3795803010587147, + 2.273703414535969, + 2.2216775103872193, + 2.2216775103872193, + 1.9097791698801405, + 1.981541802518228, + 1.5778657214023133, + 1.5629315062541924, + 2.6086571004823034, + 1.9163540908754297, + 1.5629315062541924, + 2.596003055647592, + 2.5548807933161815, + 1.8727661382233525, + 1.8385927648722558, + 2.4293303489630818, + 2.164461541806346, + 2.2841554715592074, + 1.405068570959087, + 1.8777991489912709, + 1.3689438450633387, + 1.405068570959087, + 1.5629315062541924, + 2.612646914374828, + 1.5629315062541924, + 2.2216775103872193, + 1.5629315062541924, + 1.968731246274983, + 1.5629315062541924, + 2.6141833032666306, + 1.5629315062541924, + 2.4185117224953836, + 2.273703414535969, + 2.2587939554734637, + 2.5176226765078527, + 1.5629315062541924, + 1.5629315062541924, + 2.2216775103872193, + 2.7640811111485957, + 1.5629315062541924, + 2.253022440711902, + 2.4293303489630818, + 2.204743141961746, + 2.056831850387219, + 1.6165388024504925, + 2.1475638411090703, + 2.0690811111485954, + 2.2216775103872193, + 1.8385927648722558, + 2.6378948527424724, + 2.519823130326035, + 2.295724023298086, + 2.7524385083491794, + 1.5629315062541924, + 2.212860049246504, + 2.4681479270410693, + 2.2216775103872193, + 2.253022440711902, + 2.4293303489630818, + 1.5629315062541924, + 1.9097791698801405, + 1.5629315062541924, + 2.312165891637598, + 2.212860049246504, + 1.5629315062541924 + ], + "losses": [ + 2020.0647478143976, + 1783.7767860253437, + 1913.3967498491086, + 2020.0647478143976, + 2534.700181698695, + 2020.0647478143976, + 2042.788780154585, + 2323.4039189058713, + 2111.0454480011385, + 1867.7293965747283, + 2020.0647478143976, + 1813.1396479231805, + 1356.6271470299466, + 2020.0647478143976, + 2467.2825251989507, + 2020.0647478143976, + 1169.72103850289, + 2878.660271824896, + 1924.8294619726955, + 2511.3137436444576, + 1595.7341100085885, + 2020.0647478143976, + 2109.799113667768, + 2020.0647478143976, + 2065.4216528925617, + 2020.0647478143976, + 1259.792129725977, + 2020.0647478143976, + 1845.248795108642, + 2240.8216024232343, + 2020.0647478143976, + 2262.8228060083893, + 2240.8216024232343, + 2347.690047244586, + 2020.0647478143976, + 1662.2368625088986, + 2511.3137436444576, + 2326.230637196544, + 2326.230637196544, + 1735.5385731331469, + 2291.306201407247, + 2111.0454480011385, + 2020.0647478143976, + 1677.0149722648923, + 2554.276136009273, + 2020.0647478143976, + 1693.792286314479, + 1681.8816277412084, + 2280.299877198412, + 2202.8469441371435, + 1813.1396479231805, + 2020.7537203686422, + 1951.0702258212687, + 2240.8216024232343, + 2351.958534876586, + 2164.1171473088725, + 2240.8216024232343, + 2020.0647478143976, + 1183.3843076326698, + 2020.0647478143976, + 2326.230637196544, + 2020.0647478143976, + 1767.105028996182, + 2020.0647478143976, + 1287.2123510784259, + 2020.0647478143976, + 2311.9185803023647, + 2511.3137436444576, + 1169.72103850289, + 1203.5357654350614, + 2020.0647478143976, + 2020.0647478143976, + 2326.230637196544, + 1882.5060289276414, + 2020.0647478143976, + 2055.2012844063765, + 1813.1396479231805, + 1954.3983714757119, + 2407.09018265109, + 2285.5086712319326, + 2234.8419209342183, + 1582.5060289276414, + 2326.230637196544, + 2202.8469441371435, + 1142.3492328190187, + 1115.4373563441525, + 2109.799113667768, + 1981.6810169345538, + 2020.0647478143976, + 1595.7341100085885, + 1592.376446450568, + 2326.230637196544, + 2055.2012844063765, + 1813.1396479231805, + 2020.0647478143976, + 1735.5385731331469, + 2020.0647478143976, + 1592.0138728022027, + 1595.7341100085885, + 2020.0647478143976 + ], + "constraints": [ + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0 + ] + }, + "fitness_history": { + "gen": [ + 0, + 1, + 2, + 3 + ], + "avg": [ + 1.3457767274332526, + 0.9566909031398302, + 0.502805054118316, + 0.15642334466274066 + ], + "max": [ + 2.1600157439680157, + 1.6360627044583638, + 1.5607919093370366, + 0.8123975083491792 + ], + "min": [ + -0.5805761986329421, + -0.4395834290409131, + -0.4395834290409131, + -0.8474661642289083 + ] + }, + "fixed_seed": 42, + "eautocharge_hours_float": null, + "eauto_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 32400.000000000004, + "initial_soc_percentage": 54 + } +} \ No newline at end of file diff --git a/tests/testdata/genetic/optimize_result_1_be.json b/tests/testdata/genetic/optimize_result_1_be.json new file mode 100644 index 00000000..cade9cfd --- /dev/null +++ b/tests/testdata/genetic/optimize_result_1_be.json @@ -0,0 +1,2218 @@ +{ + "parameters": { + "ems": { + "pv_forecast_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "electricity_price_per_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "feed_in_tariff_per_wh": [ + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05 + ], + "price_per_wh_battery": 0.0001, + "total_load": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "pv_prognose_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "strompreis_euro_pro_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "einspeiseverguetung_euro_pro_wh": [ + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05 + ], + "preis_euro_pro_wh_akku": 0.0001, + "gesamtlast": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ] + }, + "pv_battery": { + "device_id": "battery1", + "capacity_wh": 26400, + "max_charge_power_w": 5000.0, + "initial_soc_percentage": 80, + "min_soc_percentage": 15 + }, + "inverter": { + "device_id": "inverter1", + "max_power_wh": 10000.0, + "battery_id": "battery1" + }, + "ev": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "discharging_efficiency": 1.0, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 54, + "min_soc_percentage": 0 + }, + "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.0, + 18.6, + 19.2, + 19.1, + 18.7, + 18.5, + 17.7, + 16.2, + 14.6, + 13.6, + 13.0, + 12.6, + 12.2, + 11.7, + 11.6, + 11.3, + 11.0, + 10.7, + 10.2, + 11.4, + 14.4, + 16.4, + 18.3, + 19.5, + 20.7, + 21.9, + 22.7, + 23.1, + 23.1, + 22.8, + 21.8, + 20.2, + 19.1, + 18.0, + 17.4 + ], + "start_solution": [ + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "pv_akku": { + "device_id": "battery1", + "capacity_wh": 26400, + "max_charge_power_w": 5000.0, + "initial_soc_percentage": 80, + "min_soc_percentage": 15 + }, + "eauto": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "discharging_efficiency": 1.0, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 54, + "min_soc_percentage": 0 + } + }, + "ac_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "dc_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "discharge_allowed": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0 + ], + "ev_charge_hours_float": null, + "result": { + "load_wh_per_hour": [ + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "ev_soc_per_hour": [ + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0 + ], + "revenue_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19478794541504615, + 0.18681973047764083, + 0.12880892587597292, + 0.02404700392596282, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.17104472158211628, + 0.14608958812480227, + 0.09047346757070289, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_losses": 2804.7326610375394, + "total_balance": 0.9004618481798127, + "total_revenue": 0.9524762008447317, + "total_costs": 1.8529380490245444, + "home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "costs_per_hour": [ + 0.0, + 0.004569992000000018, + 0.0, + 0.0018232052307313393, + 0.0, + 4.482711108977355e-14, + 0.0, + 0.016809914659344942, + 0.0, + 0.05480703000000003, + 0.0, + 0.291163892, + 0.0, + 0.19588158, + 0.174739608, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.16677339, + 0.0, + 0.24530383800000005, + 0.08545095, + 0.007989913613567745, + 0.028255713342252034, + 0.0, + 0.010682755832597498, + 0.0, + 0.0003442778967139274, + 0.0, + 0.028137079449292023, + 0.0, + 0.08231598, + 0.0, + 0.293043269, + 0.0, + 0.16484566 + ], + "grid_consumption_wh_per_hour": [ + 0.0, + 20.660000000000082, + 0.0, + 9.703061366318996, + 0.0, + 2.236881790906864e-10, + 0.0, + 74.05248748610107, + 0.0, + 171.54000000000008, + 0.0, + 980.68, + 0.0, + 704.61, + 516.37, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 506.91, + 0.0, + 806.3900000000001, + 351.65, + 35.04348076126204, + 127.73830624887898, + 0.0, + 56.853410498124, + 0.0, + 1.7179535764168035, + 0.0, + 123.95189184710142, + 0.0, + 257.64, + 0.0, + 987.01, + 0.0, + 592.97 + ], + "grid_feed_in_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2782.6849345006594, + 2668.8532925377262, + 1840.127512513899, + 343.5286275137546, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2443.4960226016615, + 2086.994116068604, + 1292.4781081528986, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "losses_per_hour": [ + 16.744090909090914, + 0.0, + 29.157272727272726, + 2.3948326360417305, + 582.6180000000041, + 187.1478078598941, + 10.782457846871594, + 0.0, + 59.810111380126784, + 0.0, + 99.72409090909093, + 0.0, + 124.41545454545451, + 0.0, + 0.0, + 118.37045454545455, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 0.0, + 0.0, + 11.233982308648535, + 38.52740325013451, + 161.62968357967037, + 14.209990740225123, + 538.2984000000038, + 148.49832285863067, + 10.13011689299087, + 0.0, + 44.377460775206174, + 0.0, + 77.27590909090907, + 0.0, + 100.08954545454549, + 0.0 + ], + "battery_soc_per_hour": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 78.55109331955923, + 78.61761644833817, + 94.80144978166962, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.85214359504131, + 96.85214359504131, + 92.92488808539943, + 92.92488808539943, + 92.92488808539943, + 89.18844696969695, + 86.19972451790632, + 83.57924414600548, + 81.18465909090907, + 79.08027720385672, + 79.08027720385672, + 75.63739669421484, + 75.63739669421484, + 75.63739669421484, + 75.94945175834397, + 77.01965740418103, + 80.52759128892241, + 80.92231325392866, + 95.87504658726026, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 97.56073519283747, + 97.56073519283747, + 94.40134297520663 + ], + "electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "Last_Wh_pro_Stunde": [ + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "EAuto_SoC_pro_Stunde": [ + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0 + ], + "Einnahmen_Euro_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19478794541504615, + 0.18681973047764083, + 0.12880892587597292, + 0.02404700392596282, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.17104472158211628, + 0.14608958812480227, + 0.09047346757070289, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Gesamt_Verluste": 2804.7326610375394, + "Gesamtbilanz_Euro": 0.9004618481798127, + "Gesamteinnahmen_Euro": 0.9524762008447317, + "Gesamtkosten_Euro": 1.8529380490245444, + "Home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Kosten_Euro_pro_Stunde": [ + 0.0, + 0.004569992000000018, + 0.0, + 0.0018232052307313393, + 0.0, + 4.482711108977355e-14, + 0.0, + 0.016809914659344942, + 0.0, + 0.05480703000000003, + 0.0, + 0.291163892, + 0.0, + 0.19588158, + 0.174739608, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.16677339, + 0.0, + 0.24530383800000005, + 0.08545095, + 0.007989913613567745, + 0.028255713342252034, + 0.0, + 0.010682755832597498, + 0.0, + 0.0003442778967139274, + 0.0, + 0.028137079449292023, + 0.0, + 0.08231598, + 0.0, + 0.293043269, + 0.0, + 0.16484566 + ], + "Netzbezug_Wh_pro_Stunde": [ + 0.0, + 20.660000000000082, + 0.0, + 9.703061366318996, + 0.0, + 2.236881790906864e-10, + 0.0, + 74.05248748610107, + 0.0, + 171.54000000000008, + 0.0, + 980.68, + 0.0, + 704.61, + 516.37, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 506.91, + 0.0, + 806.3900000000001, + 351.65, + 35.04348076126204, + 127.73830624887898, + 0.0, + 56.853410498124, + 0.0, + 1.7179535764168035, + 0.0, + 123.95189184710142, + 0.0, + 257.64, + 0.0, + 987.01, + 0.0, + 592.97 + ], + "Netzeinspeisung_Wh_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2782.6849345006594, + 2668.8532925377262, + 1840.127512513899, + 343.5286275137546, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2443.4960226016615, + 2086.994116068604, + 1292.4781081528986, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Verluste_Pro_Stunde": [ + 16.744090909090914, + 0.0, + 29.157272727272726, + 2.3948326360417305, + 582.6180000000041, + 187.1478078598941, + 10.782457846871594, + 0.0, + 59.810111380126784, + 0.0, + 99.72409090909093, + 0.0, + 124.41545454545451, + 0.0, + 0.0, + 118.37045454545455, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 0.0, + 0.0, + 11.233982308648535, + 38.52740325013451, + 161.62968357967037, + 14.209990740225123, + 538.2984000000038, + 148.49832285863067, + 10.13011689299087, + 0.0, + 44.377460775206174, + 0.0, + 77.27590909090907, + 0.0, + 100.08954545454549, + 0.0 + ], + "akku_soc_pro_stunde": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 78.55109331955923, + 78.61761644833817, + 94.80144978166962, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.85214359504131, + 96.85214359504131, + 92.92488808539943, + 92.92488808539943, + 92.92488808539943, + 89.18844696969695, + 86.19972451790632, + 83.57924414600548, + 81.18465909090907, + 79.08027720385672, + 79.08027720385672, + 75.63739669421484, + 75.63739669421484, + 75.63739669421484, + 75.94945175834397, + 77.01965740418103, + 80.52759128892241, + 80.92231325392866, + 95.87504658726026, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 97.56073519283747, + 97.56073519283747, + 94.40134297520663 + ], + "Electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ] + }, + "ev_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 32400.000000000004, + "initial_soc_percentage": 54 + }, + "start_hour": 10, + "start_solution": [ + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "washingstart": null, + "extra_data": { + "balance": [ + 1.5629315062541924, + 2.158365581179813, + 2.165740335702944, + 1.4445126332046057, + 2.602530525650703, + 1.5629315062541924, + 0.9004618481798127, + 2.22262363104199, + 2.0453091016610436, + 1.4445126332046057, + 1.5629315062541924, + 2.4293303489630818, + 3.1320077296752524, + 1.5629315062541924, + 3.2988292733845688, + 1.5629315062541924, + 2.2587939554734637, + 1.5629315062541924, + 2.3179853855994446, + 3.586409468799831, + 2.060941413080932, + 1.5629315062541924, + 3.095869071948983, + 1.5629315062541924, + 3.6165384365879865, + 1.5629315062541924, + 3.6894546560699064, + 1.5629315062541924, + 1.4744169145760049, + 1.405068570959087, + 1.5629315062541924, + 1.778868572372095, + 1.405068570959087, + 2.3041345656193553, + 1.5629315062541924, + 2.3795803010587147, + 2.789764513631562, + 3.455211569265561, + 3.6165384365879865, + 1.9097791698801405, + 1.981541802518228, + 2.0453091016610436, + 1.5629315062541924, + 3.0448979100124167, + 1.9163540908754297, + 1.5629315062541924, + 3.6165384365879865, + 3.440531929235723, + 2.826784740487001, + 1.8534925065868375, + 2.4293303489630818, + 2.77797403459721, + 2.2841554715592074, + 1.405068570959087, + 3.4781121347699133, + 1.3689438450633387, + 1.405068570959087, + 1.5629315062541924, + 2.74043868882997, + 1.5629315062541924, + 2.2216775103872193, + 1.5629315062541924, + 1.6606062986770178, + 1.5629315062541924, + 3.4419604002655637, + 1.5629315062541924, + 1.252278947504598, + 2.3795803010587147, + 2.2587939554734637, + 3.224443186559079, + 1.5629315062541924, + 1.5629315062541924, + 3.455211569265561, + 3.550214545650703, + 1.5629315062541924, + 3.247129579931017, + 2.4293303489630818, + 2.204743141961746, + 2.781472393502402, + 1.2521326049554384, + 2.4627361520787714, + 2.1032943823386185, + 2.3795803010587147, + 1.8534925065868375, + 3.4419604002655637, + 0.9004618481798127, + 3.400659468799855, + 2.928225531235026, + 1.5629315062541924, + 2.060941413080932, + 2.7315306721942645, + 1.252278947504598, + 2.158365581179813, + 2.4293303489630818, + 1.5629315062541924, + 1.9097791698801405, + 1.5629315062541924, + 3.0182387945383575, + 2.060941413080932, + 1.5629315062541924 + ], + "losses": [ + 2020.0647478143976, + 2192.087661037538, + 1913.3967498491086, + 2146.731689963158, + 2076.6512997516284, + 2020.0647478143976, + 2804.7326610375394, + 2323.4039189058713, + 2276.9165566773136, + 2146.731689963158, + 2020.0647478143976, + 1813.1396479231805, + 2453.3717179361724, + 2020.0647478143976, + 2045.3031396587173, + 2020.0647478143976, + 1169.72103850289, + 2020.0647478143976, + 2022.8914661049919, + 2297.776927567542, + 1740.0980553572056, + 2020.0647478143976, + 1955.6482629011505, + 2020.0647478143976, + 2231.2158149510287, + 2020.0647478143976, + 1995.1164780946408, + 2020.0647478143976, + 1941.8221284758113, + 2240.8216024232343, + 2020.0647478143976, + 1985.9910471579694, + 2240.8216024232343, + 1822.3583659571498, + 2020.0647478143976, + 1662.2368625088986, + 2111.5915046106384, + 2039.4963465901828, + 2231.2158149510287, + 1735.5385731331469, + 2291.306201407247, + 2276.9165566773136, + 2020.0647478143976, + 2061.499565175687, + 2554.276136009273, + 2020.0647478143976, + 2231.2158149510287, + 1295.3855539594088, + 2252.708016174569, + 2132.240439237302, + 1813.1396479231805, + 2265.2409254784407, + 1951.0702258212687, + 2240.8216024232343, + 1393.5532541371747, + 2164.1171473088725, + 2240.8216024232343, + 2020.0647478143976, + 2397.4228088258496, + 2020.0647478143976, + 2326.230637196544, + 2020.0647478143976, + 1866.3559010102965, + 2020.0647478143976, + 1437.151294274816, + 2020.0647478143976, + 2532.8849729896056, + 1662.2368625088986, + 1169.72103850289, + 2009.9127184422987, + 2020.0647478143976, + 2020.0647478143976, + 2039.4963465901828, + 2351.784027024355, + 2020.0647478143976, + 1648.5297351292108, + 1813.1396479231805, + 1954.3983714757119, + 1474.500829230451, + 2512.3504289499906, + 1918.0841451979659, + 2404.4281890618477, + 1662.2368625088986, + 2132.240439237302, + 1437.151294274816, + 2804.7326610375394, + 2297.77692756747, + 1899.2434487406306, + 2020.0647478143976, + 1740.0980553572056, + 2238.9906419601734, + 2532.8849729896056, + 2192.087661037538, + 1813.1396479231805, + 2020.0647478143976, + 1735.5385731331469, + 2020.0647478143976, + 1564.905653765309, + 1740.0980553572056, + 2020.0647478143976 + ], + "constraints": [ + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0 + ] + }, + "fitness_history": { + "gen": [ + 0, + 1, + 2, + 3 + ], + "avg": [ + 13.814324178113074, + 10.672603319001144, + 7.251256543780025, + 3.661137085484413 + ], + "max": [ + 24.747792311219673, + 21.062958907674986, + 14.935148234123542, + 11.659243141417376 + ], + "min": [ + -0.28172049374580777, + -0.4395834290409131, + -0.9441901518201875, + -0.9441901518201875 + ] + }, + "fixed_seed": 42, + "eautocharge_hours_float": null, + "eauto_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 32400.000000000004, + "initial_soc_percentage": 54 + } +} \ No newline at end of file diff --git a/tests/testdata/genetic/optimize_result_2.json b/tests/testdata/genetic/optimize_result_2.json new file mode 100644 index 00000000..89aca22b --- /dev/null +++ b/tests/testdata/genetic/optimize_result_2.json @@ -0,0 +1,2123 @@ +{ + "parameters": { + "ems": { + "pv_forecast_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "electricity_price_per_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "feed_in_tariff_per_wh": 7e-05, + "price_per_wh_battery": 0.0, + "total_load": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "pv_prognose_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "strompreis_euro_pro_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "einspeiseverguetung_euro_pro_wh": 7e-05, + "preis_euro_pro_wh_akku": 0.0, + "gesamtlast": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ] + }, + "pv_battery": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "inverter": { + "device_id": "inverter1", + "max_power_wh": 10000.0, + "battery_id": "battery1" + }, + "ev": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + }, + "dishwasher": { + "device_id": "dishwasher1", + "consumption_wh": 5000, + "duration_h": 2, + "time_windows": { + "windows": [ + { + "start_time": "00:00:00.000000", + "duration": "2 days" + } + ] + } + }, + "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.0, + 18.6, + 19.2, + 19.1, + 18.7, + 18.5, + 17.7, + 16.2, + 14.6, + 13.6, + 13.0, + 12.6, + 12.2, + 11.7, + 11.6, + 11.3, + 11.0, + 10.7, + 10.2, + 11.4, + 14.4, + 16.4, + 18.3, + 19.5, + 20.7, + 21.9, + 22.7, + 23.1, + 23.1, + 22.8, + 21.8, + 20.2, + 19.1, + 18.0, + 17.4 + ], + "start_solution": null, + "pv_akku": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "eauto": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + } + }, + "ac_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0 + ], + "dc_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "discharge_allowed": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1 + ], + "ev_charge_hours_float": [ + 1.0, + 0.5, + 0.375, + 0.375, + 1.0, + 0.75, + 1.0, + 0.75, + 0.5, + 0.875, + 0.0, + 0.875, + 0.75, + 1.0, + 0.625, + 0.375, + 0.75, + 0.875, + 0.625, + 1.0, + 0.75, + 0.375, + 0.75, + 0.5, + 0.5, + 0.625, + 0.875, + 0.5, + 0.0, + 0.0, + 0.75, + 0.875, + 0.0, + 0.0, + 1.0, + 0.375, + 0.375, + 1.0, + 0.0, + 0.875, + 0.5, + 1.0, + 0.625, + 0.75, + 0.875, + 1.0, + 0.5, + 0.5 + ], + "result": { + "load_wh_per_hour": [ + 1053.07, + 10240.91, + 14186.56, + 11620.03, + 11218.67, + 7609.82, + 9082.22, + 10280.78, + 2177.92, + 1178.71, + 1050.98, + 1988.56, + 912.38, + 1704.6100000000001, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "ev_soc_per_hour": [ + 5.0, + 5.0, + 20.294999999999998, + 33.405, + 50.885000000000005, + 61.809999999999995, + 68.365, + 81.475, + 96.77, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518 + ], + "revenue_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.010924611164505103, + 0.25751345302450973, + 0.14608958812480227, + 0.07926913850394514, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_losses": 5766.286846118221, + "total_balance": 12.78299149726557, + "total_revenue": 0.5042016086902498, + "total_costs": 13.28719310595582, + "home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "costs_per_hour": [ + 0.0, + 2.034522392, + 2.737606326, + 1.9651220859999998, + 0.9557324300000001, + 0.4189863, + 1.1236923319999998, + 1.64866014, + 0.07038454500000005, + 0.05480703000000003, + 0.0, + 0.588063892, + 0.0, + 0.47388158, + 0.174739608, + 0.28801899, + 0.0, + 0.0, + 0.0, + 0.0, + 0.16677339, + 0.0, + 0.0, + 0.0, + 0.007989913613567745, + 0.028255713342252034, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.08231598, + 0.174597189, + 0.293043269, + 0.0, + 0.0 + ], + "grid_consumption_wh_per_hour": [ + 0.0, + 9197.66, + 13079.82, + 10458.34, + 5199.85, + 2090.75, + 5112.339999999999, + 7262.820000000001, + 234.85000000000014, + 171.54000000000008, + 0.0, + 1980.6799999999998, + 0.0, + 1704.6100000000001, + 516.37, + 868.05, + 0.0, + 0.0, + 0.0, + 0.0, + 506.91, + 0.0, + 0.0, + 0.0, + 35.04348076126204, + 127.73830624887898, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 257.64, + 566.69, + 987.01, + 0.0, + 0.0 + ], + "grid_feed_in_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 156.06587377864435, + 3678.7636146358536, + 2086.994116068604, + 1132.4162643420734, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "losses_per_hour": [ + 16.744090909090914, + 483.0, + 1014.0, + 552.0, + 465.0, + 207.0, + 414.0, + 483.0, + 55.200000000000045, + 0.0, + 99.72409090909093, + 120.0, + 124.41545454545451, + 120.0, + 0.0, + 0.0, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 109.96227272727276, + 47.952272727272714, + 11.233982308648535, + 38.52740325013451, + 161.62968357967037, + 21.962728535423857, + 519.5704951465664, + 0.5004782113116364, + 10.13011689299087, + 36.109951963721926, + 44.377460775206174, + 0.0, + 0.0, + 0.0, + 100.08954545454549, + 80.85954545454547 + ], + "battery_soc_per_hour": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 96.13812844352617, + 96.13812844352617, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 96.32360537190083, + 99.65693870523415, + 95.72968319559227, + 99.0630165289256, + 99.0630165289256, + 99.0630165289256, + 96.07429407713497, + 93.45381370523414, + 91.05922865013771, + 88.95484676308537, + 88.95484676308537, + 85.51196625344349, + 82.04093491735533, + 80.52728994490354, + 80.83934500903268, + 81.90955065486975, + 85.41748453961112, + 85.56748624593055, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.84060778236915 + ], + "electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "Last_Wh_pro_Stunde": [ + 1053.07, + 10240.91, + 14186.56, + 11620.03, + 11218.67, + 7609.82, + 9082.22, + 10280.78, + 2177.92, + 1178.71, + 1050.98, + 1988.56, + 912.38, + 1704.6100000000001, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "EAuto_SoC_pro_Stunde": [ + 5.0, + 5.0, + 20.294999999999998, + 33.405, + 50.885000000000005, + 61.809999999999995, + 68.365, + 81.475, + 96.77, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518 + ], + "Einnahmen_Euro_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.010924611164505103, + 0.25751345302450973, + 0.14608958812480227, + 0.07926913850394514, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Gesamt_Verluste": 5766.286846118221, + "Gesamtbilanz_Euro": 12.78299149726557, + "Gesamteinnahmen_Euro": 0.5042016086902498, + "Gesamtkosten_Euro": 13.28719310595582, + "Home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Kosten_Euro_pro_Stunde": [ + 0.0, + 2.034522392, + 2.737606326, + 1.9651220859999998, + 0.9557324300000001, + 0.4189863, + 1.1236923319999998, + 1.64866014, + 0.07038454500000005, + 0.05480703000000003, + 0.0, + 0.588063892, + 0.0, + 0.47388158, + 0.174739608, + 0.28801899, + 0.0, + 0.0, + 0.0, + 0.0, + 0.16677339, + 0.0, + 0.0, + 0.0, + 0.007989913613567745, + 0.028255713342252034, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.08231598, + 0.174597189, + 0.293043269, + 0.0, + 0.0 + ], + "Netzbezug_Wh_pro_Stunde": [ + 0.0, + 9197.66, + 13079.82, + 10458.34, + 5199.85, + 2090.75, + 5112.339999999999, + 7262.820000000001, + 234.85000000000014, + 171.54000000000008, + 0.0, + 1980.6799999999998, + 0.0, + 1704.6100000000001, + 516.37, + 868.05, + 0.0, + 0.0, + 0.0, + 0.0, + 506.91, + 0.0, + 0.0, + 0.0, + 35.04348076126204, + 127.73830624887898, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 257.64, + 566.69, + 987.01, + 0.0, + 0.0 + ], + "Netzeinspeisung_Wh_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 156.06587377864435, + 3678.7636146358536, + 2086.994116068604, + 1132.4162643420734, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Verluste_Pro_Stunde": [ + 16.744090909090914, + 483.0, + 1014.0, + 552.0, + 465.0, + 207.0, + 414.0, + 483.0, + 55.200000000000045, + 0.0, + 99.72409090909093, + 120.0, + 124.41545454545451, + 120.0, + 0.0, + 0.0, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 109.96227272727276, + 47.952272727272714, + 11.233982308648535, + 38.52740325013451, + 161.62968357967037, + 21.962728535423857, + 519.5704951465664, + 0.5004782113116364, + 10.13011689299087, + 36.109951963721926, + 44.377460775206174, + 0.0, + 0.0, + 0.0, + 100.08954545454549, + 80.85954545454547 + ], + "akku_soc_pro_stunde": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 96.13812844352617, + 96.13812844352617, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 96.32360537190083, + 99.65693870523415, + 95.72968319559227, + 99.0630165289256, + 99.0630165289256, + 99.0630165289256, + 96.07429407713497, + 93.45381370523414, + 91.05922865013771, + 88.95484676308537, + 88.95484676308537, + 85.51196625344349, + 82.04093491735533, + 80.52728994490354, + 80.83934500903268, + 81.90955065486975, + 85.41748453961112, + 85.56748624593055, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.84060778236915 + ], + "Electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ] + }, + "ev_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.875, + 0.75, + 1.0, + 0.625, + 0.375, + 0.75, + 0.875, + 0.1, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59110.8, + "initial_soc_percentage": 5 + }, + "start_hour": 10, + "start_solution": [ + 0.0, + 0.0, + 2.0, + 0.0, + 1.0, + 1.0, + 0.0, + 2.0, + 1.0, + 1.0, + 1.0, + 0.0, + 2.0, + 0.0, + 2.0, + 0.0, + 2.0, + 2.0, + 0.0, + 2.0, + 1.0, + 2.0, + 1.0, + 2.0, + 2.0, + 2.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 2.0, + 0.0, + 2.0, + 1.0, + 1.0, + 6.0, + 2.0, + 1.0, + 1.0, + 6.0, + 4.0, + 6.0, + 4.0, + 2.0, + 5.0, + 0.0, + 5.0, + 4.0, + 6.0, + 3.0, + 1.0, + 4.0, + 5.0, + 3.0, + 6.0, + 4.0, + 1.0, + 4.0, + 2.0, + 2.0, + 3.0, + 5.0, + 2.0, + 0.0, + 0.0, + 4.0, + 5.0, + 0.0, + 0.0, + 6.0, + 1.0, + 1.0, + 6.0, + 0.0, + 5.0, + 2.0, + 6.0, + 3.0, + 4.0, + 5.0, + 6.0, + 2.0, + 2.0, + 14.0 + ], + "washingstart": 14, + "extra_data": { + "balance": [ + 14.221506700636702, + 15.299930235610336, + 14.212059628393439, + 14.58029583495025, + 15.218700819591465, + 14.62471850080464, + 14.831865327418862, + 14.026261220300325, + 12.78299149726557, + 15.254055526881615, + 15.024197168967335, + 14.368857308300326, + 15.876830436675256, + 15.2126417415724, + 14.682943106802838, + 13.897410738312315, + 14.371854631136598, + 14.904987324786175, + 13.579695501563089, + 14.949201631285609, + 14.364148546823948, + 14.026261220300325, + 14.563872321673, + 14.79639929946259, + 14.640597806802838, + 14.863840015053594, + 14.831865327418862, + 13.89437687019911, + 14.634695026161982, + 14.586398818526943, + 15.2126417415724, + 14.651436693935828, + 14.832611524977176, + 14.221506700636702, + 14.561252068383046, + 14.364148546823948, + 14.442675133190964, + 14.682943106802838, + 14.682943106802838, + 14.586398818526943, + 14.67188363579013, + 15.16539975383106, + 14.586398818526943, + 14.831865327418862, + 14.832025945708118, + 14.483057329352384, + 14.354852466693526, + 14.282854736298434, + 14.723303889616528, + 14.692805764802324, + 15.16539975383106, + 14.831865327418862, + 14.563872321673, + 14.561252068383046, + 15.289675639468703, + 14.370522152302872, + 14.682943106802838, + 14.904987324786175, + 14.831865327418862, + 14.364148546823948, + 15.289675639468703, + 15.299930235610336, + 14.863840015053594, + 14.863840015053594, + 14.483094914986774, + 14.916199859214707, + 14.221506700636702, + 14.026261220300325, + 14.563872321673, + 13.396510478462671, + 12.78299149726557, + 14.370522152302872, + 14.503025425809126, + 14.346794687236407, + 14.70166818618751, + 15.254055526881615, + 14.371854631136598, + 14.682943106802838, + 14.363628070777011, + 13.972460083694896, + 14.282854736298434, + 14.916199859214707, + 14.832025945708118, + 14.246646590665076, + 14.868509073896895, + 14.026261220300325, + 15.784917496972003, + 14.94040577786713, + 14.371854631136598, + 14.727666169888666, + 14.85376068880464, + 14.831865327418862, + 14.354852466693526, + 14.354852466693526, + 14.364148546823948, + 14.282854736298434, + 14.364148546823948, + 14.94040577786713, + 15.024197168967335, + 14.94040577786713 + ], + "losses": [ + 9866.696042401421, + 7516.761899409781, + 6697.154058947939, + 4568.920397989366, + 8910.757324882812, + 6472.472943050138, + 6885.562555311458, + 6436.220488631327, + 5766.286846118221, + 6041.605332098291, + 8260.05972288515, + 6282.398215904054, + 9177.80638291733, + 5598.710581658907, + 7610.968464030486, + 6346.493167321551, + 8246.724648677668, + 6548.194868796616, + 7249.947693225658, + 7652.859889457773, + 6186.4811335689365, + 6436.220488631327, + 6946.020087822352, + 7272.821841283314, + 7597.168464030485, + 7453.961526470664, + 6885.562555311458, + 8580.159126919834, + 6038.1871113542065, + 7167.8048435292785, + 5598.710581658907, + 5423.084862470211, + 6222.116774404548, + 9866.696042401421, + 7414.368929686839, + 6186.4811335689365, + 6236.749009784242, + 7610.968464030486, + 7610.968464030486, + 7167.8048435292785, + 6399.140623558964, + 6653.566416949209, + 7167.8048435292785, + 6885.562555311458, + 6971.287059443688, + 6202.88802785612, + 6454.121733361086, + 4071.7104177832302, + 5822.76597499057, + 5146.7731654160325, + 6653.566416949209, + 6885.562555311458, + 6946.020087822352, + 7414.368929686839, + 7669.76903437066, + 5257.593312732217, + 7610.968464030486, + 6548.194868796616, + 6885.562555311458, + 6186.4811335689365, + 7669.76903437066, + 7516.761899409781, + 7453.961526470664, + 7453.961526470664, + 6537.83547520668, + 4768.801291163598, + 9866.696042401421, + 6436.220488631327, + 6946.020087822352, + 6485.918693035536, + 5766.286846118221, + 5257.593312732217, + 7723.667195260369, + 7004.197548597559, + 7032.875331945162, + 6041.605332098291, + 8246.724648677668, + 7610.968464030486, + 4185.388626781521, + 10298.0699186246, + 4071.7104177832302, + 4768.801291163598, + 6971.287059443688, + 10370.177313162452, + 5398.059968038848, + 6436.220488631327, + 9014.331353048226, + 10449.049125174153, + 8246.724648677668, + 4730.372317054354, + 6318.650670322865, + 6885.562555311458, + 6454.121733361086, + 6454.121733361086, + 6186.4811335689365, + 4071.7104177832302, + 6186.4811335689365, + 10449.049125174153, + 8260.05972288515, + 10449.049125174153 + ], + "constraints": [ + -18.955, + -18.955, + -18.518, + -18.518, + -18.518, + -19.391999999999996, + -19.391999999999996, + -18.955, + -18.518, + -19.829000000000008, + -19.391999999999996, + -18.955, + -18.518, + -19.829000000000008, + -19.391999999999996, + -19.391999999999996, + -18.955, + -18.955, + -18.518, + -18.518, + -18.518, + -18.955, + -18.955, + -19.391999999999996, + -18.955, + -18.518, + -19.391999999999996, + -18.955, + -18.518, + -18.518, + -19.829000000000008, + -19.391999999999996, + -18.518, + -18.955, + -18.955, + -18.518, + -18.518, + -19.391999999999996, + -19.391999999999996, + -18.518, + -18.518, + -18.955, + -18.518, + -19.391999999999996, + -19.391999999999996, + -18.955, + -18.518, + -18.518, + -18.955, + -18.518, + -18.955, + -19.391999999999996, + -18.955, + -18.955, + -19.829000000000008, + -18.955, + -19.391999999999996, + -18.955, + -19.391999999999996, + -18.518, + -19.829000000000008, + -18.955, + -18.518, + -18.518, + -18.518, + -19.829000000000008, + -18.955, + -18.955, + -18.955, + -18.518, + -18.518, + -18.955, + -19.391999999999996, + -19.391999999999996, + -18.955, + -19.829000000000008, + -18.955, + -19.391999999999996, + -18.955, + -18.955, + -18.518, + -19.829000000000008, + -19.391999999999996, + -19.391999999999996, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.391999999999996, + -19.391999999999996, + -18.518, + -18.518, + -18.518, + -18.518, + -18.518, + -19.829000000000008, + -19.391999999999996, + -19.829000000000008 + ] + }, + "fitness_history": { + "gen": [ + 0, + 1, + 2, + 3 + ], + "avg": [ + 16.685518856626615, + 15.61390152051758, + 14.988718004044808, + 14.601365045870402 + ], + "max": [ + 20.378343583283172, + 17.307985915077595, + 16.378933811317683, + 15.876830436675256 + ], + "min": [ + 13.820271169793038, + 13.89437687019911, + 13.579695501563089, + 12.78299149726557 + ] + }, + "fixed_seed": 42, + "eautocharge_hours_float": [ + 1.0, + 0.5, + 0.375, + 0.375, + 1.0, + 0.75, + 1.0, + 0.75, + 0.5, + 0.875, + 0.0, + 0.875, + 0.75, + 1.0, + 0.625, + 0.375, + 0.75, + 0.875, + 0.625, + 1.0, + 0.75, + 0.375, + 0.75, + 0.5, + 0.5, + 0.625, + 0.875, + 0.5, + 0.0, + 0.0, + 0.75, + 0.875, + 0.0, + 0.0, + 1.0, + 0.375, + 0.375, + 1.0, + 0.0, + 0.875, + 0.5, + 1.0, + 0.625, + 0.75, + 0.875, + 1.0, + 0.5, + 0.5 + ], + "eauto_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.875, + 0.75, + 1.0, + 0.625, + 0.375, + 0.75, + 0.875, + 0.1, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59110.8, + "initial_soc_percentage": 5 + } +} \ No newline at end of file diff --git a/tests/testdata/genetic/optimize_result_2_be.json b/tests/testdata/genetic/optimize_result_2_be.json new file mode 100644 index 00000000..ede137fc --- /dev/null +++ b/tests/testdata/genetic/optimize_result_2_be.json @@ -0,0 +1,2123 @@ +{ + "parameters": { + "ems": { + "pv_forecast_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "electricity_price_per_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "feed_in_tariff_per_wh": 7e-05, + "price_per_wh_battery": 0.0, + "total_load": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "pv_prognose_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "strompreis_euro_pro_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "einspeiseverguetung_euro_pro_wh": 7e-05, + "preis_euro_pro_wh_akku": 0.0, + "gesamtlast": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ] + }, + "pv_battery": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "inverter": { + "device_id": "inverter1", + "max_power_wh": 10000.0, + "battery_id": "battery1" + }, + "ev": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + }, + "dishwasher": { + "device_id": "dishwasher1", + "consumption_wh": 5000, + "duration_h": 2, + "time_windows": { + "windows": [ + { + "start_time": "00:00:00.000000", + "duration": "2 days" + } + ] + } + }, + "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.0, + 18.6, + 19.2, + 19.1, + 18.7, + 18.5, + 17.7, + 16.2, + 14.6, + 13.6, + 13.0, + 12.6, + 12.2, + 11.7, + 11.6, + 11.3, + 11.0, + 10.7, + 10.2, + 11.4, + 14.4, + 16.4, + 18.3, + 19.5, + 20.7, + 21.9, + 22.7, + 23.1, + 23.1, + 22.8, + 21.8, + 20.2, + 19.1, + 18.0, + 17.4 + ], + "start_solution": null, + "pv_akku": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "eauto": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + } + }, + "ac_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "dc_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "discharge_allowed": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1 + ], + "ev_charge_hours_float": [ + 0.625, + 0.375, + 0.5, + 1.0, + 1.0, + 0.75, + 0.5, + 0.375, + 0.375, + 0.625, + 0.75, + 0.875, + 0.625, + 0.625, + 0.625, + 1.0, + 0.375, + 0.625, + 0.875, + 0.0, + 0.5, + 0.0, + 0.0, + 0.5, + 0.875, + 0.625, + 0.75, + 0.5, + 0.375, + 0.375, + 0.75, + 0.625, + 1.0, + 0.375, + 0.625, + 0.625, + 0.0, + 0.5, + 0.875, + 0.375, + 0.5, + 0.875, + 0.5, + 0.875, + 0.75, + 0.625, + 0.75, + 0.0 + ], + "result": { + "load_wh_per_hour": [ + 8919.07, + 10240.91, + 7875.5599999999995, + 7687.03, + 7718.67, + 11664.82, + 5149.22, + 6347.78, + 1129.12, + 8678.71, + 3550.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 5694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 6141.98, + 1056.97, + 992.46, + 6155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "ev_soc_per_hour": [ + 5.0, + 18.11, + 33.405, + 44.330000000000005, + 55.254999999999995, + 66.18, + 83.66, + 90.215, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955 + ], + "revenue_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_losses": 10674.660531928814, + "total_balance": 14.366070195145795, + "total_revenue": 0.0, + "total_costs": 14.366070195145795, + "home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "costs_per_hour": [ + 0.81824412, + 1.061242392, + 0.49579402599999994, + 0.399351386, + 0.13127915000000007, + 1.2316083, + 0.259218932, + 0.7558691399999999, + 0.061530097476751734, + 2.4510570300000003, + 0.0, + 0.0, + 0.26650619799999997, + 0.19588158, + 0.13029273082161758, + 0.28801899, + 1.870021256, + 0.199865757, + 0.0, + 0.0, + 0.16677339, + 0.0, + 1.7663038380000002, + 0.08545095, + 0.007989913613567745, + 1.134255713342252, + 0.025392879919306634, + 0.010682755832597498, + 4.174095896658514e-14, + 0.0003442778967139274, + 0.0, + 0.0, + 0.04565364324294593, + 0.0, + 0.0, + 0.293043269, + 0.214398479, + 0.0 + ], + "grid_consumption_wh_per_hour": [ + 3588.79, + 4797.66, + 2368.8199999999997, + 2125.34, + 714.2500000000003, + 6145.75, + 1179.3400000000001, + 3329.8199999999997, + 205.30563055305882, + 7671.54, + 0.0, + 0.0, + 912.38, + 704.61, + 385.0258003002884, + 868.05, + 5694.34, + 608.79, + 0.0, + 0.0, + 506.91, + 0.0, + 5806.39, + 351.65, + 35.04348076126204, + 5127.738306248879, + 121.32288542430308, + 56.853410498124, + 2.270998855635753e-10, + 1.7179535764168035, + 0.0, + 0.0, + 152.3311419517715, + 0.0, + 0.0, + 987.01, + 733.99, + 0.0 + ], + "grid_feed_in_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "losses_per_hour": [ + 1014.0, + 1083.0, + 945.0, + 945.0, + 479.4, + 552.0, + 207.0, + 276.0, + 73.03732433363291, + 600.0, + 440.6331818181816, + 133.72909090909081, + 0.0, + 0.0, + 17.910572686324315, + 0.0, + 600.0, + 0.0, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 600.0, + 0.0, + 11.233982308648535, + 638.5274032501345, + 145.08565374908358, + 14.209990740225123, + 538.2983999999728, + 441.7178455708299, + 260.56941082122324, + 171.99990368477063, + 41.441862965787436, + 35.132727272727266, + 77.27590909090907, + 0.0, + 0.0, + 80.85954545454547 + ], + "battery_soc_per_hour": [ + 80.0, + 61.06060606060606, + 42.12121212121212, + 23.18181818181818, + 4.242424242424243, + 0.0, + 0.0, + 0.0, + 0.0, + 2.0288145648231377, + 18.695481231489804, + 4.786605542784571, + 0.5653589863107422, + 0.5653589863107422, + 0.5653589863107422, + 0.0, + 0.0, + 16.666666666666664, + 16.666666666666664, + 14.272081611570247, + 12.167699724517906, + 12.167699724517906, + 8.724819214876034, + 25.391485881542703, + 25.391485881542703, + 25.703540945671826, + 43.44041325817556, + 47.47057030676122, + 47.86529227176747, + 62.81802560510005, + 75.08796575984533, + 82.04461281340734, + 85.81933369454761, + 86.97049655470836, + 85.86150895140257, + 83.42224414424004, + 83.42224414424004, + 83.42224414424004 + ], + "electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "Last_Wh_pro_Stunde": [ + 8919.07, + 10240.91, + 7875.5599999999995, + 7687.03, + 7718.67, + 11664.82, + 5149.22, + 6347.78, + 1129.12, + 8678.71, + 3550.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 5694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 6141.98, + 1056.97, + 992.46, + 6155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "EAuto_SoC_pro_Stunde": [ + 5.0, + 18.11, + 33.405, + 44.330000000000005, + 55.254999999999995, + 66.18, + 83.66, + 90.215, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955 + ], + "Einnahmen_Euro_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Gesamt_Verluste": 10674.660531928814, + "Gesamtbilanz_Euro": 14.366070195145795, + "Gesamteinnahmen_Euro": 0.0, + "Gesamtkosten_Euro": 14.366070195145795, + "Home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Kosten_Euro_pro_Stunde": [ + 0.81824412, + 1.061242392, + 0.49579402599999994, + 0.399351386, + 0.13127915000000007, + 1.2316083, + 0.259218932, + 0.7558691399999999, + 0.061530097476751734, + 2.4510570300000003, + 0.0, + 0.0, + 0.26650619799999997, + 0.19588158, + 0.13029273082161758, + 0.28801899, + 1.870021256, + 0.199865757, + 0.0, + 0.0, + 0.16677339, + 0.0, + 1.7663038380000002, + 0.08545095, + 0.007989913613567745, + 1.134255713342252, + 0.025392879919306634, + 0.010682755832597498, + 4.174095896658514e-14, + 0.0003442778967139274, + 0.0, + 0.0, + 0.04565364324294593, + 0.0, + 0.0, + 0.293043269, + 0.214398479, + 0.0 + ], + "Netzbezug_Wh_pro_Stunde": [ + 3588.79, + 4797.66, + 2368.8199999999997, + 2125.34, + 714.2500000000003, + 6145.75, + 1179.3400000000001, + 3329.8199999999997, + 205.30563055305882, + 7671.54, + 0.0, + 0.0, + 912.38, + 704.61, + 385.0258003002884, + 868.05, + 5694.34, + 608.79, + 0.0, + 0.0, + 506.91, + 0.0, + 5806.39, + 351.65, + 35.04348076126204, + 5127.738306248879, + 121.32288542430308, + 56.853410498124, + 2.270998855635753e-10, + 1.7179535764168035, + 0.0, + 0.0, + 152.3311419517715, + 0.0, + 0.0, + 987.01, + 733.99, + 0.0 + ], + "Netzeinspeisung_Wh_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Verluste_Pro_Stunde": [ + 1014.0, + 1083.0, + 945.0, + 945.0, + 479.4, + 552.0, + 207.0, + 276.0, + 73.03732433363291, + 600.0, + 440.6331818181816, + 133.72909090909081, + 0.0, + 0.0, + 17.910572686324315, + 0.0, + 600.0, + 0.0, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 600.0, + 0.0, + 11.233982308648535, + 638.5274032501345, + 145.08565374908358, + 14.209990740225123, + 538.2983999999728, + 441.7178455708299, + 260.56941082122324, + 171.99990368477063, + 41.441862965787436, + 35.132727272727266, + 77.27590909090907, + 0.0, + 0.0, + 80.85954545454547 + ], + "akku_soc_pro_stunde": [ + 80.0, + 61.06060606060606, + 42.12121212121212, + 23.18181818181818, + 4.242424242424243, + 0.0, + 0.0, + 0.0, + 0.0, + 2.0288145648231377, + 18.695481231489804, + 4.786605542784571, + 0.5653589863107422, + 0.5653589863107422, + 0.5653589863107422, + 0.0, + 0.0, + 16.666666666666664, + 16.666666666666664, + 14.272081611570247, + 12.167699724517906, + 12.167699724517906, + 8.724819214876034, + 25.391485881542703, + 25.391485881542703, + 25.703540945671826, + 43.44041325817556, + 47.47057030676122, + 47.86529227176747, + 62.81802560510005, + 75.08796575984533, + 82.04461281340734, + 85.81933369454761, + 86.97049655470836, + 85.86150895140257, + 83.42224414424004, + 83.42224414424004, + 83.42224414424004 + ], + "Electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ] + }, + "ev_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.75, + 0.875, + 0.625, + 0.625, + 0.625, + 1.0, + 0.375, + 0.5, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59373.0, + "initial_soc_percentage": 5 + }, + "start_hour": 10, + "start_solution": [ + 2.0, + 2.0, + 0.0, + 0.0, + 2.0, + 0.0, + 0.0, + 2.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 0.0, + 2.0, + 1.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 2.0, + 0.0, + 1.0, + 1.0, + 0.0, + 1.0, + 2.0, + 0.0, + 0.0, + 2.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 0.0, + 0.0, + 1.0, + 3.0, + 1.0, + 2.0, + 6.0, + 6.0, + 4.0, + 2.0, + 1.0, + 1.0, + 3.0, + 4.0, + 5.0, + 3.0, + 3.0, + 3.0, + 6.0, + 1.0, + 3.0, + 5.0, + 0.0, + 2.0, + 0.0, + 0.0, + 2.0, + 5.0, + 3.0, + 4.0, + 2.0, + 1.0, + 1.0, + 4.0, + 3.0, + 6.0, + 1.0, + 3.0, + 3.0, + 0.0, + 2.0, + 5.0, + 1.0, + 2.0, + 5.0, + 2.0, + 5.0, + 4.0, + 3.0, + 4.0, + 0.0, + 19.0 + ], + "washingstart": 19, + "extra_data": { + "balance": [ + 14.597720997785041, + 16.2697167644664, + 15.312269902913977, + 14.85376068880464, + 16.720875566168758, + 16.582410786880896, + 16.60921781689759, + 14.108577200300326, + 14.563872321673, + 16.218166803135617, + 16.705942005168758, + 14.371854631136598, + 15.236710891103375, + 15.5480471649553, + 14.844151433079656, + 14.844151433079656, + 14.371854631136598, + 15.286536561962663, + 15.492537996981472, + 18.320478387677934, + 15.187933957880919, + 15.525973084489191, + 14.563872321673, + 15.621042845697431, + 14.501732154554485, + 15.42746783574979, + 16.621213692326418, + 13.89437687019911, + 14.662083400305969, + 15.5480471649553, + 15.47179762089056, + 15.959975832818147, + 17.02329841917316, + 14.44170891147855, + 15.546075803013684, + 16.720875566168758, + 14.918614925810312, + 15.286536561962663, + 16.548270329023996, + 15.5480471649553, + 16.621213692326418, + 16.2697167644664, + 15.101366547867132, + 16.405841140620637, + 14.8367674156028, + 15.834297629942883, + 14.205056609914148, + 15.2198870074443, + 16.733802065830545, + 16.078873980639404, + 15.964878569913978, + 15.312269902913977, + 14.563872321673, + 17.109810328909987, + 15.42746783574979, + 15.187933957880919, + 17.109810328909987, + 15.834297629942883, + 15.567674618888258, + 16.720875566168758, + 15.42746783574979, + 16.2697167644664, + 14.205056609914148, + 16.43824416121008, + 16.2697167644664, + 15.65776938938505, + 14.597720997785041, + 14.108577200300326, + 14.563872321673, + 13.396510478462671, + 15.466247470015084, + 15.187933957880919, + 14.61918488159425, + 14.346794687236407, + 15.202454906071104, + 15.525973084489191, + 14.563872321673, + 17.979393423628157, + 15.187933957880919, + 14.597720997785041, + 15.795038161842308, + 15.91724628240999, + 16.032992816445045, + 14.94040577786713, + 14.916199859214707, + 15.101366547867132, + 16.720875566168758, + 14.94040577786713, + 14.597720997785041, + 15.834297629942883, + 15.027558602568279, + 16.2697167644664, + 16.2697167644664, + 16.032992816445045, + 15.016993650470285, + 15.248611447727734, + 16.720875566168758, + 14.94040577786713, + 16.405841140620637, + 14.94040577786713 + ], + "losses": [ + 8784.99689313645, + 6028.088698749184, + 6674.62797204885, + 6318.650670322865, + 8709.528691828156, + 7682.38760014809, + 10645.367632658927, + 6401.087761358599, + 6946.020087822352, + 10113.7752361747, + 8725.431419100882, + 8246.724648677668, + 11545.213367219221, + 7431.5621869248835, + 7753.098482401574, + 7753.098482401574, + 8246.724648677668, + 11090.321587280538, + 6997.435288622984, + 8777.3817424105, + 6313.255100148023, + 9930.331900277071, + 6946.020087822352, + 6493.430038527615, + 8021.589374853718, + 5966.241354866967, + 8636.573023402168, + 8580.159126919834, + 5657.418808526202, + 7431.5621869248835, + 6724.760515895711, + 8470.917240099943, + 8670.005540635548, + 7729.3334762521035, + 7735.852114928777, + 8709.528691828156, + 8099.456706356224, + 11090.321587280538, + 11050.546358189142, + 7431.5621869248835, + 8636.573023402168, + 6028.088698749184, + 10379.413670628699, + 9549.127338777747, + 8232.459722885149, + 8624.677214810807, + 10560.026497676323, + 7622.963913777213, + 8849.619944889773, + 8780.63126214931, + 8159.86797204885, + 6674.62797204885, + 6946.020087822352, + 9999.62070247934, + 5966.241354866967, + 6313.255100148023, + 9999.62070247934, + 8624.677214810807, + 8043.376906991039, + 8709.528691828156, + 5966.241354866967, + 6028.088698749184, + 10560.026497676323, + 7981.222906704189, + 6028.088698749184, + 11757.582021635626, + 8784.99689313645, + 6401.087761358599, + 6946.020087822352, + 6485.918693035536, + 6848.858106448798, + 6313.255100148023, + 7101.382277904996, + 7004.197548597559, + 12339.89333513819, + 9930.331900277071, + 6946.020087822352, + 8472.784284178542, + 6313.255100148023, + 8784.99689313645, + 8719.164316005646, + 10402.800897121735, + 8654.317085360193, + 10449.049125174153, + 4768.801291163598, + 10379.413670628699, + 8709.528691828156, + 10449.049125174153, + 8784.99689313645, + 8624.677214810807, + 5209.828315663131, + 6028.088698749184, + 6028.088698749184, + 8654.317085360193, + 5478.151137990369, + 7776.108597214346, + 8709.528691828156, + 10449.049125174153, + 9549.127338777747, + 10449.049125174153 + ], + "constraints": [ + -18.518, + -19.829000000000008, + -18.518, + -19.391999999999996, + -19.829000000000008, + -18.518, + -19.829000000000008, + -18.955, + -18.955, + -18.518, + -19.829000000000008, + -18.955, + -19.391999999999996, + -19.829000000000008, + -19.391999999999996, + -19.391999999999996, + -18.955, + -19.829000000000008, + -18.955, + -19.829000000000008, + -18.955, + -19.829000000000008, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -19.829000000000008, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.391999999999996, + -19.829000000000008, + -18.518, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.829000000000008, + -18.955, + -18.518, + -19.391999999999996, + -19.829000000000008, + -18.518, + -18.955, + -19.391999999999996, + -19.829000000000008, + -18.518, + -18.955, + -19.829000000000008, + -18.955, + -18.955, + -19.829000000000008, + -19.391999999999996, + -19.829000000000008, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -18.518, + -18.955, + -18.955, + -18.518, + -18.955, + -18.955, + -18.955, + -19.391999999999996, + -19.391999999999996, + -19.829000000000008, + -18.955, + -19.391999999999996, + -18.955, + -18.518, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -18.518, + -19.391999999999996, + -18.955, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -18.518, + -19.829000000000008, + -19.829000000000008, + -18.955, + -19.829000000000008 + ] + }, + "fitness_history": { + "gen": [ + 0, + 1, + 2, + 3 + ], + "avg": [ + 31.761298415855272, + 27.85142881741465, + 25.347529410486928, + 23.620183102769932 + ], + "max": [ + 47.91271540764464, + 35.33836760234757, + 30.369824129831095, + 27.861358064223104 + ], + "min": [ + 19.887612550517698, + 19.940876857848036, + 19.988951680073594, + 19.988951680073594 + ] + }, + "fixed_seed": 42, + "eautocharge_hours_float": [ + 0.625, + 0.375, + 0.5, + 1.0, + 1.0, + 0.75, + 0.5, + 0.375, + 0.375, + 0.625, + 0.75, + 0.875, + 0.625, + 0.625, + 0.625, + 1.0, + 0.375, + 0.625, + 0.875, + 0.0, + 0.5, + 0.0, + 0.0, + 0.5, + 0.875, + 0.625, + 0.75, + 0.5, + 0.375, + 0.375, + 0.75, + 0.625, + 1.0, + 0.375, + 0.625, + 0.625, + 0.0, + 0.5, + 0.875, + 0.375, + 0.5, + 0.875, + 0.5, + 0.875, + 0.75, + 0.625, + 0.75, + 0.0 + ], + "eauto_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.75, + 0.875, + 0.625, + 0.625, + 0.625, + 1.0, + 0.375, + 0.5, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59373.0, + "initial_soc_percentage": 5 + } +} \ No newline at end of file diff --git a/tests/testdata/genetic/optimize_result_2_full.json b/tests/testdata/genetic/optimize_result_2_full.json new file mode 100644 index 00000000..187529ec --- /dev/null +++ b/tests/testdata/genetic/optimize_result_2_full.json @@ -0,0 +1,3711 @@ +{ + "parameters": { + "ems": { + "pv_forecast_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "electricity_price_per_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "feed_in_tariff_per_wh": 7e-05, + "price_per_wh_battery": 0.0, + "total_load": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "pv_prognose_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "strompreis_euro_pro_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "einspeiseverguetung_euro_pro_wh": 7e-05, + "preis_euro_pro_wh_akku": 0.0, + "gesamtlast": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ] + }, + "pv_battery": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "inverter": { + "device_id": "inverter1", + "max_power_wh": 10000.0, + "battery_id": "battery1" + }, + "ev": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + }, + "dishwasher": { + "device_id": "dishwasher1", + "consumption_wh": 5000, + "duration_h": 2, + "time_windows": { + "windows": [ + { + "start_time": "00:00:00.000000", + "duration": "2 days" + } + ] + } + }, + "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.0, + 18.6, + 19.2, + 19.1, + 18.7, + 18.5, + 17.7, + 16.2, + 14.6, + 13.6, + 13.0, + 12.6, + 12.2, + 11.7, + 11.6, + 11.3, + 11.0, + 10.7, + 10.2, + 11.4, + 14.4, + 16.4, + 18.3, + 19.5, + 20.7, + 21.9, + 22.7, + 23.1, + 23.1, + 22.8, + 21.8, + 20.2, + 19.1, + 18.0, + 17.4 + ], + "start_solution": null, + "pv_akku": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "eauto": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + } + }, + "ac_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "dc_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "discharge_allowed": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ], + "ev_charge_hours_float": [ + 0.875, + 0.75, + 0.5, + 1.0, + 0.625, + 0.625, + 0.875, + 0.625, + 0.875, + 0.875, + 0.0, + 1.0, + 0.5, + 0.625, + 1.0, + 0.75, + 0.5, + 0.625, + 0.375, + 0.5, + 0.625, + 0.625, + 0.5, + 0.625, + 0.625, + 0.0, + 0.625, + 0.5, + 0.5, + 1.0, + 0.375, + 0.625, + 0.875, + 0.5, + 0.5, + 0.75, + 0.75, + 0.75, + 0.0, + 0.875, + 0.75, + 1.0, + 1.0, + 0.75, + 0.625, + 0.875, + 0.5, + 0.875 + ], + "result": { + "load_wh_per_hour": [ + 1053.07, + 11551.91, + 6564.5599999999995, + 7687.03, + 14151.67, + 11542.82, + 6460.22, + 7658.78, + 5062.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "ev_soc_per_hour": [ + 5.0, + 5.0, + 22.48, + 31.22, + 42.144999999999996, + 59.62499999999999, + 72.735, + 81.475, + 92.4, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955 + ], + "revenue_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_losses": 7647.623819992857, + "total_balance": 7.824605715847156, + "total_revenue": 0.0, + "total_costs": 7.824605715847156, + "home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "costs_per_hour": [ + 0.027996119999999992, + 1.351235592, + 1.1423217259999998, + 1.226111386, + 1.4948178300000001, + 1.2071595, + 0.0, + 1.0534661399999998, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19588158, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.01995551999999987, + 0.08545095, + 0.007989913613567745, + 0.012219458233588571, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "grid_consumption_wh_per_hour": [ + 122.78999999999996, + 6108.66, + 5457.82, + 6525.34, + 8132.85, + 6023.75, + 0.0, + 4640.82, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 704.61, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 65.59999999999957, + 351.65, + 35.04348076126204, + 55.24167375040041, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "grid_feed_in_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "losses_per_hour": [ + 0.0, + 1152.0, + 276.0, + 345.0, + 552.0, + 414.0, + 615.5918181818183, + 345.0, + 632.3249999999998, + 23.391818181818195, + 99.72409090909093, + 133.72909090909081, + 124.41545454545451, + 0.0, + 70.41409090909087, + 118.37045454545455, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 69.12409090909085, + 109.0704545454546, + 101.01681818181828, + 0.0, + 11.233982308648535, + 48.41330768174522, + 161.62968357967037, + 21.962728535423857, + 538.2984000000038, + 441.95211196761403, + 260.56941082122324, + 171.99990368477063, + 62.214291413756285, + 35.132727272727266, + 77.27590909090907, + 134.59227272727276, + 100.08954545454549, + 80.85954545454547 + ], + "battery_soc_per_hour": [ + 80.0, + 80.0, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 50.341167355371894, + 50.341167355371894, + 36.91550447658402, + 36.17712637741047, + 33.0292699724518, + 28.808023415977967, + 24.88076790633609, + 24.88076790633609, + 22.658100895316807, + 18.921659779614323, + 15.932937327823693, + 13.312456955922865, + 10.917871900826448, + 8.813490013774107, + 6.63154269972452, + 3.1886621900826473, + 0.0, + 0.0, + 0.3120550641291261, + 1.07020564583707, + 4.578139530578446, + 4.728141236897871, + 19.68087457022947, + 31.94341995234899, + 38.90006700591099, + 42.674787887051245, + 43.17025540478875, + 42.06126780148296, + 39.622002994320425, + 35.373509537020155, + 32.214117319389295 + ], + "electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "Last_Wh_pro_Stunde": [ + 1053.07, + 11551.91, + 6564.5599999999995, + 7687.03, + 14151.67, + 11542.82, + 6460.22, + 7658.78, + 5062.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "EAuto_SoC_pro_Stunde": [ + 5.0, + 5.0, + 22.48, + 31.22, + 42.144999999999996, + 59.62499999999999, + 72.735, + 81.475, + 92.4, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955 + ], + "Einnahmen_Euro_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Gesamt_Verluste": 7647.623819992857, + "Gesamtbilanz_Euro": 7.824605715847156, + "Gesamteinnahmen_Euro": 0.0, + "Gesamtkosten_Euro": 7.824605715847156, + "Home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Kosten_Euro_pro_Stunde": [ + 0.027996119999999992, + 1.351235592, + 1.1423217259999998, + 1.226111386, + 1.4948178300000001, + 1.2071595, + 0.0, + 1.0534661399999998, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19588158, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.01995551999999987, + 0.08545095, + 0.007989913613567745, + 0.012219458233588571, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Netzbezug_Wh_pro_Stunde": [ + 122.78999999999996, + 6108.66, + 5457.82, + 6525.34, + 8132.85, + 6023.75, + 0.0, + 4640.82, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 704.61, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 65.59999999999957, + 351.65, + 35.04348076126204, + 55.24167375040041, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Netzeinspeisung_Wh_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Verluste_Pro_Stunde": [ + 0.0, + 1152.0, + 276.0, + 345.0, + 552.0, + 414.0, + 615.5918181818183, + 345.0, + 632.3249999999998, + 23.391818181818195, + 99.72409090909093, + 133.72909090909081, + 124.41545454545451, + 0.0, + 70.41409090909087, + 118.37045454545455, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 69.12409090909085, + 109.0704545454546, + 101.01681818181828, + 0.0, + 11.233982308648535, + 48.41330768174522, + 161.62968357967037, + 21.962728535423857, + 538.2984000000038, + 441.95211196761403, + 260.56941082122324, + 171.99990368477063, + 62.214291413756285, + 35.132727272727266, + 77.27590909090907, + 134.59227272727276, + 100.08954545454549, + 80.85954545454547 + ], + "akku_soc_pro_stunde": [ + 80.0, + 80.0, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 50.341167355371894, + 50.341167355371894, + 36.91550447658402, + 36.17712637741047, + 33.0292699724518, + 28.808023415977967, + 24.88076790633609, + 24.88076790633609, + 22.658100895316807, + 18.921659779614323, + 15.932937327823693, + 13.312456955922865, + 10.917871900826448, + 8.813490013774107, + 6.63154269972452, + 3.1886621900826473, + 0.0, + 0.0, + 0.3120550641291261, + 1.07020564583707, + 4.578139530578446, + 4.728141236897871, + 19.68087457022947, + 31.94341995234899, + 38.90006700591099, + 42.674787887051245, + 43.17025540478875, + 42.06126780148296, + 39.622002994320425, + 35.373509537020155, + 32.214117319389295 + ], + "Electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ] + }, + "ev_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.5, + 0.625, + 1.0, + 0.75, + 0.5, + 0.625, + 0.375, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59373.0, + "initial_soc_percentage": 5 + }, + "start_hour": 10, + "start_solution": [ + 0.0, + 0.0, + 2.0, + 1.0, + 0.0, + 1.0, + 2.0, + 2.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 5.0, + 4.0, + 2.0, + 6.0, + 3.0, + 3.0, + 5.0, + 3.0, + 5.0, + 5.0, + 0.0, + 6.0, + 2.0, + 3.0, + 6.0, + 4.0, + 2.0, + 3.0, + 1.0, + 2.0, + 3.0, + 3.0, + 2.0, + 3.0, + 3.0, + 0.0, + 3.0, + 2.0, + 2.0, + 6.0, + 1.0, + 3.0, + 5.0, + 2.0, + 2.0, + 4.0, + 4.0, + 4.0, + 0.0, + 5.0, + 4.0, + 6.0, + 6.0, + 4.0, + 3.0, + 5.0, + 2.0, + 5.0, + 14.0 + ], + "washingstart": 14, + "extra_data": { + "balance": [ + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 8.250842714342252, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156 + ], + "losses": [ + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7629.147481119599, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857 + ], + "constraints": [ + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -19.829000000000008, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955 + ] + }, + "fitness_history": { + "gen": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400 + ], + "avg": [ + 16.685518856626615, + 15.61390152051758, + 14.988718004044808, + 14.601365045870402, + 14.294721932651825, + 13.906269422267185, + 13.636534240565156, + 13.23072823626663, + 12.852475571881898, + 12.612852652665381, + 12.295333564136946, + 11.999970526857275, + 11.839011933723295, + 11.628652783605627, + 11.435317552346412, + 11.089168473062392, + 10.663574391420076, + 10.41900750133103, + 10.006287879763317, + 9.774884134097023, + 9.635718756196422, + 9.42997374927623, + 9.11760658902115, + 9.075110144016502, + 9.001960354187176, + 8.911528757750508, + 8.868098579546631, + 8.82804758499918, + 8.88711895316457, + 8.914267690878058, + 8.739695394348908, + 8.687561956968164, + 8.746906158433298, + 8.746317176446416, + 8.665567563083144, + 8.729031261892757, + 8.68497653932458, + 8.644913556669787, + 8.59156896642245, + 8.50142537513843, + 8.56477632802283, + 8.418040870698428, + 8.456144558926384, + 8.433398348006772, + 8.399133735788414, + 8.448978160724293, + 8.46253347184992, + 8.425582770381913, + 8.401996479068414, + 8.426815118630254, + 8.430501661077608, + 8.399133735788414, + 8.418292481620844, + 8.399133735788414, + 8.436114275690255, + 8.417918692368414, + 8.436703648948415, + 8.428872518362278, + 8.418740341788414, + 8.419584476232279, + 8.408594093520088, + 8.431600976999738, + 8.459374973238107, + 8.438959622481793, + 8.441699785478896, + 8.394513172273834, + 8.449648663597298, + 8.40599131039926, + 8.371608603302182, + 8.31545190184288, + 8.280797714773154, + 8.29927741712519, + 8.266264743768271, + 8.303326901449077, + 8.313814862512134, + 8.296612467755528, + 8.348234380003065, + 8.301203936022773, + 8.24833547656417, + 8.227187729242848, + 8.219347483369354, + 8.2519094197168, + 8.19544885230745, + 8.216426971379684, + 8.193893201122183, + 8.180912805935014, + 8.2599017130458, + 8.26600292659671, + 8.177135552955821, + 8.22200906403718, + 8.204279298291143, + 8.172506426916408, + 8.211262613309259, + 8.172330753215338, + 8.162285913524919, + 8.14514773669585, + 8.177397233619057, + 8.169823162326345, + 8.141168017847153, + 8.162627896614817, + 8.193039747215776, + 8.156378264557155, + 8.141168017847153, + 8.146073660377544, + 8.146313280297544, + 8.174800388526597, + 8.214851270488358, + 8.15226130226548, + 8.178459692580764, + 8.141168017847153, + 8.170316383727155, + 8.158910532063455, + 8.14621719687824, + 8.165783626668683, + 8.166436218315276, + 8.141168017847153, + 8.145859508928682, + 8.23130400780516, + 8.21369537979069, + 8.165705623544541, + 8.159173072950614, + 8.174033412120677, + 8.183491543957649, + 8.141168017847153, + 8.141168017847153, + 8.146039820248362, + 8.254502255213815, + 8.159042228956597, + 8.189669982638963, + 8.148515380907154, + 8.15912962832345, + 8.137353854247154, + 8.134419882247155, + 8.134248947419568, + 8.188101483479088, + 8.119305582757157, + 8.129441270107264, + 8.132543839522796, + 8.132166728856808, + 8.123171074599998, + 8.12145157452044, + 8.140533962890213, + 8.142527235975683, + 8.180145472492644, + 8.111828297847156, + 8.111828297847156, + 8.152802215006865, + 8.14007168297701, + 8.149149068496442, + 8.186868689405541, + 8.250352896623577, + 8.135618436353193, + 8.148144378488837, + 8.142800609254536, + 8.139733636608685, + 8.131705874478685, + 8.145833285092044, + 8.169007698984986, + 8.17983232565918, + 8.111828297847156, + 8.14451735035165, + 8.144497729229586, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.15809905891819, + 8.215053032589857, + 8.163948567618043, + 8.146572509308685, + 8.128860913878244, + 8.158231737061115, + 8.137898938608684, + 8.199424144490163, + 8.174708592757476, + 8.150678833925408, + 8.096451746888599, + 8.084804269967156, + 8.117437702397705, + 8.184391310619809, + 8.120703948098683, + 8.084675497847154, + 8.1569948318699, + 8.084675497847154, + 8.109059149694986, + 8.084975061987155, + 8.124808552909935, + 8.084675497847154, + 8.084675497847154, + 8.198057881681093, + 8.134010285489602, + 8.15174455763613, + 8.097757860242107, + 8.084675497847154, + 8.084675497847154, + 8.085856324899812, + 8.084675497847154, + 8.111284392517877, + 8.084675497847154, + 8.100220497822106, + 8.206805389293631, + 8.101790117226678, + 8.130757680707598, + 8.186792246908448, + 8.084675497847154, + 8.145693495458397, + 8.133296136565145, + 8.13882711458339, + 8.19293136518798, + 8.13455300575631, + 8.175394281599221, + 8.18449413018885, + 8.146807632570802, + 8.088850724332893, + 8.084623186980389, + 8.16295342734593, + 8.127652257214406, + 8.100263747469963, + 8.0887758157183, + 8.08374697996204, + 8.111572804663192, + 8.11604455828245, + 8.083367726177979, + 8.0914290776862, + 8.132510847095304, + 8.10434566549442, + 8.085735934075563, + 8.157326986901571, + 8.125064651218942, + 8.211820347515971, + 8.16993040353364, + 8.115654436551528, + 8.132316126075843, + 8.160990684383917, + 8.111265198038055, + 8.156557490057562, + 8.10187896058442, + 8.112775258111858, + 8.106453305243638, + 8.220333295603616, + 8.124961286484455, + 8.106302613033863, + 8.111419156269621, + 8.083367726177979, + 8.083367726177979, + 8.093603355567314, + 8.139210391358562, + 8.110036521623131, + 8.113560356335407, + 8.110963455727157, + 8.083367726177979, + 8.133512862746205, + 8.083367726177979, + 8.117088332377712, + 8.130307235032216, + 8.101624925929164, + 8.11965203083701, + 8.180899972831305, + 8.083367726177979, + 8.141093896874011, + 8.140031303153256, + 8.083367726177979, + 8.13117208065127, + 8.09968043336511, + 8.08057130737264, + 8.07591060936374, + 8.073114190558401, + 8.084974554553552, + 8.114024898850445, + 8.130580789484759, + 8.15148102670518, + 7.997717710372562, + 7.993882324407118, + 7.997954934994394, + 8.052178594399262, + 8.081809447495395, + 8.01944523508, + 8.062259953037662, + 7.990153765999999, + 8.012249281599999, + 8.025581669612963, + 7.990153765999999, + 8.024693670932892, + 7.9817279947293, + 8.073339596072893, + 7.958847192226783, + 7.908897375232877, + 7.981073744600643, + 7.905294172223215, + 7.889175948011995, + 7.896894958877483, + 7.881615933811338, + 7.855652605026348, + 7.843852970496855, + 7.841487443965482, + 7.854328668408189, + 7.824605715847156, + 7.8315894370576515, + 7.853002484242106, + 7.854386317037156, + 7.961732063594212, + 7.848873367987621, + 7.884386176018038, + 7.859252830331092, + 7.893095672331686, + 7.8285512670286845, + 7.824605715847156, + 7.843409550807009, + 7.86083510781808, + 7.918183767100402, + 7.824605715847156, + 7.86097664896482, + 7.859428037576122, + 7.851663650881569, + 7.85875965103216, + 7.893317003117135, + 7.847851779903177, + 7.824605715847156, + 7.8310046852786845, + 7.885905602036348, + 7.850516160661503, + 7.824605715847156, + 7.824605715847156, + 7.834067664479585, + 7.881132093321114, + 7.9081356059611005, + 7.8642187160866, + 7.886482464563635, + 7.829646484548684, + 7.828569868128684, + 7.824605715847156, + 7.824605715847156, + 7.84536899845237, + 7.865666364318685, + 7.852444354538684, + 7.9143996273817985, + 7.8290304915736355, + 7.824605715847156, + 7.849826239641154, + 7.848705587298684, + 7.824605715847156, + 7.866141500567724, + 7.9812124582240065, + 7.842546579999557, + 7.835387742634675, + 7.8493572803221445, + 7.824605715847156, + 7.854698626248685, + 7.834611325908684, + 7.870019734500306, + 7.8286799669558835, + 7.824605715847156, + 7.880020098015384, + 7.881253137683635, + 7.864146978043147, + 7.861112207028684, + 7.824605715847156, + 7.856779181835481, + 7.824605715847156, + 7.854798532027877, + 7.834222602958778, + 7.892629141147934, + 7.870801932128684, + 7.8523061275975605, + 7.8573088526304335, + 7.873578015310929, + 7.824605715847156, + 7.859688484167651, + 7.824605715847156, + 7.849825203465607, + 7.824605715847156, + 7.944211256840067, + 7.994467984697903, + 7.854117959634821, + 7.854117959634821, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.915913393744861, + 7.849590117845483, + 7.847305172483881, + 7.824605715847156, + 7.851018528718685, + 7.888731513641822, + 7.830619442823106, + 7.838634122246349, + 7.824605715847156, + 7.870693920287708, + 7.894634041651334, + 7.844378004013177, + 7.824605715847156, + 7.824605715847156, + 7.860646889810319, + 7.885672821246865, + 7.904507235534523, + 7.859788895111795, + 7.875508147788871, + 7.868663564898684, + 7.85813289112666, + 7.824605715847156, + 7.881001242110212, + 7.828868085832108 + ], + "max": [ + 20.378343583283172, + 17.307985915077595, + 16.378933811317683, + 15.876830436675256, + 15.257367724921895, + 15.425441262719918, + 14.695906864210652, + 14.205887574069676, + 14.0012442339641, + 14.054847707351685, + 13.397401216557196, + 13.542012343726993, + 13.463633264872353, + 12.747010122791544, + 14.076924013791542, + 12.442409063062858, + 12.208054810930621, + 12.949177141290827, + 14.178232111791543, + 13.653644520931202, + 12.08494115562414, + 13.0273172943248, + 10.54295383185262, + 12.12810276297237, + 13.672298956481912, + 11.587850021788457, + 9.27923190395582, + 9.080545508955817, + 11.203523149174847, + 12.086527035867087, + 9.889182279071562, + 9.085096024417796, + 12.11087678986184, + 12.371973007828885, + 10.733598132328467, + 12.413702006174848, + 12.765383758834234, + 12.872745882832598, + 10.975978674417837, + 9.130962369788415, + 12.089863543031365, + 8.563979395788417, + 12.858290001215318, + 11.82559495762414, + 8.399133735788418, + 11.275446660989843, + 11.912059415174848, + 10.098734568832597, + 8.685408063788417, + 10.428934692788417, + 11.535926264707724, + 8.399133735788418, + 10.315008319031364, + 8.399133735788418, + 12.097187725972368, + 10.277629393788416, + 10.277629393788416, + 11.373011993174847, + 9.379464035788416, + 9.435436356174849, + 9.285746338788416, + 11.667223368585937, + 11.844930288071563, + 12.41377067262414, + 11.076394276174891, + 8.399133735788418, + 11.664446087788416, + 10.05607813440558, + 10.455879667342252, + 9.654429803174892, + 8.399133735788418, + 9.75469967117485, + 8.347532775788418, + 11.771373404381283, + 11.21076613234225, + 10.022171753342294, + 11.750412244071564, + 11.4146234052616, + 8.784965715342253, + 8.784965715342253, + 8.784965715342253, + 11.146134761955862, + 9.806130508955821, + 12.10627739534225, + 9.651341280672646, + 8.378694641875125, + 11.749863906342252, + 11.915271475504504, + 8.17713555295582, + 11.058224952031363, + 11.058224952031363, + 8.292822202753339, + 12.28498775934225, + 9.580218505449292, + 9.193759224101965, + 8.17713555295582, + 11.224227791577063, + 10.104178906613567, + 8.141168017847157, + 10.287155894613567, + 11.774321783368599, + 9.662192688847156, + 8.141168017847157, + 8.631732270886307, + 8.631732270886307, + 10.067642284342252, + 11.834501920613567, + 9.250496459679754, + 10.463153016564025, + 8.141168017847157, + 10.389809885847157, + 9.915419439477274, + 8.64608592095582, + 10.602728899999999, + 10.667988064659344, + 8.141168017847157, + 8.610317126, + 12.165370255000003, + 12.912393792957877, + 10.594928587585937, + 9.94167352819316, + 11.112847970127445, + 12.373520628896715, + 8.141168017847157, + 8.141168017847157, + 8.628348257967877, + 12.82817770652491, + 9.928589128791543, + 11.879798822000001, + 9.022602923847156, + 10.113367385476751, + 8.141168017847157, + 8.141168017847157, + 8.945586695088396, + 10.292685456644676, + 8.742197908847155, + 9.84378580385793, + 10.183382465411087, + 9.234069845659388, + 9.023429851000042, + 9.0741559651755, + 10.276932427999999, + 11.18172211069981, + 11.918621505242946, + 8.111828297847156, + 8.111828297847156, + 10.160524155832597, + 10.936166810832596, + 11.843905362775862, + 12.484503124613566, + 13.168252166955098, + 9.308044030342252, + 11.743436362015299, + 11.131087813090103, + 10.902362174000043, + 10.099585960999999, + 10.03281247023073, + 11.052432636644719, + 12.102190842999999, + 8.111828297847156, + 11.380733548296448, + 10.960018972847196, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 11.381464692999998, + 12.754662392779746, + 10.517645788999998, + 10.436691916000001, + 9.251906433955861, + 11.312612822, + 11.071878774000002, + 10.89554028800004, + 12.14378393541109, + 10.572022255411088, + 8.724300251919306, + 8.097552709847157, + 11.360895952902291, + 11.934223609743869, + 11.687520523, + 8.084675497847156, + 12.299611456136137, + 8.084675497847156, + 10.523040682630118, + 8.114631911847155, + 10.486561177342253, + 8.084675497847156, + 8.084675497847156, + 11.603970467539394, + 10.578668317444219, + 10.985625183659346, + 9.392911737342253, + 8.084675497847156, + 8.084675497847156, + 8.202758203112744, + 8.084675497847156, + 10.745564964919305, + 8.084675497847156, + 9.63917549534225, + 11.692944589504545, + 9.79613743579946, + 10.857220567093968, + 11.281861807922699, + 8.084675497847156, + 12.758662059281889, + 10.031894386342293, + 13.499837171470721, + 13.20450638657912, + 10.492689033000001, + 11.652733572242987, + 11.652733572242987, + 11.299922505743869, + 8.503505918090102, + 8.084675497847156, + 13.0858596499251, + 10.921746469000041, + 9.35308787961478, + 8.554864781743868, + 8.084675497847156, + 9.837915503000001, + 10.178431811847156, + 8.083367726177979, + 8.889502876999998, + 9.525712407847198, + 9.132264692, + 8.320188515936232, + 11.418923576139772, + 12.27626493027424, + 11.906701403613567, + 13.436411089440648, + 11.312038763532875, + 11.224037838142287, + 12.06410768700004, + 9.013283454847155, + 10.194088952443071, + 9.718603871, + 10.39192563274387, + 10.39192563274387, + 12.527617748692238, + 9.673046955847157, + 10.376856411766463, + 10.888510735342251, + 8.083367726177979, + 8.083367726177979, + 8.595149195644677, + 13.377695286791543, + 10.750247270693071, + 9.793197093896714, + 9.804494621441233, + 8.083367726177979, + 10.621305202896714, + 8.083367726177979, + 9.960373797852533, + 10.494869661982165, + 9.909087701296448, + 9.93280171679752, + 12.38866235304296, + 8.083367726177979, + 11.933133183678253, + 11.757444984644676, + 8.083367726177979, + 12.535353642729312, + 9.901066365246812, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 9.642259966405154, + 10.765810497262185, + 11.783361439, + 13.045703112489273, + 8.083367726177979, + 8.083367726177979, + 8.677056705261558, + 12.782939502, + 12.326526644449292, + 10.919300673999999, + 11.484220961792172, + 7.990153765999999, + 10.199705326, + 11.047891043449333, + 7.990153765999999, + 11.257003906, + 7.990153765999999, + 11.43918414379752, + 9.544513563875126, + 7.990153765999999, + 12.623570002853606, + 12.320897981000039, + 11.55195665085293, + 10.94252811434225, + 10.669440671797563, + 9.425996072, + 9.749331180817062, + 9.512778527679753, + 9.518406343797519, + 7.824605715847156, + 8.522977836896754, + 10.664282555342252, + 10.802665834847199, + 12.438493698526832, + 9.389620287847197, + 10.90815186706286, + 9.773151867062861, + 11.172117101904451, + 8.219160834, + 7.824605715847156, + 9.704989211832597, + 11.447544912939497, + 11.447544912939497, + 7.824605715847156, + 11.461699027613568, + 10.879847251694235, + 9.815250782238966, + 11.223618701342252, + 10.958645909242946, + 10.14921212144929, + 7.824605715847156, + 8.464502658999999, + 12.067669677000001, + 10.415650197281888, + 7.824605715847156, + 7.824605715847156, + 8.770800579090102, + 10.758202815090144, + 11.43273627932232, + 11.785905739791543, + 11.476849087342252, + 8.328682586, + 8.221020944, + 7.824605715847156, + 7.824605715847156, + 9.900933976368599, + 11.930670563, + 10.608469585, + 11.31543011300004, + 8.139126806, + 7.824605715847156, + 9.21941941183264, + 10.234592861, + 7.824605715847156, + 11.97818418790392, + 12.112630802919305, + 9.618692131087258, + 8.90280839459906, + 10.299762163346006, + 7.824605715847156, + 10.833896756, + 8.825166722, + 12.366007581162252, + 8.232030826719937, + 7.824605715847156, + 10.652781109342293, + 11.578775902342251, + 10.77601944761361, + 11.475254834, + 7.824605715847156, + 11.041952314679753, + 7.824605715847156, + 10.843887333919305, + 8.760901547090102, + 13.039974842743868, + 12.444227344, + 10.594646890887624, + 11.094919394174848, + 11.094919394174848, + 7.824605715847156, + 11.332882547896714, + 7.824605715847156, + 10.346554477692239, + 7.824605715847156, + 12.82228965, + 12.654189452, + 10.77583009461361, + 10.77583009461361, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 15.15998934746482, + 10.323045915679753, + 9.711080406519672, + 7.824605715847156, + 10.172843734, + 10.497736393875126, + 8.125292064644677, + 9.227446355766505, + 7.824605715847156, + 12.433426159902355, + 11.205922652477275, + 9.801834532449334, + 7.824605715847156, + 7.824605715847156, + 10.877960985832598, + 10.877960985832598, + 11.268341210797562, + 9.673912043238966, + 10.837602594793688, + 10.328081499, + 10.26862326179752, + 7.824605715847156, + 11.381428642, + 8.250842714342252 + ], + "min": [ + 13.820271169793038, + 13.89437687019911, + 13.579695501563089, + 12.78299149726557, + 12.75282024750524, + 12.340414043784927, + 11.952478164315156, + 11.952478164315156, + 11.581748564791543, + 11.581748564791543, + 11.581748564791543, + 10.919459375791543, + 10.919459375791543, + 10.136886997872887, + 10.136886997872887, + 8.928814496221406, + 8.928339875788415, + 8.928339875788415, + 8.817093132031363, + 8.817093132031363, + 8.817093132031363, + 8.817093132031363, + 8.817093132031363, + 8.817093132031363, + 8.800366346788417, + 8.596842299788417, + 8.712518711788416, + 8.563979395788417, + 8.563979395788417, + 8.563979395788417, + 8.563979395788417, + 8.563979395788417, + 8.502876877788417, + 8.502876877788417, + 8.502876877788417, + 8.45027072478846, + 8.45027072478846, + 8.45027072478846, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.347532775788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.38845097995582, + 8.38845097995582, + 8.38845097995582, + 8.352515651174851, + 8.27019967117485, + 8.27019967117485, + 8.27019967117485, + 8.259516915342253, + 8.259516915342253, + 8.259516915342253, + 8.259516915342253, + 8.259516915342253, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.143792579847156, + 8.143792579847156, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.060163026177978, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156 + ] + }, + "fixed_seed": 42, + "eautocharge_hours_float": [ + 0.875, + 0.75, + 0.5, + 1.0, + 0.625, + 0.625, + 0.875, + 0.625, + 0.875, + 0.875, + 0.0, + 1.0, + 0.5, + 0.625, + 1.0, + 0.75, + 0.5, + 0.625, + 0.375, + 0.5, + 0.625, + 0.625, + 0.5, + 0.625, + 0.625, + 0.0, + 0.625, + 0.5, + 0.5, + 1.0, + 0.375, + 0.625, + 0.875, + 0.5, + 0.5, + 0.75, + 0.75, + 0.75, + 0.0, + 0.875, + 0.75, + 1.0, + 1.0, + 0.75, + 0.625, + 0.875, + 0.5, + 0.875 + ], + "eauto_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.5, + 0.625, + 1.0, + 0.75, + 0.5, + 0.625, + 0.375, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59373.0, + "initial_soc_percentage": 5 + } +} \ No newline at end of file diff --git a/tests/testdata/test_example_report.pdf b/tests/testdata/genetic/test_example_report.pdf similarity index 100% rename from tests/testdata/test_example_report.pdf rename to tests/testdata/genetic/test_example_report.pdf diff --git a/tests/testdata/genetic0optimize_input_1.json b/tests/testdata/genetic0/optimize_input_1.json similarity index 100% rename from tests/testdata/genetic0optimize_input_1.json rename to tests/testdata/genetic0/optimize_input_1.json diff --git a/tests/testdata/genetic0optimize_input_2.json b/tests/testdata/genetic0/optimize_input_2.json similarity index 100% rename from tests/testdata/genetic0optimize_input_2.json rename to tests/testdata/genetic0/optimize_input_2.json diff --git a/tests/testdata/genetic0/optimize_result_1.json b/tests/testdata/genetic0/optimize_result_1.json new file mode 100644 index 00000000..17fd89d4 --- /dev/null +++ b/tests/testdata/genetic0/optimize_result_1.json @@ -0,0 +1,2218 @@ +{ + "parameters": { + "ems": { + "pv_forecast_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "electricity_price_per_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "feed_in_tariff_per_wh": [ + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05 + ], + "price_per_wh_battery": 0.0001, + "total_load": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "pv_prognose_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "strompreis_euro_pro_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "einspeiseverguetung_euro_pro_wh": [ + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05 + ], + "preis_euro_pro_wh_akku": 0.0001, + "gesamtlast": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ] + }, + "pv_battery": { + "device_id": "battery1", + "capacity_wh": 26400, + "max_charge_power_w": 5000.0, + "initial_soc_percentage": 80, + "min_soc_percentage": 15 + }, + "inverter": { + "device_id": "inverter1", + "max_power_wh": 10000.0, + "battery_id": "battery1" + }, + "ev": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "discharging_efficiency": 1.0, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 54, + "min_soc_percentage": 0 + }, + "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.0, + 18.6, + 19.2, + 19.1, + 18.7, + 18.5, + 17.7, + 16.2, + 14.6, + 13.6, + 13.0, + 12.6, + 12.2, + 11.7, + 11.6, + 11.3, + 11.0, + 10.7, + 10.2, + 11.4, + 14.4, + 16.4, + 18.3, + 19.5, + 20.7, + 21.9, + 22.7, + 23.1, + 23.1, + 22.8, + 21.8, + 20.2, + 19.1, + 18.0, + 17.4 + ], + "start_solution": [ + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "pv_akku": { + "device_id": "battery1", + "capacity_wh": 26400, + "max_charge_power_w": 5000.0, + "initial_soc_percentage": 80, + "min_soc_percentage": 15 + }, + "eauto": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "discharging_efficiency": 1.0, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 54, + "min_soc_percentage": 0 + } + }, + "ac_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "dc_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "discharge_allowed": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 0 + ], + "ev_charge_hours_float": null, + "result": { + "load_wh_per_hour": [ + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "ev_soc_per_hour": [ + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0 + ], + "revenue_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19391086083906173, + 0.18681973047764083, + 0.12880892587597292, + 0.02404700392596282, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.12500582038027028, + 0.14608958812480227, + 0.09047346757070289, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_losses": 2878.660271824896, + "total_balance": 1.053854835771092, + "total_revenue": 0.9055602150669013, + "total_costs": 1.9594150508379933, + "home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "costs_per_hour": [ + 0.0, + 0.004569992000000018, + 0.0, + 0.0, + 0.0, + 4.482711108977355e-14, + 0.0, + 0.016809914659344942, + 0.0, + 0.05480703000000003, + 0.0, + 0.291163892, + 0.0, + 0.0, + 0.0, + 0.0, + 0.22802125600000003, + 0.0, + 0.182970359, + 0.162995926, + 0.0, + 0.26411047, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.010682755832597498, + 0.0, + 0.0003442778967139274, + 0.0, + 0.028137079449292023, + 0.0, + 0.08231598, + 0.174597189, + 0.293043269, + 0.0, + 0.16484566 + ], + "grid_consumption_wh_per_hour": [ + 0.0, + 20.660000000000082, + 0.0, + 0.0, + 0.0, + 2.236881790906864e-10, + 0.0, + 74.05248748610107, + 0.0, + 171.54000000000008, + 0.0, + 980.68, + 0.0, + 0.0, + 0.0, + 0.0, + 694.34, + 0.0, + 556.31, + 488.89, + 0.0, + 799.85, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 56.853410498124, + 0.0, + 1.7179535764168035, + 0.0, + 123.95189184710142, + 0.0, + 257.64, + 566.69, + 987.01, + 0.0, + 592.97 + ], + "grid_feed_in_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2770.155154843739, + 2668.8532925377262, + 1840.127512513899, + 343.5286275137546, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1785.797434003861, + 2086.994116068604, + 1292.4781081528986, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "losses_per_hour": [ + 16.744090909090914, + 0.0, + 29.157272727272726, + 3.7179773678125034, + 582.6180000000041, + 188.65138141872444, + 10.782457846871594, + 0.0, + 59.810111380126784, + 0.0, + 99.72409090909093, + 0.0, + 124.41545454545451, + 96.08318181818186, + 70.41409090909087, + 118.37045454545455, + 0.0, + 83.01681818181817, + 0.0, + 0.0, + 69.12409090909085, + 0.0, + 109.96227272727276, + 47.952272727272714, + 16.01263877609336, + 55.946263193163475, + 161.62968357967037, + 14.209990740225123, + 538.2984000000038, + 227.42215349036655, + 10.13011689299087, + 0.0, + 44.377460775206174, + 0.0, + 0.0, + 0.0, + 100.08954545454549, + 0.0 + ], + "battery_soc_per_hour": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 78.55109331955923, + 78.57585051614844, + 94.75968384947988, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.85214359504131, + 96.85214359504131, + 92.92488808539943, + 89.89195936639118, + 87.6692923553719, + 83.93285123966942, + 83.93285123966942, + 81.31237086776858, + 81.31237086776858, + 81.31237086776858, + 79.13042355371898, + 79.13042355371898, + 75.65939221763084, + 74.14574724517905, + 74.30696088041155, + 74.82732877552169, + 78.33526266026307, + 78.72998462526934, + 93.68271795860093, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.84060778236915 + ], + "electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "Last_Wh_pro_Stunde": [ + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "EAuto_SoC_pro_Stunde": [ + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0 + ], + "Einnahmen_Euro_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19391086083906173, + 0.18681973047764083, + 0.12880892587597292, + 0.02404700392596282, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.12500582038027028, + 0.14608958812480227, + 0.09047346757070289, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Gesamt_Verluste": 2878.660271824896, + "Gesamtbilanz_Euro": 1.053854835771092, + "Gesamteinnahmen_Euro": 0.9055602150669013, + "Gesamtkosten_Euro": 1.9594150508379933, + "Home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Kosten_Euro_pro_Stunde": [ + 0.0, + 0.004569992000000018, + 0.0, + 0.0, + 0.0, + 4.482711108977355e-14, + 0.0, + 0.016809914659344942, + 0.0, + 0.05480703000000003, + 0.0, + 0.291163892, + 0.0, + 0.0, + 0.0, + 0.0, + 0.22802125600000003, + 0.0, + 0.182970359, + 0.162995926, + 0.0, + 0.26411047, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.010682755832597498, + 0.0, + 0.0003442778967139274, + 0.0, + 0.028137079449292023, + 0.0, + 0.08231598, + 0.174597189, + 0.293043269, + 0.0, + 0.16484566 + ], + "Netzbezug_Wh_pro_Stunde": [ + 0.0, + 20.660000000000082, + 0.0, + 0.0, + 0.0, + 2.236881790906864e-10, + 0.0, + 74.05248748610107, + 0.0, + 171.54000000000008, + 0.0, + 980.68, + 0.0, + 0.0, + 0.0, + 0.0, + 694.34, + 0.0, + 556.31, + 488.89, + 0.0, + 799.85, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 56.853410498124, + 0.0, + 1.7179535764168035, + 0.0, + 123.95189184710142, + 0.0, + 257.64, + 566.69, + 987.01, + 0.0, + 592.97 + ], + "Netzeinspeisung_Wh_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2770.155154843739, + 2668.8532925377262, + 1840.127512513899, + 343.5286275137546, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1785.797434003861, + 2086.994116068604, + 1292.4781081528986, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Verluste_Pro_Stunde": [ + 16.744090909090914, + 0.0, + 29.157272727272726, + 3.7179773678125034, + 582.6180000000041, + 188.65138141872444, + 10.782457846871594, + 0.0, + 59.810111380126784, + 0.0, + 99.72409090909093, + 0.0, + 124.41545454545451, + 96.08318181818186, + 70.41409090909087, + 118.37045454545455, + 0.0, + 83.01681818181817, + 0.0, + 0.0, + 69.12409090909085, + 0.0, + 109.96227272727276, + 47.952272727272714, + 16.01263877609336, + 55.946263193163475, + 161.62968357967037, + 14.209990740225123, + 538.2984000000038, + 227.42215349036655, + 10.13011689299087, + 0.0, + 44.377460775206174, + 0.0, + 0.0, + 0.0, + 100.08954545454549, + 0.0 + ], + "akku_soc_pro_stunde": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 78.55109331955923, + 78.57585051614844, + 94.75968384947988, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.85214359504131, + 96.85214359504131, + 92.92488808539943, + 89.89195936639118, + 87.6692923553719, + 83.93285123966942, + 83.93285123966942, + 81.31237086776858, + 81.31237086776858, + 81.31237086776858, + 79.13042355371898, + 79.13042355371898, + 75.65939221763084, + 74.14574724517905, + 74.30696088041155, + 74.82732877552169, + 78.33526266026307, + 78.72998462526934, + 93.68271795860093, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.84060778236915 + ], + "Electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ] + }, + "ev_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 32400.000000000004, + "initial_soc_percentage": 54 + }, + "start_hour": 10, + "start_solution": [ + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 2.0, + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 2.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "washingstart": null, + "extra_data": { + "balance": [ + 1.5629315062541924, + 2.6256495075632187, + 2.165740335702944, + 1.5629315062541924, + 2.240440405626093, + 1.5629315062541924, + 1.9877015035829169, + 2.22262363104199, + 1.5778657214023133, + 2.208397307667416, + 1.5629315062541924, + 2.4293303489630818, + 2.412448593343341, + 1.5629315062541924, + 2.218927499018621, + 1.5629315062541924, + 2.2587939554734637, + 1.053854835771092, + 1.6043069913102126, + 2.273703414535969, + 2.212860049246504, + 1.5629315062541924, + 2.295724023298086, + 1.5629315062541924, + 2.3276080643501436, + 1.5629315062541924, + 2.638380034677266, + 1.5629315062541924, + 1.6639995536998098, + 1.405068570959087, + 1.5629315062541924, + 1.8051355297292633, + 1.405068570959087, + 2.287814905181311, + 1.5629315062541924, + 2.3795803010587147, + 2.273703414535969, + 2.2216775103872193, + 2.2216775103872193, + 1.9097791698801405, + 1.981541802518228, + 1.5778657214023133, + 1.5629315062541924, + 2.6086571004823034, + 1.9163540908754297, + 1.5629315062541924, + 2.596003055647592, + 2.5548807933161815, + 1.8727661382233525, + 1.8385927648722558, + 2.4293303489630818, + 2.164461541806346, + 2.2841554715592074, + 1.405068570959087, + 1.8777991489912709, + 1.3689438450633387, + 1.405068570959087, + 1.5629315062541924, + 2.612646914374828, + 1.5629315062541924, + 2.2216775103872193, + 1.5629315062541924, + 1.968731246274983, + 1.5629315062541924, + 2.6141833032666306, + 1.5629315062541924, + 2.4185117224953836, + 2.273703414535969, + 2.2587939554734637, + 2.5176226765078527, + 1.5629315062541924, + 1.5629315062541924, + 2.2216775103872193, + 2.7640811111485957, + 1.5629315062541924, + 2.253022440711902, + 2.4293303489630818, + 2.204743141961746, + 2.056831850387219, + 1.6165388024504925, + 2.1475638411090703, + 2.0690811111485954, + 2.2216775103872193, + 1.8385927648722558, + 2.6378948527424724, + 2.519823130326035, + 2.295724023298086, + 2.7524385083491794, + 1.5629315062541924, + 2.212860049246504, + 2.4681479270410693, + 2.2216775103872193, + 2.253022440711902, + 2.4293303489630818, + 1.5629315062541924, + 1.9097791698801405, + 1.5629315062541924, + 2.312165891637598, + 2.212860049246504, + 1.5629315062541924 + ], + "losses": [ + 2020.0647478143976, + 1783.7767860253437, + 1913.3967498491086, + 2020.0647478143976, + 2534.700181698695, + 2020.0647478143976, + 2042.788780154585, + 2323.4039189058713, + 2111.0454480011385, + 1867.7293965747283, + 2020.0647478143976, + 1813.1396479231805, + 1356.6271470299466, + 2020.0647478143976, + 2467.2825251989507, + 2020.0647478143976, + 1169.72103850289, + 2878.660271824896, + 1924.8294619726955, + 2511.3137436444576, + 1595.7341100085885, + 2020.0647478143976, + 2109.799113667768, + 2020.0647478143976, + 2065.4216528925617, + 2020.0647478143976, + 1259.792129725977, + 2020.0647478143976, + 1845.248795108642, + 2240.8216024232343, + 2020.0647478143976, + 2262.8228060083893, + 2240.8216024232343, + 2347.690047244586, + 2020.0647478143976, + 1662.2368625088986, + 2511.3137436444576, + 2326.230637196544, + 2326.230637196544, + 1735.5385731331469, + 2291.306201407247, + 2111.0454480011385, + 2020.0647478143976, + 1677.0149722648923, + 2554.276136009273, + 2020.0647478143976, + 1693.792286314479, + 1681.8816277412084, + 2280.299877198412, + 2202.8469441371435, + 1813.1396479231805, + 2020.7537203686422, + 1951.0702258212687, + 2240.8216024232343, + 2351.958534876586, + 2164.1171473088725, + 2240.8216024232343, + 2020.0647478143976, + 1183.3843076326698, + 2020.0647478143976, + 2326.230637196544, + 2020.0647478143976, + 1767.105028996182, + 2020.0647478143976, + 1287.2123510784259, + 2020.0647478143976, + 2311.9185803023647, + 2511.3137436444576, + 1169.72103850289, + 1203.5357654350614, + 2020.0647478143976, + 2020.0647478143976, + 2326.230637196544, + 1882.5060289276414, + 2020.0647478143976, + 2055.2012844063765, + 1813.1396479231805, + 1954.3983714757119, + 2407.09018265109, + 2285.5086712319326, + 2234.8419209342183, + 1582.5060289276414, + 2326.230637196544, + 2202.8469441371435, + 1142.3492328190187, + 1115.4373563441525, + 2109.799113667768, + 1981.6810169345538, + 2020.0647478143976, + 1595.7341100085885, + 1592.376446450568, + 2326.230637196544, + 2055.2012844063765, + 1813.1396479231805, + 2020.0647478143976, + 1735.5385731331469, + 2020.0647478143976, + 1592.0138728022027, + 1595.7341100085885, + 2020.0647478143976 + ], + "constraints": [ + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0 + ] + }, + "fitness_history": { + "gen": [ + 0, + 1, + 2, + 3 + ], + "avg": [ + 1.3457767274332526, + 0.9566909031398302, + 0.502805054118316, + 0.15642334466274066 + ], + "max": [ + 2.1600157439680157, + 1.6360627044583638, + 1.5607919093370366, + 0.8123975083491792 + ], + "min": [ + -0.5805761986329421, + -0.4395834290409131, + -0.4395834290409131, + -0.8474661642289083 + ] + }, + "fixed_seed": 42, + "eautocharge_hours_float": null, + "eauto_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 32400.000000000004, + "initial_soc_percentage": 54 + } +} \ No newline at end of file diff --git a/tests/testdata/genetic0/optimize_result_1_be.json b/tests/testdata/genetic0/optimize_result_1_be.json new file mode 100644 index 00000000..cade9cfd --- /dev/null +++ b/tests/testdata/genetic0/optimize_result_1_be.json @@ -0,0 +1,2218 @@ +{ + "parameters": { + "ems": { + "pv_forecast_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "electricity_price_per_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "feed_in_tariff_per_wh": [ + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05 + ], + "price_per_wh_battery": 0.0001, + "total_load": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "pv_prognose_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "strompreis_euro_pro_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "einspeiseverguetung_euro_pro_wh": [ + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05, + 7e-05 + ], + "preis_euro_pro_wh_akku": 0.0001, + "gesamtlast": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ] + }, + "pv_battery": { + "device_id": "battery1", + "capacity_wh": 26400, + "max_charge_power_w": 5000.0, + "initial_soc_percentage": 80, + "min_soc_percentage": 15 + }, + "inverter": { + "device_id": "inverter1", + "max_power_wh": 10000.0, + "battery_id": "battery1" + }, + "ev": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "discharging_efficiency": 1.0, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 54, + "min_soc_percentage": 0 + }, + "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.0, + 18.6, + 19.2, + 19.1, + 18.7, + 18.5, + 17.7, + 16.2, + 14.6, + 13.6, + 13.0, + 12.6, + 12.2, + 11.7, + 11.6, + 11.3, + 11.0, + 10.7, + 10.2, + 11.4, + 14.4, + 16.4, + 18.3, + 19.5, + 20.7, + 21.9, + 22.7, + 23.1, + 23.1, + 22.8, + 21.8, + 20.2, + 19.1, + 18.0, + 17.4 + ], + "start_solution": [ + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "pv_akku": { + "device_id": "battery1", + "capacity_wh": 26400, + "max_charge_power_w": 5000.0, + "initial_soc_percentage": 80, + "min_soc_percentage": 15 + }, + "eauto": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "discharging_efficiency": 1.0, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 54, + "min_soc_percentage": 0 + } + }, + "ac_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "dc_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "discharge_allowed": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0, + 1, + 0 + ], + "ev_charge_hours_float": null, + "result": { + "load_wh_per_hour": [ + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "ev_soc_per_hour": [ + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0 + ], + "revenue_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19478794541504615, + 0.18681973047764083, + 0.12880892587597292, + 0.02404700392596282, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.17104472158211628, + 0.14608958812480227, + 0.09047346757070289, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_losses": 2804.7326610375394, + "total_balance": 0.9004618481798127, + "total_revenue": 0.9524762008447317, + "total_costs": 1.8529380490245444, + "home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "costs_per_hour": [ + 0.0, + 0.004569992000000018, + 0.0, + 0.0018232052307313393, + 0.0, + 4.482711108977355e-14, + 0.0, + 0.016809914659344942, + 0.0, + 0.05480703000000003, + 0.0, + 0.291163892, + 0.0, + 0.19588158, + 0.174739608, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.16677339, + 0.0, + 0.24530383800000005, + 0.08545095, + 0.007989913613567745, + 0.028255713342252034, + 0.0, + 0.010682755832597498, + 0.0, + 0.0003442778967139274, + 0.0, + 0.028137079449292023, + 0.0, + 0.08231598, + 0.0, + 0.293043269, + 0.0, + 0.16484566 + ], + "grid_consumption_wh_per_hour": [ + 0.0, + 20.660000000000082, + 0.0, + 9.703061366318996, + 0.0, + 2.236881790906864e-10, + 0.0, + 74.05248748610107, + 0.0, + 171.54000000000008, + 0.0, + 980.68, + 0.0, + 704.61, + 516.37, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 506.91, + 0.0, + 806.3900000000001, + 351.65, + 35.04348076126204, + 127.73830624887898, + 0.0, + 56.853410498124, + 0.0, + 1.7179535764168035, + 0.0, + 123.95189184710142, + 0.0, + 257.64, + 0.0, + 987.01, + 0.0, + 592.97 + ], + "grid_feed_in_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2782.6849345006594, + 2668.8532925377262, + 1840.127512513899, + 343.5286275137546, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2443.4960226016615, + 2086.994116068604, + 1292.4781081528986, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "losses_per_hour": [ + 16.744090909090914, + 0.0, + 29.157272727272726, + 2.3948326360417305, + 582.6180000000041, + 187.1478078598941, + 10.782457846871594, + 0.0, + 59.810111380126784, + 0.0, + 99.72409090909093, + 0.0, + 124.41545454545451, + 0.0, + 0.0, + 118.37045454545455, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 0.0, + 0.0, + 11.233982308648535, + 38.52740325013451, + 161.62968357967037, + 14.209990740225123, + 538.2984000000038, + 148.49832285863067, + 10.13011689299087, + 0.0, + 44.377460775206174, + 0.0, + 77.27590909090907, + 0.0, + 100.08954545454549, + 0.0 + ], + "battery_soc_per_hour": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 78.55109331955923, + 78.61761644833817, + 94.80144978166962, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.85214359504131, + 96.85214359504131, + 92.92488808539943, + 92.92488808539943, + 92.92488808539943, + 89.18844696969695, + 86.19972451790632, + 83.57924414600548, + 81.18465909090907, + 79.08027720385672, + 79.08027720385672, + 75.63739669421484, + 75.63739669421484, + 75.63739669421484, + 75.94945175834397, + 77.01965740418103, + 80.52759128892241, + 80.92231325392866, + 95.87504658726026, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 97.56073519283747, + 97.56073519283747, + 94.40134297520663 + ], + "electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "Last_Wh_pro_Stunde": [ + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "EAuto_SoC_pro_Stunde": [ + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0, + 54.0 + ], + "Einnahmen_Euro_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19478794541504615, + 0.18681973047764083, + 0.12880892587597292, + 0.02404700392596282, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.17104472158211628, + 0.14608958812480227, + 0.09047346757070289, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Gesamt_Verluste": 2804.7326610375394, + "Gesamtbilanz_Euro": 0.9004618481798127, + "Gesamteinnahmen_Euro": 0.9524762008447317, + "Gesamtkosten_Euro": 1.8529380490245444, + "Home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Kosten_Euro_pro_Stunde": [ + 0.0, + 0.004569992000000018, + 0.0, + 0.0018232052307313393, + 0.0, + 4.482711108977355e-14, + 0.0, + 0.016809914659344942, + 0.0, + 0.05480703000000003, + 0.0, + 0.291163892, + 0.0, + 0.19588158, + 0.174739608, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.16677339, + 0.0, + 0.24530383800000005, + 0.08545095, + 0.007989913613567745, + 0.028255713342252034, + 0.0, + 0.010682755832597498, + 0.0, + 0.0003442778967139274, + 0.0, + 0.028137079449292023, + 0.0, + 0.08231598, + 0.0, + 0.293043269, + 0.0, + 0.16484566 + ], + "Netzbezug_Wh_pro_Stunde": [ + 0.0, + 20.660000000000082, + 0.0, + 9.703061366318996, + 0.0, + 2.236881790906864e-10, + 0.0, + 74.05248748610107, + 0.0, + 171.54000000000008, + 0.0, + 980.68, + 0.0, + 704.61, + 516.37, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 506.91, + 0.0, + 806.3900000000001, + 351.65, + 35.04348076126204, + 127.73830624887898, + 0.0, + 56.853410498124, + 0.0, + 1.7179535764168035, + 0.0, + 123.95189184710142, + 0.0, + 257.64, + 0.0, + 987.01, + 0.0, + 592.97 + ], + "Netzeinspeisung_Wh_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2782.6849345006594, + 2668.8532925377262, + 1840.127512513899, + 343.5286275137546, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2443.4960226016615, + 2086.994116068604, + 1292.4781081528986, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Verluste_Pro_Stunde": [ + 16.744090909090914, + 0.0, + 29.157272727272726, + 2.3948326360417305, + 582.6180000000041, + 187.1478078598941, + 10.782457846871594, + 0.0, + 59.810111380126784, + 0.0, + 99.72409090909093, + 0.0, + 124.41545454545451, + 0.0, + 0.0, + 118.37045454545455, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 0.0, + 0.0, + 11.233982308648535, + 38.52740325013451, + 161.62968357967037, + 14.209990740225123, + 538.2984000000038, + 148.49832285863067, + 10.13011689299087, + 0.0, + 44.377460775206174, + 0.0, + 77.27590909090907, + 0.0, + 100.08954545454549, + 0.0 + ], + "akku_soc_pro_stunde": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 78.55109331955923, + 78.61761644833817, + 94.80144978166962, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.85214359504131, + 96.85214359504131, + 92.92488808539943, + 92.92488808539943, + 92.92488808539943, + 89.18844696969695, + 86.19972451790632, + 83.57924414600548, + 81.18465909090907, + 79.08027720385672, + 79.08027720385672, + 75.63739669421484, + 75.63739669421484, + 75.63739669421484, + 75.94945175834397, + 77.01965740418103, + 80.52759128892241, + 80.92231325392866, + 95.87504658726026, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 97.56073519283747, + 97.56073519283747, + 94.40134297520663 + ], + "Electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ] + }, + "ev_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 32400.000000000004, + "initial_soc_percentage": 54 + }, + "start_hour": 10, + "start_solution": [ + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "washingstart": null, + "extra_data": { + "balance": [ + 1.5629315062541924, + 2.158365581179813, + 2.165740335702944, + 1.4445126332046057, + 2.602530525650703, + 1.5629315062541924, + 0.9004618481798127, + 2.22262363104199, + 2.0453091016610436, + 1.4445126332046057, + 1.5629315062541924, + 2.4293303489630818, + 3.1320077296752524, + 1.5629315062541924, + 3.2988292733845688, + 1.5629315062541924, + 2.2587939554734637, + 1.5629315062541924, + 2.3179853855994446, + 3.586409468799831, + 2.060941413080932, + 1.5629315062541924, + 3.095869071948983, + 1.5629315062541924, + 3.6165384365879865, + 1.5629315062541924, + 3.6894546560699064, + 1.5629315062541924, + 1.4744169145760049, + 1.405068570959087, + 1.5629315062541924, + 1.778868572372095, + 1.405068570959087, + 2.3041345656193553, + 1.5629315062541924, + 2.3795803010587147, + 2.789764513631562, + 3.455211569265561, + 3.6165384365879865, + 1.9097791698801405, + 1.981541802518228, + 2.0453091016610436, + 1.5629315062541924, + 3.0448979100124167, + 1.9163540908754297, + 1.5629315062541924, + 3.6165384365879865, + 3.440531929235723, + 2.826784740487001, + 1.8534925065868375, + 2.4293303489630818, + 2.77797403459721, + 2.2841554715592074, + 1.405068570959087, + 3.4781121347699133, + 1.3689438450633387, + 1.405068570959087, + 1.5629315062541924, + 2.74043868882997, + 1.5629315062541924, + 2.2216775103872193, + 1.5629315062541924, + 1.6606062986770178, + 1.5629315062541924, + 3.4419604002655637, + 1.5629315062541924, + 1.252278947504598, + 2.3795803010587147, + 2.2587939554734637, + 3.224443186559079, + 1.5629315062541924, + 1.5629315062541924, + 3.455211569265561, + 3.550214545650703, + 1.5629315062541924, + 3.247129579931017, + 2.4293303489630818, + 2.204743141961746, + 2.781472393502402, + 1.2521326049554384, + 2.4627361520787714, + 2.1032943823386185, + 2.3795803010587147, + 1.8534925065868375, + 3.4419604002655637, + 0.9004618481798127, + 3.400659468799855, + 2.928225531235026, + 1.5629315062541924, + 2.060941413080932, + 2.7315306721942645, + 1.252278947504598, + 2.158365581179813, + 2.4293303489630818, + 1.5629315062541924, + 1.9097791698801405, + 1.5629315062541924, + 3.0182387945383575, + 2.060941413080932, + 1.5629315062541924 + ], + "losses": [ + 2020.0647478143976, + 2192.087661037538, + 1913.3967498491086, + 2146.731689963158, + 2076.6512997516284, + 2020.0647478143976, + 2804.7326610375394, + 2323.4039189058713, + 2276.9165566773136, + 2146.731689963158, + 2020.0647478143976, + 1813.1396479231805, + 2453.3717179361724, + 2020.0647478143976, + 2045.3031396587173, + 2020.0647478143976, + 1169.72103850289, + 2020.0647478143976, + 2022.8914661049919, + 2297.776927567542, + 1740.0980553572056, + 2020.0647478143976, + 1955.6482629011505, + 2020.0647478143976, + 2231.2158149510287, + 2020.0647478143976, + 1995.1164780946408, + 2020.0647478143976, + 1941.8221284758113, + 2240.8216024232343, + 2020.0647478143976, + 1985.9910471579694, + 2240.8216024232343, + 1822.3583659571498, + 2020.0647478143976, + 1662.2368625088986, + 2111.5915046106384, + 2039.4963465901828, + 2231.2158149510287, + 1735.5385731331469, + 2291.306201407247, + 2276.9165566773136, + 2020.0647478143976, + 2061.499565175687, + 2554.276136009273, + 2020.0647478143976, + 2231.2158149510287, + 1295.3855539594088, + 2252.708016174569, + 2132.240439237302, + 1813.1396479231805, + 2265.2409254784407, + 1951.0702258212687, + 2240.8216024232343, + 1393.5532541371747, + 2164.1171473088725, + 2240.8216024232343, + 2020.0647478143976, + 2397.4228088258496, + 2020.0647478143976, + 2326.230637196544, + 2020.0647478143976, + 1866.3559010102965, + 2020.0647478143976, + 1437.151294274816, + 2020.0647478143976, + 2532.8849729896056, + 1662.2368625088986, + 1169.72103850289, + 2009.9127184422987, + 2020.0647478143976, + 2020.0647478143976, + 2039.4963465901828, + 2351.784027024355, + 2020.0647478143976, + 1648.5297351292108, + 1813.1396479231805, + 1954.3983714757119, + 1474.500829230451, + 2512.3504289499906, + 1918.0841451979659, + 2404.4281890618477, + 1662.2368625088986, + 2132.240439237302, + 1437.151294274816, + 2804.7326610375394, + 2297.77692756747, + 1899.2434487406306, + 2020.0647478143976, + 1740.0980553572056, + 2238.9906419601734, + 2532.8849729896056, + 2192.087661037538, + 1813.1396479231805, + 2020.0647478143976, + 1735.5385731331469, + 2020.0647478143976, + 1564.905653765309, + 1740.0980553572056, + 2020.0647478143976 + ], + "constraints": [ + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0, + -54.0 + ] + }, + "fitness_history": { + "gen": [ + 0, + 1, + 2, + 3 + ], + "avg": [ + 13.814324178113074, + 10.672603319001144, + 7.251256543780025, + 3.661137085484413 + ], + "max": [ + 24.747792311219673, + 21.062958907674986, + 14.935148234123542, + 11.659243141417376 + ], + "min": [ + -0.28172049374580777, + -0.4395834290409131, + -0.9441901518201875, + -0.9441901518201875 + ] + }, + "fixed_seed": 42, + "eautocharge_hours_float": null, + "eauto_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 32400.000000000004, + "initial_soc_percentage": 54 + } +} \ No newline at end of file diff --git a/tests/testdata/genetic0/optimize_result_2.json b/tests/testdata/genetic0/optimize_result_2.json new file mode 100644 index 00000000..89aca22b --- /dev/null +++ b/tests/testdata/genetic0/optimize_result_2.json @@ -0,0 +1,2123 @@ +{ + "parameters": { + "ems": { + "pv_forecast_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "electricity_price_per_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "feed_in_tariff_per_wh": 7e-05, + "price_per_wh_battery": 0.0, + "total_load": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "pv_prognose_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "strompreis_euro_pro_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "einspeiseverguetung_euro_pro_wh": 7e-05, + "preis_euro_pro_wh_akku": 0.0, + "gesamtlast": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ] + }, + "pv_battery": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "inverter": { + "device_id": "inverter1", + "max_power_wh": 10000.0, + "battery_id": "battery1" + }, + "ev": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + }, + "dishwasher": { + "device_id": "dishwasher1", + "consumption_wh": 5000, + "duration_h": 2, + "time_windows": { + "windows": [ + { + "start_time": "00:00:00.000000", + "duration": "2 days" + } + ] + } + }, + "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.0, + 18.6, + 19.2, + 19.1, + 18.7, + 18.5, + 17.7, + 16.2, + 14.6, + 13.6, + 13.0, + 12.6, + 12.2, + 11.7, + 11.6, + 11.3, + 11.0, + 10.7, + 10.2, + 11.4, + 14.4, + 16.4, + 18.3, + 19.5, + 20.7, + 21.9, + 22.7, + 23.1, + 23.1, + 22.8, + 21.8, + 20.2, + 19.1, + 18.0, + 17.4 + ], + "start_solution": null, + "pv_akku": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "eauto": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + } + }, + "ac_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0 + ], + "dc_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "discharge_allowed": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1 + ], + "ev_charge_hours_float": [ + 1.0, + 0.5, + 0.375, + 0.375, + 1.0, + 0.75, + 1.0, + 0.75, + 0.5, + 0.875, + 0.0, + 0.875, + 0.75, + 1.0, + 0.625, + 0.375, + 0.75, + 0.875, + 0.625, + 1.0, + 0.75, + 0.375, + 0.75, + 0.5, + 0.5, + 0.625, + 0.875, + 0.5, + 0.0, + 0.0, + 0.75, + 0.875, + 0.0, + 0.0, + 1.0, + 0.375, + 0.375, + 1.0, + 0.0, + 0.875, + 0.5, + 1.0, + 0.625, + 0.75, + 0.875, + 1.0, + 0.5, + 0.5 + ], + "result": { + "load_wh_per_hour": [ + 1053.07, + 10240.91, + 14186.56, + 11620.03, + 11218.67, + 7609.82, + 9082.22, + 10280.78, + 2177.92, + 1178.71, + 1050.98, + 1988.56, + 912.38, + 1704.6100000000001, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "ev_soc_per_hour": [ + 5.0, + 5.0, + 20.294999999999998, + 33.405, + 50.885000000000005, + 61.809999999999995, + 68.365, + 81.475, + 96.77, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518 + ], + "revenue_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.010924611164505103, + 0.25751345302450973, + 0.14608958812480227, + 0.07926913850394514, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_losses": 5766.286846118221, + "total_balance": 12.78299149726557, + "total_revenue": 0.5042016086902498, + "total_costs": 13.28719310595582, + "home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "costs_per_hour": [ + 0.0, + 2.034522392, + 2.737606326, + 1.9651220859999998, + 0.9557324300000001, + 0.4189863, + 1.1236923319999998, + 1.64866014, + 0.07038454500000005, + 0.05480703000000003, + 0.0, + 0.588063892, + 0.0, + 0.47388158, + 0.174739608, + 0.28801899, + 0.0, + 0.0, + 0.0, + 0.0, + 0.16677339, + 0.0, + 0.0, + 0.0, + 0.007989913613567745, + 0.028255713342252034, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.08231598, + 0.174597189, + 0.293043269, + 0.0, + 0.0 + ], + "grid_consumption_wh_per_hour": [ + 0.0, + 9197.66, + 13079.82, + 10458.34, + 5199.85, + 2090.75, + 5112.339999999999, + 7262.820000000001, + 234.85000000000014, + 171.54000000000008, + 0.0, + 1980.6799999999998, + 0.0, + 1704.6100000000001, + 516.37, + 868.05, + 0.0, + 0.0, + 0.0, + 0.0, + 506.91, + 0.0, + 0.0, + 0.0, + 35.04348076126204, + 127.73830624887898, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 257.64, + 566.69, + 987.01, + 0.0, + 0.0 + ], + "grid_feed_in_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 156.06587377864435, + 3678.7636146358536, + 2086.994116068604, + 1132.4162643420734, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "losses_per_hour": [ + 16.744090909090914, + 483.0, + 1014.0, + 552.0, + 465.0, + 207.0, + 414.0, + 483.0, + 55.200000000000045, + 0.0, + 99.72409090909093, + 120.0, + 124.41545454545451, + 120.0, + 0.0, + 0.0, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 109.96227272727276, + 47.952272727272714, + 11.233982308648535, + 38.52740325013451, + 161.62968357967037, + 21.962728535423857, + 519.5704951465664, + 0.5004782113116364, + 10.13011689299087, + 36.109951963721926, + 44.377460775206174, + 0.0, + 0.0, + 0.0, + 100.08954545454549, + 80.85954545454547 + ], + "battery_soc_per_hour": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 96.13812844352617, + 96.13812844352617, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 96.32360537190083, + 99.65693870523415, + 95.72968319559227, + 99.0630165289256, + 99.0630165289256, + 99.0630165289256, + 96.07429407713497, + 93.45381370523414, + 91.05922865013771, + 88.95484676308537, + 88.95484676308537, + 85.51196625344349, + 82.04093491735533, + 80.52728994490354, + 80.83934500903268, + 81.90955065486975, + 85.41748453961112, + 85.56748624593055, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.84060778236915 + ], + "electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "Last_Wh_pro_Stunde": [ + 1053.07, + 10240.91, + 14186.56, + 11620.03, + 11218.67, + 7609.82, + 9082.22, + 10280.78, + 2177.92, + 1178.71, + 1050.98, + 1988.56, + 912.38, + 1704.6100000000001, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "EAuto_SoC_pro_Stunde": [ + 5.0, + 5.0, + 20.294999999999998, + 33.405, + 50.885000000000005, + 61.809999999999995, + 68.365, + 81.475, + 96.77, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518, + 98.518 + ], + "Einnahmen_Euro_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.010924611164505103, + 0.25751345302450973, + 0.14608958812480227, + 0.07926913850394514, + 0.010404817872487553, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Gesamt_Verluste": 5766.286846118221, + "Gesamtbilanz_Euro": 12.78299149726557, + "Gesamteinnahmen_Euro": 0.5042016086902498, + "Gesamtkosten_Euro": 13.28719310595582, + "Home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Kosten_Euro_pro_Stunde": [ + 0.0, + 2.034522392, + 2.737606326, + 1.9651220859999998, + 0.9557324300000001, + 0.4189863, + 1.1236923319999998, + 1.64866014, + 0.07038454500000005, + 0.05480703000000003, + 0.0, + 0.588063892, + 0.0, + 0.47388158, + 0.174739608, + 0.28801899, + 0.0, + 0.0, + 0.0, + 0.0, + 0.16677339, + 0.0, + 0.0, + 0.0, + 0.007989913613567745, + 0.028255713342252034, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.08231598, + 0.174597189, + 0.293043269, + 0.0, + 0.0 + ], + "Netzbezug_Wh_pro_Stunde": [ + 0.0, + 9197.66, + 13079.82, + 10458.34, + 5199.85, + 2090.75, + 5112.339999999999, + 7262.820000000001, + 234.85000000000014, + 171.54000000000008, + 0.0, + 1980.6799999999998, + 0.0, + 1704.6100000000001, + 516.37, + 868.05, + 0.0, + 0.0, + 0.0, + 0.0, + 506.91, + 0.0, + 0.0, + 0.0, + 35.04348076126204, + 127.73830624887898, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 257.64, + 566.69, + 987.01, + 0.0, + 0.0 + ], + "Netzeinspeisung_Wh_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 156.06587377864435, + 3678.7636146358536, + 2086.994116068604, + 1132.4162643420734, + 148.64025532125078, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Verluste_Pro_Stunde": [ + 16.744090909090914, + 483.0, + 1014.0, + 552.0, + 465.0, + 207.0, + 414.0, + 483.0, + 55.200000000000045, + 0.0, + 99.72409090909093, + 120.0, + 124.41545454545451, + 120.0, + 0.0, + 0.0, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 109.96227272727276, + 47.952272727272714, + 11.233982308648535, + 38.52740325013451, + 161.62968357967037, + 21.962728535423857, + 519.5704951465664, + 0.5004782113116364, + 10.13011689299087, + 36.109951963721926, + 44.377460775206174, + 0.0, + 0.0, + 0.0, + 100.08954545454549, + 80.85954545454547 + ], + "akku_soc_pro_stunde": [ + 80.0, + 79.4714617768595, + 79.4714617768595, + 96.13812844352617, + 96.13812844352617, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 99.4714617768595, + 96.32360537190083, + 99.65693870523415, + 95.72968319559227, + 99.0630165289256, + 99.0630165289256, + 99.0630165289256, + 96.07429407713497, + 93.45381370523414, + 91.05922865013771, + 88.95484676308537, + 88.95484676308537, + 85.51196625344349, + 82.04093491735533, + 80.52728994490354, + 80.83934500903268, + 81.90955065486975, + 85.41748453961112, + 85.56748624593055, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 100.0, + 96.84060778236915 + ], + "Electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ] + }, + "ev_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.875, + 0.75, + 1.0, + 0.625, + 0.375, + 0.75, + 0.875, + 0.1, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59110.8, + "initial_soc_percentage": 5 + }, + "start_hour": 10, + "start_solution": [ + 0.0, + 0.0, + 2.0, + 0.0, + 1.0, + 1.0, + 0.0, + 2.0, + 1.0, + 1.0, + 1.0, + 0.0, + 2.0, + 0.0, + 2.0, + 0.0, + 2.0, + 2.0, + 0.0, + 2.0, + 1.0, + 2.0, + 1.0, + 2.0, + 2.0, + 2.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 2.0, + 0.0, + 2.0, + 1.0, + 1.0, + 6.0, + 2.0, + 1.0, + 1.0, + 6.0, + 4.0, + 6.0, + 4.0, + 2.0, + 5.0, + 0.0, + 5.0, + 4.0, + 6.0, + 3.0, + 1.0, + 4.0, + 5.0, + 3.0, + 6.0, + 4.0, + 1.0, + 4.0, + 2.0, + 2.0, + 3.0, + 5.0, + 2.0, + 0.0, + 0.0, + 4.0, + 5.0, + 0.0, + 0.0, + 6.0, + 1.0, + 1.0, + 6.0, + 0.0, + 5.0, + 2.0, + 6.0, + 3.0, + 4.0, + 5.0, + 6.0, + 2.0, + 2.0, + 14.0 + ], + "washingstart": 14, + "extra_data": { + "balance": [ + 14.221506700636702, + 15.299930235610336, + 14.212059628393439, + 14.58029583495025, + 15.218700819591465, + 14.62471850080464, + 14.831865327418862, + 14.026261220300325, + 12.78299149726557, + 15.254055526881615, + 15.024197168967335, + 14.368857308300326, + 15.876830436675256, + 15.2126417415724, + 14.682943106802838, + 13.897410738312315, + 14.371854631136598, + 14.904987324786175, + 13.579695501563089, + 14.949201631285609, + 14.364148546823948, + 14.026261220300325, + 14.563872321673, + 14.79639929946259, + 14.640597806802838, + 14.863840015053594, + 14.831865327418862, + 13.89437687019911, + 14.634695026161982, + 14.586398818526943, + 15.2126417415724, + 14.651436693935828, + 14.832611524977176, + 14.221506700636702, + 14.561252068383046, + 14.364148546823948, + 14.442675133190964, + 14.682943106802838, + 14.682943106802838, + 14.586398818526943, + 14.67188363579013, + 15.16539975383106, + 14.586398818526943, + 14.831865327418862, + 14.832025945708118, + 14.483057329352384, + 14.354852466693526, + 14.282854736298434, + 14.723303889616528, + 14.692805764802324, + 15.16539975383106, + 14.831865327418862, + 14.563872321673, + 14.561252068383046, + 15.289675639468703, + 14.370522152302872, + 14.682943106802838, + 14.904987324786175, + 14.831865327418862, + 14.364148546823948, + 15.289675639468703, + 15.299930235610336, + 14.863840015053594, + 14.863840015053594, + 14.483094914986774, + 14.916199859214707, + 14.221506700636702, + 14.026261220300325, + 14.563872321673, + 13.396510478462671, + 12.78299149726557, + 14.370522152302872, + 14.503025425809126, + 14.346794687236407, + 14.70166818618751, + 15.254055526881615, + 14.371854631136598, + 14.682943106802838, + 14.363628070777011, + 13.972460083694896, + 14.282854736298434, + 14.916199859214707, + 14.832025945708118, + 14.246646590665076, + 14.868509073896895, + 14.026261220300325, + 15.784917496972003, + 14.94040577786713, + 14.371854631136598, + 14.727666169888666, + 14.85376068880464, + 14.831865327418862, + 14.354852466693526, + 14.354852466693526, + 14.364148546823948, + 14.282854736298434, + 14.364148546823948, + 14.94040577786713, + 15.024197168967335, + 14.94040577786713 + ], + "losses": [ + 9866.696042401421, + 7516.761899409781, + 6697.154058947939, + 4568.920397989366, + 8910.757324882812, + 6472.472943050138, + 6885.562555311458, + 6436.220488631327, + 5766.286846118221, + 6041.605332098291, + 8260.05972288515, + 6282.398215904054, + 9177.80638291733, + 5598.710581658907, + 7610.968464030486, + 6346.493167321551, + 8246.724648677668, + 6548.194868796616, + 7249.947693225658, + 7652.859889457773, + 6186.4811335689365, + 6436.220488631327, + 6946.020087822352, + 7272.821841283314, + 7597.168464030485, + 7453.961526470664, + 6885.562555311458, + 8580.159126919834, + 6038.1871113542065, + 7167.8048435292785, + 5598.710581658907, + 5423.084862470211, + 6222.116774404548, + 9866.696042401421, + 7414.368929686839, + 6186.4811335689365, + 6236.749009784242, + 7610.968464030486, + 7610.968464030486, + 7167.8048435292785, + 6399.140623558964, + 6653.566416949209, + 7167.8048435292785, + 6885.562555311458, + 6971.287059443688, + 6202.88802785612, + 6454.121733361086, + 4071.7104177832302, + 5822.76597499057, + 5146.7731654160325, + 6653.566416949209, + 6885.562555311458, + 6946.020087822352, + 7414.368929686839, + 7669.76903437066, + 5257.593312732217, + 7610.968464030486, + 6548.194868796616, + 6885.562555311458, + 6186.4811335689365, + 7669.76903437066, + 7516.761899409781, + 7453.961526470664, + 7453.961526470664, + 6537.83547520668, + 4768.801291163598, + 9866.696042401421, + 6436.220488631327, + 6946.020087822352, + 6485.918693035536, + 5766.286846118221, + 5257.593312732217, + 7723.667195260369, + 7004.197548597559, + 7032.875331945162, + 6041.605332098291, + 8246.724648677668, + 7610.968464030486, + 4185.388626781521, + 10298.0699186246, + 4071.7104177832302, + 4768.801291163598, + 6971.287059443688, + 10370.177313162452, + 5398.059968038848, + 6436.220488631327, + 9014.331353048226, + 10449.049125174153, + 8246.724648677668, + 4730.372317054354, + 6318.650670322865, + 6885.562555311458, + 6454.121733361086, + 6454.121733361086, + 6186.4811335689365, + 4071.7104177832302, + 6186.4811335689365, + 10449.049125174153, + 8260.05972288515, + 10449.049125174153 + ], + "constraints": [ + -18.955, + -18.955, + -18.518, + -18.518, + -18.518, + -19.391999999999996, + -19.391999999999996, + -18.955, + -18.518, + -19.829000000000008, + -19.391999999999996, + -18.955, + -18.518, + -19.829000000000008, + -19.391999999999996, + -19.391999999999996, + -18.955, + -18.955, + -18.518, + -18.518, + -18.518, + -18.955, + -18.955, + -19.391999999999996, + -18.955, + -18.518, + -19.391999999999996, + -18.955, + -18.518, + -18.518, + -19.829000000000008, + -19.391999999999996, + -18.518, + -18.955, + -18.955, + -18.518, + -18.518, + -19.391999999999996, + -19.391999999999996, + -18.518, + -18.518, + -18.955, + -18.518, + -19.391999999999996, + -19.391999999999996, + -18.955, + -18.518, + -18.518, + -18.955, + -18.518, + -18.955, + -19.391999999999996, + -18.955, + -18.955, + -19.829000000000008, + -18.955, + -19.391999999999996, + -18.955, + -19.391999999999996, + -18.518, + -19.829000000000008, + -18.955, + -18.518, + -18.518, + -18.518, + -19.829000000000008, + -18.955, + -18.955, + -18.955, + -18.518, + -18.518, + -18.955, + -19.391999999999996, + -19.391999999999996, + -18.955, + -19.829000000000008, + -18.955, + -19.391999999999996, + -18.955, + -18.955, + -18.518, + -19.829000000000008, + -19.391999999999996, + -19.391999999999996, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.391999999999996, + -19.391999999999996, + -18.518, + -18.518, + -18.518, + -18.518, + -18.518, + -19.829000000000008, + -19.391999999999996, + -19.829000000000008 + ] + }, + "fitness_history": { + "gen": [ + 0, + 1, + 2, + 3 + ], + "avg": [ + 16.685518856626615, + 15.61390152051758, + 14.988718004044808, + 14.601365045870402 + ], + "max": [ + 20.378343583283172, + 17.307985915077595, + 16.378933811317683, + 15.876830436675256 + ], + "min": [ + 13.820271169793038, + 13.89437687019911, + 13.579695501563089, + 12.78299149726557 + ] + }, + "fixed_seed": 42, + "eautocharge_hours_float": [ + 1.0, + 0.5, + 0.375, + 0.375, + 1.0, + 0.75, + 1.0, + 0.75, + 0.5, + 0.875, + 0.0, + 0.875, + 0.75, + 1.0, + 0.625, + 0.375, + 0.75, + 0.875, + 0.625, + 1.0, + 0.75, + 0.375, + 0.75, + 0.5, + 0.5, + 0.625, + 0.875, + 0.5, + 0.0, + 0.0, + 0.75, + 0.875, + 0.0, + 0.0, + 1.0, + 0.375, + 0.375, + 1.0, + 0.0, + 0.875, + 0.5, + 1.0, + 0.625, + 0.75, + 0.875, + 1.0, + 0.5, + 0.5 + ], + "eauto_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.875, + 0.75, + 1.0, + 0.625, + 0.375, + 0.75, + 0.875, + 0.1, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59110.8, + "initial_soc_percentage": 5 + } +} \ No newline at end of file diff --git a/tests/testdata/genetic0/optimize_result_2_be.json b/tests/testdata/genetic0/optimize_result_2_be.json new file mode 100644 index 00000000..ede137fc --- /dev/null +++ b/tests/testdata/genetic0/optimize_result_2_be.json @@ -0,0 +1,2123 @@ +{ + "parameters": { + "ems": { + "pv_forecast_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "electricity_price_per_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "feed_in_tariff_per_wh": 7e-05, + "price_per_wh_battery": 0.0, + "total_load": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "pv_prognose_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "strompreis_euro_pro_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "einspeiseverguetung_euro_pro_wh": 7e-05, + "preis_euro_pro_wh_akku": 0.0, + "gesamtlast": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ] + }, + "pv_battery": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "inverter": { + "device_id": "inverter1", + "max_power_wh": 10000.0, + "battery_id": "battery1" + }, + "ev": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + }, + "dishwasher": { + "device_id": "dishwasher1", + "consumption_wh": 5000, + "duration_h": 2, + "time_windows": { + "windows": [ + { + "start_time": "00:00:00.000000", + "duration": "2 days" + } + ] + } + }, + "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.0, + 18.6, + 19.2, + 19.1, + 18.7, + 18.5, + 17.7, + 16.2, + 14.6, + 13.6, + 13.0, + 12.6, + 12.2, + 11.7, + 11.6, + 11.3, + 11.0, + 10.7, + 10.2, + 11.4, + 14.4, + 16.4, + 18.3, + 19.5, + 20.7, + 21.9, + 22.7, + 23.1, + 23.1, + 22.8, + 21.8, + 20.2, + 19.1, + 18.0, + 17.4 + ], + "start_solution": null, + "pv_akku": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "eauto": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + } + }, + "ac_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "dc_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "discharge_allowed": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 0, + 1, + 1, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 0, + 1, + 1, + 0, + 0, + 1 + ], + "ev_charge_hours_float": [ + 0.625, + 0.375, + 0.5, + 1.0, + 1.0, + 0.75, + 0.5, + 0.375, + 0.375, + 0.625, + 0.75, + 0.875, + 0.625, + 0.625, + 0.625, + 1.0, + 0.375, + 0.625, + 0.875, + 0.0, + 0.5, + 0.0, + 0.0, + 0.5, + 0.875, + 0.625, + 0.75, + 0.5, + 0.375, + 0.375, + 0.75, + 0.625, + 1.0, + 0.375, + 0.625, + 0.625, + 0.0, + 0.5, + 0.875, + 0.375, + 0.5, + 0.875, + 0.5, + 0.875, + 0.75, + 0.625, + 0.75, + 0.0 + ], + "result": { + "load_wh_per_hour": [ + 8919.07, + 10240.91, + 7875.5599999999995, + 7687.03, + 7718.67, + 11664.82, + 5149.22, + 6347.78, + 1129.12, + 8678.71, + 3550.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 5694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 6141.98, + 1056.97, + 992.46, + 6155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "ev_soc_per_hour": [ + 5.0, + 18.11, + 33.405, + 44.330000000000005, + 55.254999999999995, + 66.18, + 83.66, + 90.215, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955 + ], + "revenue_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_losses": 10674.660531928814, + "total_balance": 14.366070195145795, + "total_revenue": 0.0, + "total_costs": 14.366070195145795, + "home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "costs_per_hour": [ + 0.81824412, + 1.061242392, + 0.49579402599999994, + 0.399351386, + 0.13127915000000007, + 1.2316083, + 0.259218932, + 0.7558691399999999, + 0.061530097476751734, + 2.4510570300000003, + 0.0, + 0.0, + 0.26650619799999997, + 0.19588158, + 0.13029273082161758, + 0.28801899, + 1.870021256, + 0.199865757, + 0.0, + 0.0, + 0.16677339, + 0.0, + 1.7663038380000002, + 0.08545095, + 0.007989913613567745, + 1.134255713342252, + 0.025392879919306634, + 0.010682755832597498, + 4.174095896658514e-14, + 0.0003442778967139274, + 0.0, + 0.0, + 0.04565364324294593, + 0.0, + 0.0, + 0.293043269, + 0.214398479, + 0.0 + ], + "grid_consumption_wh_per_hour": [ + 3588.79, + 4797.66, + 2368.8199999999997, + 2125.34, + 714.2500000000003, + 6145.75, + 1179.3400000000001, + 3329.8199999999997, + 205.30563055305882, + 7671.54, + 0.0, + 0.0, + 912.38, + 704.61, + 385.0258003002884, + 868.05, + 5694.34, + 608.79, + 0.0, + 0.0, + 506.91, + 0.0, + 5806.39, + 351.65, + 35.04348076126204, + 5127.738306248879, + 121.32288542430308, + 56.853410498124, + 2.270998855635753e-10, + 1.7179535764168035, + 0.0, + 0.0, + 152.3311419517715, + 0.0, + 0.0, + 987.01, + 733.99, + 0.0 + ], + "grid_feed_in_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "losses_per_hour": [ + 1014.0, + 1083.0, + 945.0, + 945.0, + 479.4, + 552.0, + 207.0, + 276.0, + 73.03732433363291, + 600.0, + 440.6331818181816, + 133.72909090909081, + 0.0, + 0.0, + 17.910572686324315, + 0.0, + 600.0, + 0.0, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 600.0, + 0.0, + 11.233982308648535, + 638.5274032501345, + 145.08565374908358, + 14.209990740225123, + 538.2983999999728, + 441.7178455708299, + 260.56941082122324, + 171.99990368477063, + 41.441862965787436, + 35.132727272727266, + 77.27590909090907, + 0.0, + 0.0, + 80.85954545454547 + ], + "battery_soc_per_hour": [ + 80.0, + 61.06060606060606, + 42.12121212121212, + 23.18181818181818, + 4.242424242424243, + 0.0, + 0.0, + 0.0, + 0.0, + 2.0288145648231377, + 18.695481231489804, + 4.786605542784571, + 0.5653589863107422, + 0.5653589863107422, + 0.5653589863107422, + 0.0, + 0.0, + 16.666666666666664, + 16.666666666666664, + 14.272081611570247, + 12.167699724517906, + 12.167699724517906, + 8.724819214876034, + 25.391485881542703, + 25.391485881542703, + 25.703540945671826, + 43.44041325817556, + 47.47057030676122, + 47.86529227176747, + 62.81802560510005, + 75.08796575984533, + 82.04461281340734, + 85.81933369454761, + 86.97049655470836, + 85.86150895140257, + 83.42224414424004, + 83.42224414424004, + 83.42224414424004 + ], + "electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "Last_Wh_pro_Stunde": [ + 8919.07, + 10240.91, + 7875.5599999999995, + 7687.03, + 7718.67, + 11664.82, + 5149.22, + 6347.78, + 1129.12, + 8678.71, + 3550.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 5694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 6141.98, + 1056.97, + 992.46, + 6155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "EAuto_SoC_pro_Stunde": [ + 5.0, + 18.11, + 33.405, + 44.330000000000005, + 55.254999999999995, + 66.18, + 83.66, + 90.215, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955 + ], + "Einnahmen_Euro_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Gesamt_Verluste": 10674.660531928814, + "Gesamtbilanz_Euro": 14.366070195145795, + "Gesamteinnahmen_Euro": 0.0, + "Gesamtkosten_Euro": 14.366070195145795, + "Home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Kosten_Euro_pro_Stunde": [ + 0.81824412, + 1.061242392, + 0.49579402599999994, + 0.399351386, + 0.13127915000000007, + 1.2316083, + 0.259218932, + 0.7558691399999999, + 0.061530097476751734, + 2.4510570300000003, + 0.0, + 0.0, + 0.26650619799999997, + 0.19588158, + 0.13029273082161758, + 0.28801899, + 1.870021256, + 0.199865757, + 0.0, + 0.0, + 0.16677339, + 0.0, + 1.7663038380000002, + 0.08545095, + 0.007989913613567745, + 1.134255713342252, + 0.025392879919306634, + 0.010682755832597498, + 4.174095896658514e-14, + 0.0003442778967139274, + 0.0, + 0.0, + 0.04565364324294593, + 0.0, + 0.0, + 0.293043269, + 0.214398479, + 0.0 + ], + "Netzbezug_Wh_pro_Stunde": [ + 3588.79, + 4797.66, + 2368.8199999999997, + 2125.34, + 714.2500000000003, + 6145.75, + 1179.3400000000001, + 3329.8199999999997, + 205.30563055305882, + 7671.54, + 0.0, + 0.0, + 912.38, + 704.61, + 385.0258003002884, + 868.05, + 5694.34, + 608.79, + 0.0, + 0.0, + 506.91, + 0.0, + 5806.39, + 351.65, + 35.04348076126204, + 5127.738306248879, + 121.32288542430308, + 56.853410498124, + 2.270998855635753e-10, + 1.7179535764168035, + 0.0, + 0.0, + 152.3311419517715, + 0.0, + 0.0, + 987.01, + 733.99, + 0.0 + ], + "Netzeinspeisung_Wh_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Verluste_Pro_Stunde": [ + 1014.0, + 1083.0, + 945.0, + 945.0, + 479.4, + 552.0, + 207.0, + 276.0, + 73.03732433363291, + 600.0, + 440.6331818181816, + 133.72909090909081, + 0.0, + 0.0, + 17.910572686324315, + 0.0, + 600.0, + 0.0, + 75.86045454545456, + 66.66681818181814, + 0.0, + 109.0704545454546, + 600.0, + 0.0, + 11.233982308648535, + 638.5274032501345, + 145.08565374908358, + 14.209990740225123, + 538.2983999999728, + 441.7178455708299, + 260.56941082122324, + 171.99990368477063, + 41.441862965787436, + 35.132727272727266, + 77.27590909090907, + 0.0, + 0.0, + 80.85954545454547 + ], + "akku_soc_pro_stunde": [ + 80.0, + 61.06060606060606, + 42.12121212121212, + 23.18181818181818, + 4.242424242424243, + 0.0, + 0.0, + 0.0, + 0.0, + 2.0288145648231377, + 18.695481231489804, + 4.786605542784571, + 0.5653589863107422, + 0.5653589863107422, + 0.5653589863107422, + 0.0, + 0.0, + 16.666666666666664, + 16.666666666666664, + 14.272081611570247, + 12.167699724517906, + 12.167699724517906, + 8.724819214876034, + 25.391485881542703, + 25.391485881542703, + 25.703540945671826, + 43.44041325817556, + 47.47057030676122, + 47.86529227176747, + 62.81802560510005, + 75.08796575984533, + 82.04461281340734, + 85.81933369454761, + 86.97049655470836, + 85.86150895140257, + 83.42224414424004, + 83.42224414424004, + 83.42224414424004 + ], + "Electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ] + }, + "ev_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.75, + 0.875, + 0.625, + 0.625, + 0.625, + 1.0, + 0.375, + 0.5, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59373.0, + "initial_soc_percentage": 5 + }, + "start_hour": 10, + "start_solution": [ + 2.0, + 2.0, + 0.0, + 0.0, + 2.0, + 0.0, + 0.0, + 2.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 0.0, + 2.0, + 1.0, + 1.0, + 0.0, + 0.0, + 1.0, + 1.0, + 2.0, + 0.0, + 1.0, + 1.0, + 0.0, + 1.0, + 2.0, + 0.0, + 0.0, + 2.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 0.0, + 0.0, + 1.0, + 3.0, + 1.0, + 2.0, + 6.0, + 6.0, + 4.0, + 2.0, + 1.0, + 1.0, + 3.0, + 4.0, + 5.0, + 3.0, + 3.0, + 3.0, + 6.0, + 1.0, + 3.0, + 5.0, + 0.0, + 2.0, + 0.0, + 0.0, + 2.0, + 5.0, + 3.0, + 4.0, + 2.0, + 1.0, + 1.0, + 4.0, + 3.0, + 6.0, + 1.0, + 3.0, + 3.0, + 0.0, + 2.0, + 5.0, + 1.0, + 2.0, + 5.0, + 2.0, + 5.0, + 4.0, + 3.0, + 4.0, + 0.0, + 19.0 + ], + "washingstart": 19, + "extra_data": { + "balance": [ + 14.597720997785041, + 16.2697167644664, + 15.312269902913977, + 14.85376068880464, + 16.720875566168758, + 16.582410786880896, + 16.60921781689759, + 14.108577200300326, + 14.563872321673, + 16.218166803135617, + 16.705942005168758, + 14.371854631136598, + 15.236710891103375, + 15.5480471649553, + 14.844151433079656, + 14.844151433079656, + 14.371854631136598, + 15.286536561962663, + 15.492537996981472, + 18.320478387677934, + 15.187933957880919, + 15.525973084489191, + 14.563872321673, + 15.621042845697431, + 14.501732154554485, + 15.42746783574979, + 16.621213692326418, + 13.89437687019911, + 14.662083400305969, + 15.5480471649553, + 15.47179762089056, + 15.959975832818147, + 17.02329841917316, + 14.44170891147855, + 15.546075803013684, + 16.720875566168758, + 14.918614925810312, + 15.286536561962663, + 16.548270329023996, + 15.5480471649553, + 16.621213692326418, + 16.2697167644664, + 15.101366547867132, + 16.405841140620637, + 14.8367674156028, + 15.834297629942883, + 14.205056609914148, + 15.2198870074443, + 16.733802065830545, + 16.078873980639404, + 15.964878569913978, + 15.312269902913977, + 14.563872321673, + 17.109810328909987, + 15.42746783574979, + 15.187933957880919, + 17.109810328909987, + 15.834297629942883, + 15.567674618888258, + 16.720875566168758, + 15.42746783574979, + 16.2697167644664, + 14.205056609914148, + 16.43824416121008, + 16.2697167644664, + 15.65776938938505, + 14.597720997785041, + 14.108577200300326, + 14.563872321673, + 13.396510478462671, + 15.466247470015084, + 15.187933957880919, + 14.61918488159425, + 14.346794687236407, + 15.202454906071104, + 15.525973084489191, + 14.563872321673, + 17.979393423628157, + 15.187933957880919, + 14.597720997785041, + 15.795038161842308, + 15.91724628240999, + 16.032992816445045, + 14.94040577786713, + 14.916199859214707, + 15.101366547867132, + 16.720875566168758, + 14.94040577786713, + 14.597720997785041, + 15.834297629942883, + 15.027558602568279, + 16.2697167644664, + 16.2697167644664, + 16.032992816445045, + 15.016993650470285, + 15.248611447727734, + 16.720875566168758, + 14.94040577786713, + 16.405841140620637, + 14.94040577786713 + ], + "losses": [ + 8784.99689313645, + 6028.088698749184, + 6674.62797204885, + 6318.650670322865, + 8709.528691828156, + 7682.38760014809, + 10645.367632658927, + 6401.087761358599, + 6946.020087822352, + 10113.7752361747, + 8725.431419100882, + 8246.724648677668, + 11545.213367219221, + 7431.5621869248835, + 7753.098482401574, + 7753.098482401574, + 8246.724648677668, + 11090.321587280538, + 6997.435288622984, + 8777.3817424105, + 6313.255100148023, + 9930.331900277071, + 6946.020087822352, + 6493.430038527615, + 8021.589374853718, + 5966.241354866967, + 8636.573023402168, + 8580.159126919834, + 5657.418808526202, + 7431.5621869248835, + 6724.760515895711, + 8470.917240099943, + 8670.005540635548, + 7729.3334762521035, + 7735.852114928777, + 8709.528691828156, + 8099.456706356224, + 11090.321587280538, + 11050.546358189142, + 7431.5621869248835, + 8636.573023402168, + 6028.088698749184, + 10379.413670628699, + 9549.127338777747, + 8232.459722885149, + 8624.677214810807, + 10560.026497676323, + 7622.963913777213, + 8849.619944889773, + 8780.63126214931, + 8159.86797204885, + 6674.62797204885, + 6946.020087822352, + 9999.62070247934, + 5966.241354866967, + 6313.255100148023, + 9999.62070247934, + 8624.677214810807, + 8043.376906991039, + 8709.528691828156, + 5966.241354866967, + 6028.088698749184, + 10560.026497676323, + 7981.222906704189, + 6028.088698749184, + 11757.582021635626, + 8784.99689313645, + 6401.087761358599, + 6946.020087822352, + 6485.918693035536, + 6848.858106448798, + 6313.255100148023, + 7101.382277904996, + 7004.197548597559, + 12339.89333513819, + 9930.331900277071, + 6946.020087822352, + 8472.784284178542, + 6313.255100148023, + 8784.99689313645, + 8719.164316005646, + 10402.800897121735, + 8654.317085360193, + 10449.049125174153, + 4768.801291163598, + 10379.413670628699, + 8709.528691828156, + 10449.049125174153, + 8784.99689313645, + 8624.677214810807, + 5209.828315663131, + 6028.088698749184, + 6028.088698749184, + 8654.317085360193, + 5478.151137990369, + 7776.108597214346, + 8709.528691828156, + 10449.049125174153, + 9549.127338777747, + 10449.049125174153 + ], + "constraints": [ + -18.518, + -19.829000000000008, + -18.518, + -19.391999999999996, + -19.829000000000008, + -18.518, + -19.829000000000008, + -18.955, + -18.955, + -18.518, + -19.829000000000008, + -18.955, + -19.391999999999996, + -19.829000000000008, + -19.391999999999996, + -19.391999999999996, + -18.955, + -19.829000000000008, + -18.955, + -19.829000000000008, + -18.955, + -19.829000000000008, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -19.829000000000008, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.391999999999996, + -19.829000000000008, + -18.518, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.829000000000008, + -18.955, + -18.518, + -19.391999999999996, + -19.829000000000008, + -18.518, + -18.955, + -19.391999999999996, + -19.829000000000008, + -18.518, + -18.955, + -19.829000000000008, + -18.955, + -18.955, + -19.829000000000008, + -19.391999999999996, + -19.829000000000008, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -18.518, + -18.955, + -18.955, + -18.518, + -18.955, + -18.955, + -18.955, + -19.391999999999996, + -19.391999999999996, + -19.829000000000008, + -18.955, + -19.391999999999996, + -18.955, + -18.518, + -19.829000000000008, + -18.955, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -18.518, + -19.391999999999996, + -18.955, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -19.829000000000008, + -18.518, + -19.829000000000008, + -19.829000000000008, + -18.955, + -19.829000000000008 + ] + }, + "fitness_history": { + "gen": [ + 0, + 1, + 2, + 3 + ], + "avg": [ + 31.761298415855272, + 27.85142881741465, + 25.347529410486928, + 23.620183102769932 + ], + "max": [ + 47.91271540764464, + 35.33836760234757, + 30.369824129831095, + 27.861358064223104 + ], + "min": [ + 19.887612550517698, + 19.940876857848036, + 19.988951680073594, + 19.988951680073594 + ] + }, + "fixed_seed": 42, + "eautocharge_hours_float": [ + 0.625, + 0.375, + 0.5, + 1.0, + 1.0, + 0.75, + 0.5, + 0.375, + 0.375, + 0.625, + 0.75, + 0.875, + 0.625, + 0.625, + 0.625, + 1.0, + 0.375, + 0.625, + 0.875, + 0.0, + 0.5, + 0.0, + 0.0, + 0.5, + 0.875, + 0.625, + 0.75, + 0.5, + 0.375, + 0.375, + 0.75, + 0.625, + 1.0, + 0.375, + 0.625, + 0.625, + 0.0, + 0.5, + 0.875, + 0.375, + 0.5, + 0.875, + 0.5, + 0.875, + 0.75, + 0.625, + 0.75, + 0.0 + ], + "eauto_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.75, + 0.875, + 0.625, + 0.625, + 0.625, + 1.0, + 0.375, + 0.5, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59373.0, + "initial_soc_percentage": 5 + } +} \ No newline at end of file diff --git a/tests/testdata/genetic0/optimize_result_2_full.json b/tests/testdata/genetic0/optimize_result_2_full.json new file mode 100644 index 00000000..187529ec --- /dev/null +++ b/tests/testdata/genetic0/optimize_result_2_full.json @@ -0,0 +1,3711 @@ +{ + "parameters": { + "ems": { + "pv_forecast_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "electricity_price_per_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "feed_in_tariff_per_wh": 7e-05, + "price_per_wh_battery": 0.0, + "total_load": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "pv_prognose_wh": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 8.05, + 352.91, + 728.51, + 930.28, + 1043.25, + 1106.74, + 1161.69, + 6018.82, + 5519.07, + 3969.88, + 3017.96, + 1943.07, + 1007.17, + 319.67, + 7.88, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.04, + 335.59, + 705.32, + 1121.12, + 1604.79, + 2157.38, + 1433.25, + 5718.49, + 4553.96, + 3027.55, + 2574.46, + 1720.4, + 963.4, + 383.3, + 0.0, + 0.0, + 0.0 + ], + "strompreis_euro_pro_wh": [ + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "einspeiseverguetung_euro_pro_wh": 7e-05, + "preis_euro_pro_wh_akku": 0.0, + "gesamtlast": [ + 676.71, + 876.19, + 527.13, + 468.88, + 531.38, + 517.95, + 483.15, + 472.28, + 1011.68, + 995.0, + 1053.07, + 1063.91, + 1320.56, + 1132.03, + 1163.67, + 1176.82, + 1216.22, + 1103.78, + 1129.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ] + }, + "pv_battery": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "inverter": { + "device_id": "inverter1", + "max_power_wh": 10000.0, + "battery_id": "battery1" + }, + "ev": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + }, + "dishwasher": { + "device_id": "dishwasher1", + "consumption_wh": 5000, + "duration_h": 2, + "time_windows": { + "windows": [ + { + "start_time": "00:00:00.000000", + "duration": "2 days" + } + ] + } + }, + "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.0, + 18.6, + 19.2, + 19.1, + 18.7, + 18.5, + 17.7, + 16.2, + 14.6, + 13.6, + 13.0, + 12.6, + 12.2, + 11.7, + 11.6, + 11.3, + 11.0, + 10.7, + 10.2, + 11.4, + 14.4, + 16.4, + 18.3, + 19.5, + 20.7, + 21.9, + 22.7, + 23.1, + 23.1, + 22.8, + 21.8, + 20.2, + 19.1, + 18.0, + 17.4 + ], + "start_solution": null, + "pv_akku": { + "device_id": "battery1", + "capacity_wh": 26400, + "initial_soc_percentage": 80, + "min_soc_percentage": 0 + }, + "eauto": { + "device_id": "ev1", + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040.0, + "initial_soc_percentage": 5, + "min_soc_percentage": 80 + } + }, + "ac_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "dc_charge": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0 + ], + "discharge_allowed": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ], + "ev_charge_hours_float": [ + 0.875, + 0.75, + 0.5, + 1.0, + 0.625, + 0.625, + 0.875, + 0.625, + 0.875, + 0.875, + 0.0, + 1.0, + 0.5, + 0.625, + 1.0, + 0.75, + 0.5, + 0.625, + 0.375, + 0.5, + 0.625, + 0.625, + 0.5, + 0.625, + 0.625, + 0.0, + 0.625, + 0.5, + 0.5, + 1.0, + 0.375, + 0.625, + 0.875, + 0.5, + 0.5, + 0.75, + 0.75, + 0.75, + 0.0, + 0.875, + 0.75, + 1.0, + 1.0, + 0.75, + 0.625, + 0.875, + 0.5, + 0.875 + ], + "result": { + "load_wh_per_hour": [ + 1053.07, + 11551.91, + 6564.5599999999995, + 7687.03, + 14151.67, + 11542.82, + 6460.22, + 7658.78, + 5062.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "ev_soc_per_hour": [ + 5.0, + 5.0, + 22.48, + 31.22, + 42.144999999999996, + 59.62499999999999, + 72.735, + 81.475, + 92.4, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955 + ], + "revenue_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "total_losses": 7647.623819992857, + "total_balance": 7.824605715847156, + "total_revenue": 0.0, + "total_costs": 7.824605715847156, + "home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "costs_per_hour": [ + 0.027996119999999992, + 1.351235592, + 1.1423217259999998, + 1.226111386, + 1.4948178300000001, + 1.2071595, + 0.0, + 1.0534661399999998, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19588158, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.01995551999999987, + 0.08545095, + 0.007989913613567745, + 0.012219458233588571, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "grid_consumption_wh_per_hour": [ + 122.78999999999996, + 6108.66, + 5457.82, + 6525.34, + 8132.85, + 6023.75, + 0.0, + 4640.82, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 704.61, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 65.59999999999957, + 351.65, + 35.04348076126204, + 55.24167375040041, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "grid_feed_in_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "losses_per_hour": [ + 0.0, + 1152.0, + 276.0, + 345.0, + 552.0, + 414.0, + 615.5918181818183, + 345.0, + 632.3249999999998, + 23.391818181818195, + 99.72409090909093, + 133.72909090909081, + 124.41545454545451, + 0.0, + 70.41409090909087, + 118.37045454545455, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 69.12409090909085, + 109.0704545454546, + 101.01681818181828, + 0.0, + 11.233982308648535, + 48.41330768174522, + 161.62968357967037, + 21.962728535423857, + 538.2984000000038, + 441.95211196761403, + 260.56941082122324, + 171.99990368477063, + 62.214291413756285, + 35.132727272727266, + 77.27590909090907, + 134.59227272727276, + 100.08954545454549, + 80.85954545454547 + ], + "battery_soc_per_hour": [ + 80.0, + 80.0, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 50.341167355371894, + 50.341167355371894, + 36.91550447658402, + 36.17712637741047, + 33.0292699724518, + 28.808023415977967, + 24.88076790633609, + 24.88076790633609, + 22.658100895316807, + 18.921659779614323, + 15.932937327823693, + 13.312456955922865, + 10.917871900826448, + 8.813490013774107, + 6.63154269972452, + 3.1886621900826473, + 0.0, + 0.0, + 0.3120550641291261, + 1.07020564583707, + 4.578139530578446, + 4.728141236897871, + 19.68087457022947, + 31.94341995234899, + 38.90006700591099, + 42.674787887051245, + 43.17025540478875, + 42.06126780148296, + 39.622002994320425, + 35.373509537020155, + 32.214117319389295 + ], + "electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ], + "Last_Wh_pro_Stunde": [ + 1053.07, + 11551.91, + 6564.5599999999995, + 7687.03, + 14151.67, + 11542.82, + 6460.22, + 7658.78, + 5062.12, + 1178.71, + 1050.98, + 988.56, + 912.38, + 704.61, + 516.37, + 868.05, + 694.34, + 608.79, + 556.31, + 488.89, + 506.91, + 804.89, + 1141.98, + 1056.97, + 992.46, + 1155.99, + 827.01, + 1257.98, + 1232.67, + 871.26, + 860.88, + 1158.03, + 1222.72, + 1221.04, + 949.99, + 987.01, + 733.99, + 592.97 + ], + "EAuto_SoC_pro_Stunde": [ + 5.0, + 5.0, + 22.48, + 31.22, + 42.144999999999996, + 59.62499999999999, + 72.735, + 81.475, + 92.4, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955, + 98.955 + ], + "Einnahmen_Euro_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Gesamt_Verluste": 7647.623819992857, + "Gesamtbilanz_Euro": 7.824605715847156, + "Gesamteinnahmen_Euro": 0.0, + "Gesamtkosten_Euro": 7.824605715847156, + "Home_appliance_wh_per_hour": [ + 0.0, + 0.0, + 0.0, + 0.0, + 2500.0, + 2500.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Kosten_Euro_pro_Stunde": [ + 0.027996119999999992, + 1.351235592, + 1.1423217259999998, + 1.226111386, + 1.4948178300000001, + 1.2071595, + 0.0, + 1.0534661399999998, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.19588158, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.01995551999999987, + 0.08545095, + 0.007989913613567745, + 0.012219458233588571, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Netzbezug_Wh_pro_Stunde": [ + 122.78999999999996, + 6108.66, + 5457.82, + 6525.34, + 8132.85, + 6023.75, + 0.0, + 4640.82, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 704.61, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 65.59999999999957, + 351.65, + 35.04348076126204, + 55.24167375040041, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Netzeinspeisung_Wh_pro_Stunde": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "Verluste_Pro_Stunde": [ + 0.0, + 1152.0, + 276.0, + 345.0, + 552.0, + 414.0, + 615.5918181818183, + 345.0, + 632.3249999999998, + 23.391818181818195, + 99.72409090909093, + 133.72909090909081, + 124.41545454545451, + 0.0, + 70.41409090909087, + 118.37045454545455, + 94.68272727272722, + 83.01681818181817, + 75.86045454545456, + 66.66681818181814, + 69.12409090909085, + 109.0704545454546, + 101.01681818181828, + 0.0, + 11.233982308648535, + 48.41330768174522, + 161.62968357967037, + 21.962728535423857, + 538.2984000000038, + 441.95211196761403, + 260.56941082122324, + 171.99990368477063, + 62.214291413756285, + 35.132727272727266, + 77.27590909090907, + 134.59227272727276, + 100.08954545454549, + 80.85954545454547 + ], + "akku_soc_pro_stunde": [ + 80.0, + 80.0, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 61.06060606060606, + 50.341167355371894, + 50.341167355371894, + 36.91550447658402, + 36.17712637741047, + 33.0292699724518, + 28.808023415977967, + 24.88076790633609, + 24.88076790633609, + 22.658100895316807, + 18.921659779614323, + 15.932937327823693, + 13.312456955922865, + 10.917871900826448, + 8.813490013774107, + 6.63154269972452, + 3.1886621900826473, + 0.0, + 0.0, + 0.3120550641291261, + 1.07020564583707, + 4.578139530578446, + 4.728141236897871, + 19.68087457022947, + 31.94341995234899, + 38.90006700591099, + 42.674787887051245, + 43.17025540478875, + 42.06126780148296, + 39.622002994320425, + 35.373509537020155, + 32.214117319389295 + ], + "Electricity_price": [ + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278, + 0.0003384, + 0.0003318, + 0.0003284, + 0.0003283, + 0.0003289, + 0.0003334, + 0.000329, + 0.0003302, + 0.0003042, + 0.000243, + 0.000228, + 0.0002212, + 0.0002093, + 0.0001879, + 0.0001838, + 0.0002004, + 0.0002198, + 0.000227, + 0.0002997, + 0.0003195, + 0.0003081, + 0.0002969, + 0.0002921, + 0.000278 + ] + }, + "ev_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.5, + 0.625, + 1.0, + 0.75, + 0.5, + 0.625, + 0.375, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59373.0, + "initial_soc_percentage": 5 + }, + "start_hour": 10, + "start_solution": [ + 0.0, + 0.0, + 2.0, + 1.0, + 0.0, + 1.0, + 2.0, + 2.0, + 1.0, + 1.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 1.0, + 5.0, + 4.0, + 2.0, + 6.0, + 3.0, + 3.0, + 5.0, + 3.0, + 5.0, + 5.0, + 0.0, + 6.0, + 2.0, + 3.0, + 6.0, + 4.0, + 2.0, + 3.0, + 1.0, + 2.0, + 3.0, + 3.0, + 2.0, + 3.0, + 3.0, + 0.0, + 3.0, + 2.0, + 2.0, + 6.0, + 1.0, + 3.0, + 5.0, + 2.0, + 2.0, + 4.0, + 4.0, + 4.0, + 0.0, + 5.0, + 4.0, + 6.0, + 6.0, + 4.0, + 3.0, + 5.0, + 2.0, + 5.0, + 14.0 + ], + "washingstart": 14, + "extra_data": { + "balance": [ + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 8.250842714342252, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156 + ], + "losses": [ + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7629.147481119599, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857, + 7647.623819992857 + ], + "constraints": [ + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -19.829000000000008, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955, + -18.955 + ] + }, + "fitness_history": { + "gen": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263, + 264, + 265, + 266, + 267, + 268, + 269, + 270, + 271, + 272, + 273, + 274, + 275, + 276, + 277, + 278, + 279, + 280, + 281, + 282, + 283, + 284, + 285, + 286, + 287, + 288, + 289, + 290, + 291, + 292, + 293, + 294, + 295, + 296, + 297, + 298, + 299, + 300, + 301, + 302, + 303, + 304, + 305, + 306, + 307, + 308, + 309, + 310, + 311, + 312, + 313, + 314, + 315, + 316, + 317, + 318, + 319, + 320, + 321, + 322, + 323, + 324, + 325, + 326, + 327, + 328, + 329, + 330, + 331, + 332, + 333, + 334, + 335, + 336, + 337, + 338, + 339, + 340, + 341, + 342, + 343, + 344, + 345, + 346, + 347, + 348, + 349, + 350, + 351, + 352, + 353, + 354, + 355, + 356, + 357, + 358, + 359, + 360, + 361, + 362, + 363, + 364, + 365, + 366, + 367, + 368, + 369, + 370, + 371, + 372, + 373, + 374, + 375, + 376, + 377, + 378, + 379, + 380, + 381, + 382, + 383, + 384, + 385, + 386, + 387, + 388, + 389, + 390, + 391, + 392, + 393, + 394, + 395, + 396, + 397, + 398, + 399, + 400 + ], + "avg": [ + 16.685518856626615, + 15.61390152051758, + 14.988718004044808, + 14.601365045870402, + 14.294721932651825, + 13.906269422267185, + 13.636534240565156, + 13.23072823626663, + 12.852475571881898, + 12.612852652665381, + 12.295333564136946, + 11.999970526857275, + 11.839011933723295, + 11.628652783605627, + 11.435317552346412, + 11.089168473062392, + 10.663574391420076, + 10.41900750133103, + 10.006287879763317, + 9.774884134097023, + 9.635718756196422, + 9.42997374927623, + 9.11760658902115, + 9.075110144016502, + 9.001960354187176, + 8.911528757750508, + 8.868098579546631, + 8.82804758499918, + 8.88711895316457, + 8.914267690878058, + 8.739695394348908, + 8.687561956968164, + 8.746906158433298, + 8.746317176446416, + 8.665567563083144, + 8.729031261892757, + 8.68497653932458, + 8.644913556669787, + 8.59156896642245, + 8.50142537513843, + 8.56477632802283, + 8.418040870698428, + 8.456144558926384, + 8.433398348006772, + 8.399133735788414, + 8.448978160724293, + 8.46253347184992, + 8.425582770381913, + 8.401996479068414, + 8.426815118630254, + 8.430501661077608, + 8.399133735788414, + 8.418292481620844, + 8.399133735788414, + 8.436114275690255, + 8.417918692368414, + 8.436703648948415, + 8.428872518362278, + 8.418740341788414, + 8.419584476232279, + 8.408594093520088, + 8.431600976999738, + 8.459374973238107, + 8.438959622481793, + 8.441699785478896, + 8.394513172273834, + 8.449648663597298, + 8.40599131039926, + 8.371608603302182, + 8.31545190184288, + 8.280797714773154, + 8.29927741712519, + 8.266264743768271, + 8.303326901449077, + 8.313814862512134, + 8.296612467755528, + 8.348234380003065, + 8.301203936022773, + 8.24833547656417, + 8.227187729242848, + 8.219347483369354, + 8.2519094197168, + 8.19544885230745, + 8.216426971379684, + 8.193893201122183, + 8.180912805935014, + 8.2599017130458, + 8.26600292659671, + 8.177135552955821, + 8.22200906403718, + 8.204279298291143, + 8.172506426916408, + 8.211262613309259, + 8.172330753215338, + 8.162285913524919, + 8.14514773669585, + 8.177397233619057, + 8.169823162326345, + 8.141168017847153, + 8.162627896614817, + 8.193039747215776, + 8.156378264557155, + 8.141168017847153, + 8.146073660377544, + 8.146313280297544, + 8.174800388526597, + 8.214851270488358, + 8.15226130226548, + 8.178459692580764, + 8.141168017847153, + 8.170316383727155, + 8.158910532063455, + 8.14621719687824, + 8.165783626668683, + 8.166436218315276, + 8.141168017847153, + 8.145859508928682, + 8.23130400780516, + 8.21369537979069, + 8.165705623544541, + 8.159173072950614, + 8.174033412120677, + 8.183491543957649, + 8.141168017847153, + 8.141168017847153, + 8.146039820248362, + 8.254502255213815, + 8.159042228956597, + 8.189669982638963, + 8.148515380907154, + 8.15912962832345, + 8.137353854247154, + 8.134419882247155, + 8.134248947419568, + 8.188101483479088, + 8.119305582757157, + 8.129441270107264, + 8.132543839522796, + 8.132166728856808, + 8.123171074599998, + 8.12145157452044, + 8.140533962890213, + 8.142527235975683, + 8.180145472492644, + 8.111828297847156, + 8.111828297847156, + 8.152802215006865, + 8.14007168297701, + 8.149149068496442, + 8.186868689405541, + 8.250352896623577, + 8.135618436353193, + 8.148144378488837, + 8.142800609254536, + 8.139733636608685, + 8.131705874478685, + 8.145833285092044, + 8.169007698984986, + 8.17983232565918, + 8.111828297847156, + 8.14451735035165, + 8.144497729229586, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.15809905891819, + 8.215053032589857, + 8.163948567618043, + 8.146572509308685, + 8.128860913878244, + 8.158231737061115, + 8.137898938608684, + 8.199424144490163, + 8.174708592757476, + 8.150678833925408, + 8.096451746888599, + 8.084804269967156, + 8.117437702397705, + 8.184391310619809, + 8.120703948098683, + 8.084675497847154, + 8.1569948318699, + 8.084675497847154, + 8.109059149694986, + 8.084975061987155, + 8.124808552909935, + 8.084675497847154, + 8.084675497847154, + 8.198057881681093, + 8.134010285489602, + 8.15174455763613, + 8.097757860242107, + 8.084675497847154, + 8.084675497847154, + 8.085856324899812, + 8.084675497847154, + 8.111284392517877, + 8.084675497847154, + 8.100220497822106, + 8.206805389293631, + 8.101790117226678, + 8.130757680707598, + 8.186792246908448, + 8.084675497847154, + 8.145693495458397, + 8.133296136565145, + 8.13882711458339, + 8.19293136518798, + 8.13455300575631, + 8.175394281599221, + 8.18449413018885, + 8.146807632570802, + 8.088850724332893, + 8.084623186980389, + 8.16295342734593, + 8.127652257214406, + 8.100263747469963, + 8.0887758157183, + 8.08374697996204, + 8.111572804663192, + 8.11604455828245, + 8.083367726177979, + 8.0914290776862, + 8.132510847095304, + 8.10434566549442, + 8.085735934075563, + 8.157326986901571, + 8.125064651218942, + 8.211820347515971, + 8.16993040353364, + 8.115654436551528, + 8.132316126075843, + 8.160990684383917, + 8.111265198038055, + 8.156557490057562, + 8.10187896058442, + 8.112775258111858, + 8.106453305243638, + 8.220333295603616, + 8.124961286484455, + 8.106302613033863, + 8.111419156269621, + 8.083367726177979, + 8.083367726177979, + 8.093603355567314, + 8.139210391358562, + 8.110036521623131, + 8.113560356335407, + 8.110963455727157, + 8.083367726177979, + 8.133512862746205, + 8.083367726177979, + 8.117088332377712, + 8.130307235032216, + 8.101624925929164, + 8.11965203083701, + 8.180899972831305, + 8.083367726177979, + 8.141093896874011, + 8.140031303153256, + 8.083367726177979, + 8.13117208065127, + 8.09968043336511, + 8.08057130737264, + 8.07591060936374, + 8.073114190558401, + 8.084974554553552, + 8.114024898850445, + 8.130580789484759, + 8.15148102670518, + 7.997717710372562, + 7.993882324407118, + 7.997954934994394, + 8.052178594399262, + 8.081809447495395, + 8.01944523508, + 8.062259953037662, + 7.990153765999999, + 8.012249281599999, + 8.025581669612963, + 7.990153765999999, + 8.024693670932892, + 7.9817279947293, + 8.073339596072893, + 7.958847192226783, + 7.908897375232877, + 7.981073744600643, + 7.905294172223215, + 7.889175948011995, + 7.896894958877483, + 7.881615933811338, + 7.855652605026348, + 7.843852970496855, + 7.841487443965482, + 7.854328668408189, + 7.824605715847156, + 7.8315894370576515, + 7.853002484242106, + 7.854386317037156, + 7.961732063594212, + 7.848873367987621, + 7.884386176018038, + 7.859252830331092, + 7.893095672331686, + 7.8285512670286845, + 7.824605715847156, + 7.843409550807009, + 7.86083510781808, + 7.918183767100402, + 7.824605715847156, + 7.86097664896482, + 7.859428037576122, + 7.851663650881569, + 7.85875965103216, + 7.893317003117135, + 7.847851779903177, + 7.824605715847156, + 7.8310046852786845, + 7.885905602036348, + 7.850516160661503, + 7.824605715847156, + 7.824605715847156, + 7.834067664479585, + 7.881132093321114, + 7.9081356059611005, + 7.8642187160866, + 7.886482464563635, + 7.829646484548684, + 7.828569868128684, + 7.824605715847156, + 7.824605715847156, + 7.84536899845237, + 7.865666364318685, + 7.852444354538684, + 7.9143996273817985, + 7.8290304915736355, + 7.824605715847156, + 7.849826239641154, + 7.848705587298684, + 7.824605715847156, + 7.866141500567724, + 7.9812124582240065, + 7.842546579999557, + 7.835387742634675, + 7.8493572803221445, + 7.824605715847156, + 7.854698626248685, + 7.834611325908684, + 7.870019734500306, + 7.8286799669558835, + 7.824605715847156, + 7.880020098015384, + 7.881253137683635, + 7.864146978043147, + 7.861112207028684, + 7.824605715847156, + 7.856779181835481, + 7.824605715847156, + 7.854798532027877, + 7.834222602958778, + 7.892629141147934, + 7.870801932128684, + 7.8523061275975605, + 7.8573088526304335, + 7.873578015310929, + 7.824605715847156, + 7.859688484167651, + 7.824605715847156, + 7.849825203465607, + 7.824605715847156, + 7.944211256840067, + 7.994467984697903, + 7.854117959634821, + 7.854117959634821, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.915913393744861, + 7.849590117845483, + 7.847305172483881, + 7.824605715847156, + 7.851018528718685, + 7.888731513641822, + 7.830619442823106, + 7.838634122246349, + 7.824605715847156, + 7.870693920287708, + 7.894634041651334, + 7.844378004013177, + 7.824605715847156, + 7.824605715847156, + 7.860646889810319, + 7.885672821246865, + 7.904507235534523, + 7.859788895111795, + 7.875508147788871, + 7.868663564898684, + 7.85813289112666, + 7.824605715847156, + 7.881001242110212, + 7.828868085832108 + ], + "max": [ + 20.378343583283172, + 17.307985915077595, + 16.378933811317683, + 15.876830436675256, + 15.257367724921895, + 15.425441262719918, + 14.695906864210652, + 14.205887574069676, + 14.0012442339641, + 14.054847707351685, + 13.397401216557196, + 13.542012343726993, + 13.463633264872353, + 12.747010122791544, + 14.076924013791542, + 12.442409063062858, + 12.208054810930621, + 12.949177141290827, + 14.178232111791543, + 13.653644520931202, + 12.08494115562414, + 13.0273172943248, + 10.54295383185262, + 12.12810276297237, + 13.672298956481912, + 11.587850021788457, + 9.27923190395582, + 9.080545508955817, + 11.203523149174847, + 12.086527035867087, + 9.889182279071562, + 9.085096024417796, + 12.11087678986184, + 12.371973007828885, + 10.733598132328467, + 12.413702006174848, + 12.765383758834234, + 12.872745882832598, + 10.975978674417837, + 9.130962369788415, + 12.089863543031365, + 8.563979395788417, + 12.858290001215318, + 11.82559495762414, + 8.399133735788418, + 11.275446660989843, + 11.912059415174848, + 10.098734568832597, + 8.685408063788417, + 10.428934692788417, + 11.535926264707724, + 8.399133735788418, + 10.315008319031364, + 8.399133735788418, + 12.097187725972368, + 10.277629393788416, + 10.277629393788416, + 11.373011993174847, + 9.379464035788416, + 9.435436356174849, + 9.285746338788416, + 11.667223368585937, + 11.844930288071563, + 12.41377067262414, + 11.076394276174891, + 8.399133735788418, + 11.664446087788416, + 10.05607813440558, + 10.455879667342252, + 9.654429803174892, + 8.399133735788418, + 9.75469967117485, + 8.347532775788418, + 11.771373404381283, + 11.21076613234225, + 10.022171753342294, + 11.750412244071564, + 11.4146234052616, + 8.784965715342253, + 8.784965715342253, + 8.784965715342253, + 11.146134761955862, + 9.806130508955821, + 12.10627739534225, + 9.651341280672646, + 8.378694641875125, + 11.749863906342252, + 11.915271475504504, + 8.17713555295582, + 11.058224952031363, + 11.058224952031363, + 8.292822202753339, + 12.28498775934225, + 9.580218505449292, + 9.193759224101965, + 8.17713555295582, + 11.224227791577063, + 10.104178906613567, + 8.141168017847157, + 10.287155894613567, + 11.774321783368599, + 9.662192688847156, + 8.141168017847157, + 8.631732270886307, + 8.631732270886307, + 10.067642284342252, + 11.834501920613567, + 9.250496459679754, + 10.463153016564025, + 8.141168017847157, + 10.389809885847157, + 9.915419439477274, + 8.64608592095582, + 10.602728899999999, + 10.667988064659344, + 8.141168017847157, + 8.610317126, + 12.165370255000003, + 12.912393792957877, + 10.594928587585937, + 9.94167352819316, + 11.112847970127445, + 12.373520628896715, + 8.141168017847157, + 8.141168017847157, + 8.628348257967877, + 12.82817770652491, + 9.928589128791543, + 11.879798822000001, + 9.022602923847156, + 10.113367385476751, + 8.141168017847157, + 8.141168017847157, + 8.945586695088396, + 10.292685456644676, + 8.742197908847155, + 9.84378580385793, + 10.183382465411087, + 9.234069845659388, + 9.023429851000042, + 9.0741559651755, + 10.276932427999999, + 11.18172211069981, + 11.918621505242946, + 8.111828297847156, + 8.111828297847156, + 10.160524155832597, + 10.936166810832596, + 11.843905362775862, + 12.484503124613566, + 13.168252166955098, + 9.308044030342252, + 11.743436362015299, + 11.131087813090103, + 10.902362174000043, + 10.099585960999999, + 10.03281247023073, + 11.052432636644719, + 12.102190842999999, + 8.111828297847156, + 11.380733548296448, + 10.960018972847196, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 11.381464692999998, + 12.754662392779746, + 10.517645788999998, + 10.436691916000001, + 9.251906433955861, + 11.312612822, + 11.071878774000002, + 10.89554028800004, + 12.14378393541109, + 10.572022255411088, + 8.724300251919306, + 8.097552709847157, + 11.360895952902291, + 11.934223609743869, + 11.687520523, + 8.084675497847156, + 12.299611456136137, + 8.084675497847156, + 10.523040682630118, + 8.114631911847155, + 10.486561177342253, + 8.084675497847156, + 8.084675497847156, + 11.603970467539394, + 10.578668317444219, + 10.985625183659346, + 9.392911737342253, + 8.084675497847156, + 8.084675497847156, + 8.202758203112744, + 8.084675497847156, + 10.745564964919305, + 8.084675497847156, + 9.63917549534225, + 11.692944589504545, + 9.79613743579946, + 10.857220567093968, + 11.281861807922699, + 8.084675497847156, + 12.758662059281889, + 10.031894386342293, + 13.499837171470721, + 13.20450638657912, + 10.492689033000001, + 11.652733572242987, + 11.652733572242987, + 11.299922505743869, + 8.503505918090102, + 8.084675497847156, + 13.0858596499251, + 10.921746469000041, + 9.35308787961478, + 8.554864781743868, + 8.084675497847156, + 9.837915503000001, + 10.178431811847156, + 8.083367726177979, + 8.889502876999998, + 9.525712407847198, + 9.132264692, + 8.320188515936232, + 11.418923576139772, + 12.27626493027424, + 11.906701403613567, + 13.436411089440648, + 11.312038763532875, + 11.224037838142287, + 12.06410768700004, + 9.013283454847155, + 10.194088952443071, + 9.718603871, + 10.39192563274387, + 10.39192563274387, + 12.527617748692238, + 9.673046955847157, + 10.376856411766463, + 10.888510735342251, + 8.083367726177979, + 8.083367726177979, + 8.595149195644677, + 13.377695286791543, + 10.750247270693071, + 9.793197093896714, + 9.804494621441233, + 8.083367726177979, + 10.621305202896714, + 8.083367726177979, + 9.960373797852533, + 10.494869661982165, + 9.909087701296448, + 9.93280171679752, + 12.38866235304296, + 8.083367726177979, + 11.933133183678253, + 11.757444984644676, + 8.083367726177979, + 12.535353642729312, + 9.901066365246812, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 9.642259966405154, + 10.765810497262185, + 11.783361439, + 13.045703112489273, + 8.083367726177979, + 8.083367726177979, + 8.677056705261558, + 12.782939502, + 12.326526644449292, + 10.919300673999999, + 11.484220961792172, + 7.990153765999999, + 10.199705326, + 11.047891043449333, + 7.990153765999999, + 11.257003906, + 7.990153765999999, + 11.43918414379752, + 9.544513563875126, + 7.990153765999999, + 12.623570002853606, + 12.320897981000039, + 11.55195665085293, + 10.94252811434225, + 10.669440671797563, + 9.425996072, + 9.749331180817062, + 9.512778527679753, + 9.518406343797519, + 7.824605715847156, + 8.522977836896754, + 10.664282555342252, + 10.802665834847199, + 12.438493698526832, + 9.389620287847197, + 10.90815186706286, + 9.773151867062861, + 11.172117101904451, + 8.219160834, + 7.824605715847156, + 9.704989211832597, + 11.447544912939497, + 11.447544912939497, + 7.824605715847156, + 11.461699027613568, + 10.879847251694235, + 9.815250782238966, + 11.223618701342252, + 10.958645909242946, + 10.14921212144929, + 7.824605715847156, + 8.464502658999999, + 12.067669677000001, + 10.415650197281888, + 7.824605715847156, + 7.824605715847156, + 8.770800579090102, + 10.758202815090144, + 11.43273627932232, + 11.785905739791543, + 11.476849087342252, + 8.328682586, + 8.221020944, + 7.824605715847156, + 7.824605715847156, + 9.900933976368599, + 11.930670563, + 10.608469585, + 11.31543011300004, + 8.139126806, + 7.824605715847156, + 9.21941941183264, + 10.234592861, + 7.824605715847156, + 11.97818418790392, + 12.112630802919305, + 9.618692131087258, + 8.90280839459906, + 10.299762163346006, + 7.824605715847156, + 10.833896756, + 8.825166722, + 12.366007581162252, + 8.232030826719937, + 7.824605715847156, + 10.652781109342293, + 11.578775902342251, + 10.77601944761361, + 11.475254834, + 7.824605715847156, + 11.041952314679753, + 7.824605715847156, + 10.843887333919305, + 8.760901547090102, + 13.039974842743868, + 12.444227344, + 10.594646890887624, + 11.094919394174848, + 11.094919394174848, + 7.824605715847156, + 11.332882547896714, + 7.824605715847156, + 10.346554477692239, + 7.824605715847156, + 12.82228965, + 12.654189452, + 10.77583009461361, + 10.77583009461361, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 15.15998934746482, + 10.323045915679753, + 9.711080406519672, + 7.824605715847156, + 10.172843734, + 10.497736393875126, + 8.125292064644677, + 9.227446355766505, + 7.824605715847156, + 12.433426159902355, + 11.205922652477275, + 9.801834532449334, + 7.824605715847156, + 7.824605715847156, + 10.877960985832598, + 10.877960985832598, + 11.268341210797562, + 9.673912043238966, + 10.837602594793688, + 10.328081499, + 10.26862326179752, + 7.824605715847156, + 11.381428642, + 8.250842714342252 + ], + "min": [ + 13.820271169793038, + 13.89437687019911, + 13.579695501563089, + 12.78299149726557, + 12.75282024750524, + 12.340414043784927, + 11.952478164315156, + 11.952478164315156, + 11.581748564791543, + 11.581748564791543, + 11.581748564791543, + 10.919459375791543, + 10.919459375791543, + 10.136886997872887, + 10.136886997872887, + 8.928814496221406, + 8.928339875788415, + 8.928339875788415, + 8.817093132031363, + 8.817093132031363, + 8.817093132031363, + 8.817093132031363, + 8.817093132031363, + 8.817093132031363, + 8.800366346788417, + 8.596842299788417, + 8.712518711788416, + 8.563979395788417, + 8.563979395788417, + 8.563979395788417, + 8.563979395788417, + 8.563979395788417, + 8.502876877788417, + 8.502876877788417, + 8.502876877788417, + 8.45027072478846, + 8.45027072478846, + 8.45027072478846, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.347532775788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.399133735788418, + 8.38845097995582, + 8.38845097995582, + 8.38845097995582, + 8.352515651174851, + 8.27019967117485, + 8.27019967117485, + 8.27019967117485, + 8.259516915342253, + 8.259516915342253, + 8.259516915342253, + 8.259516915342253, + 8.259516915342253, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.17713555295582, + 8.143792579847156, + 8.143792579847156, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.141168017847157, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.111828297847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.084675497847156, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.060163026177978, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 8.083367726177979, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.990153765999999, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156, + 7.824605715847156 + ] + }, + "fixed_seed": 42, + "eautocharge_hours_float": [ + 0.875, + 0.75, + 0.5, + 1.0, + 0.625, + 0.625, + 0.875, + 0.625, + 0.875, + 0.875, + 0.0, + 1.0, + 0.5, + 0.625, + 1.0, + 0.75, + 0.5, + 0.625, + 0.375, + 0.5, + 0.625, + 0.625, + 0.5, + 0.625, + 0.625, + 0.0, + 0.625, + 0.5, + 0.5, + 1.0, + 0.375, + 0.625, + 0.875, + 0.5, + 0.5, + 0.75, + 0.75, + 0.75, + 0.0, + 0.875, + 0.75, + 1.0, + 1.0, + 0.75, + 0.625, + 0.875, + 0.5, + 0.875 + ], + "eauto_obj": { + "device_id": "ev1", + "hours": 48, + "charge_array": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.5, + 0.625, + 1.0, + 0.75, + 0.5, + 0.625, + 0.375, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "discharge_array": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "discharging_efficiency": 1.0, + "capacity_wh": 60000, + "charging_efficiency": 0.95, + "max_charge_power_w": 11040, + "soc_wh": 59373.0, + "initial_soc_percentage": 5 + } +} \ No newline at end of file diff --git a/tests/testdata/genetic0/test_example_report.pdf b/tests/testdata/genetic0/test_example_report.pdf new file mode 100644 index 0000000000000000000000000000000000000000..20e6747f91ead137fb64e9f1e0bb72e5da69d443 GIT binary patch literal 97354 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

4`}?K?h{P4AM2IhJ@HIcI#4 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

qhkg64}Ps;G%M9f&E+w__4LX8>W%QG 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