mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-07-31 22:26:12 +00:00
chore: prepare for update of genetic algorithm (#1190)
Bump Version / Bump Version Workflow (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
docker-build / platform-excludes (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
Bump Version / Bump Version Workflow (push) Has been cancelled
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
docker-build / platform-excludes (push) Has been cancelled
docker-build / build (push) Has been cancelled
docker-build / merge (push) Has been cancelled
pre-commit / pre-commit (push) Has been cancelled
Run Pytest on Pull Request / test (push) Has been cancelled
Andreas will update the genetic algorithm for 15-minutes optimization intervals. Copy the current GENETIC optimization algorithm to GENETIC0 to enable to keep the algorithm with the current functionality. Also copy resources like the load interpolator to the GENETIC0 algorithm to keep them despite possible later changes to the interpolator. Make the deprecated legacy /optimize endpoint use the GENETIC0 optimization algorithm to in-fact behave the same way even if there will later be changes to the GENETIC algorithm by Andreas. Add a new REST endpoint to provide the unprocessed optimisation results of the GENETIC and GENETIC0 algorithm in case one wants to use them as done with the deprecated /optimize endpoint. Adapt the optimization configuration to have distinct configurations for the GENETIC and the GENETIC0 algorithm. Create a copy of the current tests for the GENETIC algorithm to be used for the GENETIC0 algorithm. This avoids the tests for the GENETIC0 algorithm to be influenced by later changes by Andreas. Signed-off-by: Bobby Noelte <b0661n0e17e@gmail.com>
This commit is contained in:
Executable
+525
@@ -0,0 +1,525 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import asyncio
|
||||
import cProfile
|
||||
import json
|
||||
import pstats
|
||||
import sys
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
from loguru import logger
|
||||
|
||||
from akkudoktoreos.core.coreabc import get_config, get_ems, get_prediction
|
||||
from akkudoktoreos.core.emsettings import EnergyManagementMode
|
||||
from akkudoktoreos.optimization.genetic0.genetic0params import (
|
||||
Genetic0OptimizationParameters,
|
||||
)
|
||||
from akkudoktoreos.utils.datetimeutil import to_datetime
|
||||
|
||||
config_eos = get_config()
|
||||
prediction_eos = get_prediction()
|
||||
ems_eos = get_ems()
|
||||
|
||||
|
||||
async def prepare_optimization_real_parameters() -> Genetic0OptimizationParameters:
|
||||
"""Prepare and return optimization parameters with real world data.
|
||||
|
||||
Returns:
|
||||
GeneticOptimizationParameters: Configured optimization parameters
|
||||
"""
|
||||
# Make a config
|
||||
settings = {
|
||||
"general": {
|
||||
"latitude": 52.52,
|
||||
"longitude": 13.405,
|
||||
},
|
||||
"prediction": {
|
||||
"hours": 48,
|
||||
"historic_hours": 24,
|
||||
},
|
||||
"optimization": {
|
||||
"algorithm": "GENETIC0",
|
||||
"genetic0": {
|
||||
"horizon_hours": 24,
|
||||
"individuals": 300,
|
||||
"generations": 400,
|
||||
"seed": None,
|
||||
"penalties": {
|
||||
"ev_soc_miss": 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
# PV Forecast
|
||||
"pvforecast": {
|
||||
"provider": "PVForecastAkkudoktor",
|
||||
"planes": [
|
||||
{
|
||||
"peakpower": 5.0,
|
||||
"surface_azimuth": -10,
|
||||
"surface_tilt": 7,
|
||||
"userhorizon": [20, 27, 22, 20],
|
||||
"inverter_paco": 10000,
|
||||
},
|
||||
{
|
||||
"peakpower": 4.8,
|
||||
"surface_azimuth": -90,
|
||||
"surface_tilt": 7,
|
||||
"userhorizon": [30, 30, 30, 50],
|
||||
"inverter_paco": 10000,
|
||||
},
|
||||
{
|
||||
"peakpower": 1.4,
|
||||
"surface_azimuth": -40,
|
||||
"surface_tilt": 60,
|
||||
"userhorizon": [60, 30, 0, 30],
|
||||
"inverter_paco": 2000,
|
||||
},
|
||||
{
|
||||
"peakpower": 1.6,
|
||||
"surface_azimuth": 5,
|
||||
"surface_tilt": 45,
|
||||
"userhorizon": [45, 25, 30, 60],
|
||||
"inverter_paco": 1400,
|
||||
},
|
||||
],
|
||||
},
|
||||
# Weather Forecast
|
||||
"weather": {
|
||||
"provider": "ClearOutside",
|
||||
},
|
||||
# Electricity Price Forecast
|
||||
"elecprice": {
|
||||
"provider": "ElecPriceAkkudoktor",
|
||||
},
|
||||
# Load Forecast
|
||||
"load": {
|
||||
"provider": "LoadAkkudoktor",
|
||||
"loadakkudoktor": {
|
||||
"loadakkudoktor_year_energy_kwh": 5000, # Energy consumption per year in kWh
|
||||
},
|
||||
},
|
||||
# -- Simulations --
|
||||
# Assure we have charge rates for the EV
|
||||
"devices": {
|
||||
"max_electric_vehicles": 1,
|
||||
"electric_vehicles": [
|
||||
{
|
||||
"charge_rates": [
|
||||
0.0,
|
||||
6.0 / 16.0,
|
||||
8.0 / 16.0,
|
||||
10.0 / 16.0,
|
||||
12.0 / 16.0,
|
||||
14.0 / 16.0,
|
||||
1.0,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
# Update/ set configuration
|
||||
config_eos.merge_settings_from_dict(settings)
|
||||
|
||||
# Get current prediction data for optimization run
|
||||
ems_eos.set_start_datetime()
|
||||
print(
|
||||
f"Real data prediction from {prediction_eos.ems_start_datetime} to {prediction_eos.end_datetime}"
|
||||
)
|
||||
await prediction_eos.update_data()
|
||||
|
||||
# PV Forecast (in W)
|
||||
pv_forecast = await prediction_eos.key_to_array(
|
||||
key="pvforecast_ac_power",
|
||||
start_datetime=prediction_eos.ems_start_datetime,
|
||||
end_datetime=prediction_eos.end_datetime,
|
||||
)
|
||||
print(f"pv_forecast: {pv_forecast}")
|
||||
|
||||
# Temperature Forecast (in degree C)
|
||||
temperature_forecast = await prediction_eos.key_to_array(
|
||||
key="weather_temp_air",
|
||||
start_datetime=prediction_eos.ems_start_datetime,
|
||||
end_datetime=prediction_eos.end_datetime,
|
||||
)
|
||||
print(f"temperature_forecast: {temperature_forecast}")
|
||||
|
||||
# Electricity price (per Wh)
|
||||
electricity_price_per_wh = await prediction_eos.key_to_array(
|
||||
key="elecprice_marketprice_wh",
|
||||
start_datetime=prediction_eos.ems_start_datetime,
|
||||
end_datetime=prediction_eos.end_datetime,
|
||||
fill_method="ffill",
|
||||
)
|
||||
print(f"electricity_price_per_wh: {electricity_price_per_wh}")
|
||||
|
||||
# Overall System Load (in W)
|
||||
total_load = await prediction_eos.key_to_array(
|
||||
key="load_mean",
|
||||
start_datetime=prediction_eos.ems_start_datetime,
|
||||
end_datetime=prediction_eos.end_datetime,
|
||||
)
|
||||
print(f"total_load: {total_load}")
|
||||
|
||||
# Start Solution (binary)
|
||||
start_solution = None
|
||||
print(f"start_solution: {start_solution}")
|
||||
|
||||
# Define parameters for the optimization problem
|
||||
return Genetic0OptimizationParameters(
|
||||
**{
|
||||
"ems": {
|
||||
"price_per_wh_battery": 0e-05,
|
||||
"feed_in_tariff_per_wh": 7e-05,
|
||||
"total_load": total_load,
|
||||
"pv_forecast_wh": pv_forecast,
|
||||
"electricity_price_per_wh": electricity_price_per_wh,
|
||||
},
|
||||
"pv_battery": {
|
||||
"device_id": "battery 1",
|
||||
"capacity_wh": 26400,
|
||||
"initial_soc_percentage": 15,
|
||||
"min_soc_percentage": 15,
|
||||
},
|
||||
"inverter": {
|
||||
"device_id": "inverter 1",
|
||||
"max_power_wh": 10000,
|
||||
"battery_id": "battery 1",
|
||||
},
|
||||
"ev": {
|
||||
"device_id": "electric vehicle 1",
|
||||
"min_soc_percentage": 50,
|
||||
"capacity_wh": 60000,
|
||||
"charging_efficiency": 0.95,
|
||||
"max_charge_power_w": 11040,
|
||||
"initial_soc_percentage": 5,
|
||||
},
|
||||
"temperature_forecast": temperature_forecast,
|
||||
"start_solution": start_solution,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def prepare_optimization_parameters() -> Genetic0OptimizationParameters:
|
||||
"""Prepare and return optimization parameters with predefined data.
|
||||
|
||||
Returns:
|
||||
GeneticOptimizationParameters: Configured optimization parameters
|
||||
"""
|
||||
# Initialize the optimization problem using the default configuration
|
||||
config_eos.merge_settings_from_dict(
|
||||
{
|
||||
"prediction": {"hours": 48},
|
||||
"optimization": {
|
||||
"horizon_hours": 48,
|
||||
"interval": 3600,
|
||||
"algorithm": "GENETIC0",
|
||||
"genetic0": {
|
||||
"individuals": 300,
|
||||
"generations": 400,
|
||||
"seed": None,
|
||||
"penalties": {
|
||||
"ev_soc_miss": 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
# Assure we have charge rates for the EV
|
||||
"devices": {
|
||||
"max_electric_vehicles": 1,
|
||||
"electric_vehicles": [
|
||||
{
|
||||
"device_id": "Default EV",
|
||||
"charge_rates": [
|
||||
0.0,
|
||||
6.0 / 16.0,
|
||||
8.0 / 16.0,
|
||||
10.0 / 16.0,
|
||||
12.0 / 16.0,
|
||||
14.0 / 16.0,
|
||||
1.0,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
# PV Forecast (in W)
|
||||
pv_forecast = np.zeros(48)
|
||||
pv_forecast[12] = 5000
|
||||
|
||||
# Temperature Forecast (in degree C)
|
||||
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,
|
||||
]
|
||||
|
||||
# Electricity price (per Wh)
|
||||
electricity_price_per_wh = np.full(48, 0.001)
|
||||
electricity_price_per_wh[0:10] = 0.00001
|
||||
electricity_price_per_wh[11:15] = 0.00005
|
||||
electricity_price_per_wh[20] = 0.00001
|
||||
|
||||
# Overall System Load (in W)
|
||||
total_load = [
|
||||
676.71,
|
||||
876.19,
|
||||
527.13,
|
||||
468.88,
|
||||
531.38,
|
||||
517.95,
|
||||
483.15,
|
||||
472.28,
|
||||
1011.68,
|
||||
995.00,
|
||||
1053.07,
|
||||
1063.91,
|
||||
1320.56,
|
||||
1132.03,
|
||||
1163.67,
|
||||
1176.82,
|
||||
1216.22,
|
||||
1103.78,
|
||||
1129.12,
|
||||
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,
|
||||
]
|
||||
|
||||
# Start Solution (binary)
|
||||
start_solution = None
|
||||
|
||||
# Define parameters for the optimization problem
|
||||
return Genetic0OptimizationParameters(
|
||||
**{
|
||||
"ems": {
|
||||
"price_per_wh_battery": 0e-05,
|
||||
"feed_in_tariff_per_wh": 7e-05,
|
||||
"total_load": total_load,
|
||||
"pv_forecast_wh": pv_forecast,
|
||||
"electricity_price_per_wh": electricity_price_per_wh,
|
||||
},
|
||||
"pv_battery": {
|
||||
"device_id": "battery 1",
|
||||
"capacity_wh": 26400,
|
||||
"initial_soc_percentage": 15,
|
||||
"min_soc_percentage": 15,
|
||||
},
|
||||
"inverter": {
|
||||
"device_id": "inverter 1",
|
||||
"max_power_wh": 10000,
|
||||
"battery_id": "battery 1",
|
||||
},
|
||||
"ev": {
|
||||
"device_id": "electric vehicle 1",
|
||||
"min_soc_percentage": 50,
|
||||
"capacity_wh": 60000,
|
||||
"charging_efficiency": 0.95,
|
||||
"max_charge_power_w": 11040,
|
||||
"initial_soc_percentage": 5,
|
||||
},
|
||||
"temperature_forecast": temperature_forecast,
|
||||
"start_solution": start_solution,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def run_optimization(
|
||||
real_world: bool, start_hour: int, verbose: bool, seed: int, parameters_file: str, ngen: int
|
||||
) -> Any:
|
||||
"""Run the optimization problem.
|
||||
|
||||
Args:
|
||||
start_hour (int, optional): Starting hour for optimization. Defaults to 0.
|
||||
verbose (bool, optional): Whether to print verbose output. Defaults to False.
|
||||
|
||||
Returns:
|
||||
dict: Optimization result as a dictionary
|
||||
"""
|
||||
# Prepare parameters
|
||||
if parameters_file:
|
||||
with open(parameters_file, "r") as f:
|
||||
parameters = Genetic0OptimizationParameters(**json.load(f))
|
||||
elif real_world:
|
||||
parameters = asyncio.run(prepare_optimization_real_parameters())
|
||||
else:
|
||||
parameters = prepare_optimization_parameters()
|
||||
logger.info("Optimization Parameters:")
|
||||
logger.info(parameters.model_dump_json(indent=4))
|
||||
|
||||
if start_hour is None:
|
||||
start_datetime = None
|
||||
else:
|
||||
start_datetime = to_datetime().set(hour=start_hour)
|
||||
|
||||
asyncio.run(
|
||||
ems_eos.run(
|
||||
start_datetime=start_datetime,
|
||||
mode=EnergyManagementMode.OPTIMIZATION,
|
||||
algorithm="GENETIC0",
|
||||
genetic0_parameters=parameters,
|
||||
genetic0_generations=ngen,
|
||||
genetic0_seed=seed,
|
||||
)
|
||||
)
|
||||
|
||||
solution = ems_eos.genetic0_solution()
|
||||
if solution is None:
|
||||
return None
|
||||
return solution.model_dump_json()
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function to run the optimization script with optional profiling."""
|
||||
parser = argparse.ArgumentParser(description="Run Energy Optimization Simulation")
|
||||
parser.add_argument("--profile", action="store_true", help="Enable performance profiling")
|
||||
parser.add_argument(
|
||||
"--verbose", action="store_true", help="Enable verbose output during optimization"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--real-world", action="store_true", help="Use real world data for predictions"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--start-hour", type=int, default=0, help="Starting hour for optimization (default: 0)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--parameters-file",
|
||||
type=str,
|
||||
default="",
|
||||
help="Load optimization parameters from json file (default: unset)",
|
||||
)
|
||||
parser.add_argument("--seed", type=int, default=42, help="Use fixed random seed (default: 42)")
|
||||
parser.add_argument(
|
||||
"--ngen",
|
||||
type=int,
|
||||
default=400,
|
||||
help="Number of generations during optimization process (default: 400)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.profile:
|
||||
# Run with profiling
|
||||
profiler = cProfile.Profile()
|
||||
try:
|
||||
result = profiler.runcall(
|
||||
run_optimization,
|
||||
real_world=args.real_world,
|
||||
start_hour=args.start_hour,
|
||||
verbose=args.verbose,
|
||||
seed=args.seed,
|
||||
parameters_file=args.parameters_file,
|
||||
ngen=args.ngen,
|
||||
)
|
||||
# Print profiling statistics
|
||||
stats = pstats.Stats(profiler)
|
||||
stats.strip_dirs().sort_stats("cumulative").print_stats(200)
|
||||
# Print result
|
||||
if args.verbose:
|
||||
print("\nOptimization Result:")
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error during optimization: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
else:
|
||||
# Run without profiling
|
||||
try:
|
||||
start_time = time.time()
|
||||
result = run_optimization(
|
||||
real_world=args.real_world,
|
||||
start_hour=args.start_hour,
|
||||
verbose=args.verbose,
|
||||
seed=args.seed,
|
||||
parameters_file=args.parameters_file,
|
||||
ngen=args.ngen,
|
||||
)
|
||||
end_time = time.time()
|
||||
elapsed_time = end_time - start_time
|
||||
if args.verbose:
|
||||
print(f"\nElapsed time: {elapsed_time:.4f} seconds.")
|
||||
print("\nOptimization Result:")
|
||||
print(result)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error during optimization: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -215,10 +215,10 @@ def prepare_optimization_parameters() -> GeneticOptimizationParameters:
|
||||
{
|
||||
"prediction": {"hours": 48},
|
||||
"optimization": {
|
||||
"horizon_hours": 48,
|
||||
"interval": 3600,
|
||||
"algorithm": "GENETIC",
|
||||
"genetic": {
|
||||
"horizon_hours": 48,
|
||||
"interval_sec": 3600,
|
||||
"individuals": 300,
|
||||
"generations": 400,
|
||||
"seed": None,
|
||||
@@ -433,7 +433,7 @@ def run_optimization(
|
||||
start_datetime=start_datetime,
|
||||
mode=EnergyManagementMode.OPTIMIZATION,
|
||||
genetic_parameters=parameters,
|
||||
genetic_individuals=ngen,
|
||||
genetic_generations=ngen,
|
||||
genetic_seed=seed,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -110,38 +110,6 @@ class TestElecPriceFixed:
|
||||
provider.config.reset_settings()
|
||||
assert not provider.enabled()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_data_hourly_intervals(self, provider, config_eos):
|
||||
"""Test updating data with hourly intervals (3600s)."""
|
||||
# Set start datetime
|
||||
ems_eos = get_ems()
|
||||
start_dt = to_datetime("2024-01-01 00:00:00", in_timezone="Europe/Berlin")
|
||||
ems_eos.set_start_datetime(start_dt)
|
||||
|
||||
# Configure hourly intervals
|
||||
config_eos.optimization.interval = 3600
|
||||
config_eos.prediction.hours = 24
|
||||
|
||||
# Update data
|
||||
await provider.update_data(force_enable=True, force_update=True)
|
||||
|
||||
# Verify data was generated
|
||||
assert len(provider) == 24 # 24 hours * 1 interval per hour
|
||||
|
||||
# Check prices
|
||||
records = provider.records
|
||||
|
||||
# First 8 hours should be night rate (0.288 kWh = 0.000288 Wh)
|
||||
for i in range(8):
|
||||
assert abs(records[i].elecprice_marketprice_wh - 0.000288) < 1e-6
|
||||
# Verify timestamps are on hour boundaries
|
||||
assert records[i].date_time.minute == 0
|
||||
assert records[i].date_time.second == 0
|
||||
|
||||
# Next 16 hours should be day rate (0.34 kWh = 0.00034 Wh)
|
||||
for i in range(8, 24):
|
||||
assert abs(records[i].elecprice_marketprice_wh - 0.00034) < 1e-6
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_data_15min_intervals(self, provider, config_eos):
|
||||
"""Test updating data with 15-minute intervals (900s)."""
|
||||
@@ -149,7 +117,6 @@ class TestElecPriceFixed:
|
||||
start_dt = to_datetime("2024-01-01 00:00:00", in_timezone="Europe/Berlin")
|
||||
ems_eos.set_start_datetime(start_dt)
|
||||
|
||||
config_eos.optimization.interval = 900
|
||||
config_eos.prediction.hours = 10 # spans both windows: 00:00–10:00 = 40 intervals
|
||||
|
||||
await provider.update_data(force_enable=True, force_update=True)
|
||||
@@ -176,40 +143,6 @@ class TestElecPriceFixed:
|
||||
f"Expected day rate at interval {i}, got {records[i].elecprice_marketprice_wh}"
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_data_30min_intervals(self, provider, config_eos):
|
||||
"""Test updating data with 30-minute intervals (1800s)."""
|
||||
ems_eos = get_ems()
|
||||
start_dt = to_datetime("2024-01-01 00:00:00", in_timezone="Europe/Berlin")
|
||||
ems_eos.set_start_datetime(start_dt)
|
||||
|
||||
config_eos.optimization.interval = 1800
|
||||
config_eos.prediction.hours = 10 # spans both windows: 00:00–10:00 = 20 intervals
|
||||
|
||||
await provider.update_data(force_enable=True, force_update=True)
|
||||
|
||||
# 10 hours * 2 intervals per hour = 20 intervals
|
||||
assert len(provider) == 20
|
||||
|
||||
records = provider.records
|
||||
|
||||
# Check timestamps are on 30-minute boundaries
|
||||
for record in records:
|
||||
assert record.date_time.minute in (0, 30)
|
||||
assert record.date_time.second == 0
|
||||
|
||||
# First 16 intervals: 00:00–08:00, night rate (8h * 2 = 16)
|
||||
for i in range(16):
|
||||
assert abs(records[i].elecprice_marketprice_wh - 0.000288) < 1e-6, (
|
||||
f"Expected night rate at interval {i}, got {records[i].elecprice_marketprice_wh}"
|
||||
)
|
||||
|
||||
# Remaining 4 intervals: 08:00–10:00, day rate (2h * 2 = 4)
|
||||
for i in range(16, 20):
|
||||
assert abs(records[i].elecprice_marketprice_wh - 0.00034) < 1e-6, (
|
||||
f"Expected day rate at interval {i}, got {records[i].elecprice_marketprice_wh}"
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_data_without_config(self, provider, config_eos):
|
||||
"""Test update_data fails without configuration."""
|
||||
@@ -232,12 +165,11 @@ class TestElecPriceFixed:
|
||||
@pytest.mark.asyncio
|
||||
async def test_key_to_array_resampling(self, provider, config_eos):
|
||||
"""Test that key_to_array can resample to different intervals."""
|
||||
# Setup provider with hourly data
|
||||
# Provider provides 15-minutes data
|
||||
ems_eos = get_ems()
|
||||
start_dt = to_datetime("2024-01-01 00:00:00", in_timezone="Europe/Berlin")
|
||||
ems_eos.set_start_datetime(start_dt)
|
||||
|
||||
config_eos.optimization.interval = 3600
|
||||
config_eos.prediction.hours = 24
|
||||
|
||||
await provider.update_data(force_enable=True, force_update=True)
|
||||
@@ -315,7 +247,6 @@ class TestElecPriceFixedIntegration:
|
||||
|
||||
config_eos.elecprice.elecpricefixed = ElecPriceFixedCommonSettings(time_windows=time_windows)
|
||||
config_eos.prediction.hours = 168 # 7 days
|
||||
config_eos.optimization.interval = 900 # 15 minutes
|
||||
|
||||
# Update data
|
||||
await provider.update_data(force_enable=True, force_update=True)
|
||||
|
||||
@@ -0,0 +1,299 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from akkudoktoreos.devices.genetic0.genetic0battery import (
|
||||
Genetic0Battery,
|
||||
Genetic0SolarPanelBatteryParameters,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def setup_pv_battery():
|
||||
device_id="battery1"
|
||||
capacity_wh=10000
|
||||
initial_soc_percentage=50
|
||||
charging_efficiency=0.88
|
||||
discharging_efficiency=0.88
|
||||
min_soc_percentage=20
|
||||
max_soc_percentage=80
|
||||
max_charge_power_w=8000
|
||||
hours=24
|
||||
|
||||
params = Genetic0SolarPanelBatteryParameters(
|
||||
device_id=device_id,
|
||||
capacity_wh=capacity_wh,
|
||||
initial_soc_percentage=initial_soc_percentage,
|
||||
charging_efficiency=charging_efficiency,
|
||||
discharging_efficiency=discharging_efficiency,
|
||||
min_soc_percentage=min_soc_percentage,
|
||||
max_soc_percentage=max_soc_percentage,
|
||||
max_charge_power_w=max_charge_power_w,
|
||||
hours=hours,
|
||||
)
|
||||
battery = Genetic0Battery(
|
||||
params,
|
||||
prediction_hours=48,
|
||||
)
|
||||
battery.reset()
|
||||
|
||||
assert battery.parameters.device_id==device_id
|
||||
assert battery.capacity_wh==capacity_wh
|
||||
assert battery.initial_soc_percentage==initial_soc_percentage
|
||||
assert battery.charging_efficiency==charging_efficiency
|
||||
assert battery.initial_soc_percentage==initial_soc_percentage
|
||||
assert battery.discharging_efficiency==discharging_efficiency
|
||||
assert battery.max_soc_percentage==max_soc_percentage
|
||||
assert battery.max_charge_power_w==max_charge_power_w
|
||||
assert battery.soc_wh==float((initial_soc_percentage / 100) * capacity_wh)
|
||||
assert battery.min_soc_wh==float((min_soc_percentage / 100) * capacity_wh)
|
||||
assert battery.max_soc_wh==float((max_soc_percentage / 100) * capacity_wh)
|
||||
assert np.all(battery.charge_array == 0)
|
||||
assert np.all(battery.discharge_array == 0)
|
||||
|
||||
# Init for test
|
||||
battery.charge_array = np.full(battery.prediction_hours, 1)
|
||||
battery.discharge_array = np.full(battery.prediction_hours, 1)
|
||||
assert np.all(battery.charge_array == 1)
|
||||
assert np.all(battery.discharge_array == 1)
|
||||
|
||||
return battery
|
||||
|
||||
|
||||
def test_initial_state_of_charge(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
assert battery.current_soc_percentage() == 50.0, "Initial SoC should be 50%"
|
||||
|
||||
|
||||
def test_battery_discharge_below_min_soc(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
discharged_wh, loss_wh = battery.discharge_energy(5000, 0)
|
||||
|
||||
# Ensure it discharges energy and stops at the min SOC
|
||||
assert discharged_wh > 0
|
||||
print(discharged_wh, loss_wh, battery.current_soc_percentage(), battery.min_soc_percentage)
|
||||
assert battery.current_soc_percentage() >= 20 # Ensure it's above min_soc_percentage
|
||||
assert loss_wh >= 0 # Losses should not be negative
|
||||
assert discharged_wh == 2640.0, "The energy discharged should be limited by min_soc"
|
||||
|
||||
|
||||
def test_battery_charge_above_max_soc(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
charged_wh, loss_wh = battery.charge_energy(5000, 0)
|
||||
|
||||
# Ensure it charges energy and stops at the max SOC
|
||||
assert charged_wh > 0
|
||||
assert battery.current_soc_percentage() <= 80 # Ensure it's below max_soc_percentage
|
||||
assert loss_wh >= 0 # Losses should not be negative
|
||||
assert charged_wh == 3000.0, "The energy charged should be limited by max_soc"
|
||||
|
||||
|
||||
def test_battery_charge_when_full(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
battery.soc_wh = battery.max_soc_wh # Set battery to full
|
||||
charged_wh, loss_wh = battery.charge_energy(5000, 0)
|
||||
|
||||
# No charging should happen if battery is full
|
||||
assert charged_wh == 0
|
||||
assert loss_wh == 0
|
||||
assert battery.current_soc_percentage() == 80, "SoC should remain at max_soc"
|
||||
|
||||
|
||||
def test_battery_discharge_when_empty(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
battery.soc_wh = battery.min_soc_wh # Set battery to minimum SOC
|
||||
discharged_wh, loss_wh = battery.discharge_energy(5000, 0)
|
||||
|
||||
# No discharge should happen if battery is at min SOC
|
||||
assert discharged_wh == 0
|
||||
assert loss_wh == 0
|
||||
assert battery.current_soc_percentage() == 20, "SoC should remain at min_soc"
|
||||
|
||||
|
||||
def test_battery_discharge_exactly_min_soc(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
battery.soc_wh = battery.min_soc_wh # Set battery to exactly min SOC
|
||||
discharged_wh, loss_wh = battery.discharge_energy(1000, 0)
|
||||
|
||||
# Battery should not go below the min SOC
|
||||
assert discharged_wh == 0
|
||||
assert battery.current_soc_percentage() == 20 # SOC should remain at min_SOC
|
||||
|
||||
|
||||
def test_battery_charge_exactly_max_soc(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
battery.soc_wh = battery.max_soc_wh # Set battery to exactly max SOC
|
||||
charged_wh, loss_wh = battery.charge_energy(1000, 0)
|
||||
|
||||
# Battery should not exceed the max SOC
|
||||
assert charged_wh == 0
|
||||
assert battery.current_soc_percentage() == 80 # SOC should remain at max_SOC
|
||||
|
||||
|
||||
def test_battery_reset_function(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
battery.soc_wh = 8000 # Change the SOC to some value
|
||||
battery.reset()
|
||||
|
||||
# After reset, SOC should be equal to the initial value
|
||||
assert battery.current_soc_percentage() == battery.initial_soc_percentage
|
||||
|
||||
|
||||
def test_soc_limits(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
|
||||
# Manually set SoC above max limit
|
||||
battery.soc_wh = battery.max_soc_wh + 1000
|
||||
battery.soc_wh = min(battery.soc_wh, battery.max_soc_wh)
|
||||
assert battery.current_soc_percentage() <= 80, "SoC should not exceed max_soc"
|
||||
|
||||
# Manually set SoC below min limit
|
||||
battery.soc_wh = battery.min_soc_wh - 1000
|
||||
battery.soc_wh = max(battery.soc_wh, battery.min_soc_wh)
|
||||
assert battery.current_soc_percentage() >= 20, "SoC should not drop below min_soc"
|
||||
|
||||
|
||||
def test_max_charge_power_w(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
assert battery.parameters.max_charge_power_w == 8000, (
|
||||
"Default max charge power should be 5000W, We ask for 8000W here"
|
||||
)
|
||||
|
||||
|
||||
def test_charge_energy_within_limits(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
initial_soc_wh = battery.soc_wh
|
||||
|
||||
charged_wh, losses_wh = battery.charge_energy(wh=4000, hour=1)
|
||||
|
||||
assert charged_wh > 0, "Charging should add energy"
|
||||
assert losses_wh >= 0, "Losses should not be negative"
|
||||
assert battery.soc_wh > initial_soc_wh, "State of charge should increase after charging"
|
||||
assert battery.soc_wh <= battery.max_soc_wh, "SOC should not exceed max SOC"
|
||||
|
||||
|
||||
def test_charge_energy_exceeds_capacity(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
initial_soc_wh = battery.soc_wh
|
||||
|
||||
# Try to overcharge beyond max capacity
|
||||
charged_wh, losses_wh = battery.charge_energy(wh=20000, hour=2)
|
||||
|
||||
assert charged_wh + initial_soc_wh <= battery.max_soc_wh, (
|
||||
"Charging should not exceed max capacity"
|
||||
)
|
||||
assert losses_wh >= 0, "Losses should not be negative"
|
||||
assert battery.soc_wh == battery.max_soc_wh, "SOC should be at max after overcharge attempt"
|
||||
|
||||
|
||||
def test_charge_energy_not_allowed_hour(setup_pv_battery):
|
||||
battery = setup_pv_battery
|
||||
|
||||
# Disable charging for all hours
|
||||
battery.set_charge_per_hour(np.zeros(battery.prediction_hours))
|
||||
|
||||
charged_wh, losses_wh = battery.charge_energy(wh=4000, hour=3)
|
||||
|
||||
assert charged_wh == 0, "No energy should be charged in disallowed hours"
|
||||
assert losses_wh == 0, "No losses should occur if charging is not allowed"
|
||||
assert (
|
||||
battery.soc_wh == (battery.parameters.initial_soc_percentage / 100) * battery.capacity_wh
|
||||
), "SOC should remain unchanged"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"wh, charge_factor, expected_raises",
|
||||
[
|
||||
(None, 0.5, False), # Expected to work normally (if capacity allows)
|
||||
(None, 1.0, False), # Often still OK, depending on fixture capacity
|
||||
(None, 2.0, False), # Exceeds max charge → always ValueError
|
||||
(1000, 0, False),
|
||||
(1000, 1.0, True),
|
||||
],
|
||||
)
|
||||
def test_charge_energy_with_charge_factor(setup_pv_battery, wh, charge_factor, expected_raises):
|
||||
battery = setup_pv_battery
|
||||
hour = 4
|
||||
|
||||
if wh is not None and charge_factor == 0.0: # mode 1
|
||||
raw_request_wh = wh
|
||||
else:
|
||||
raw_request_wh = battery.max_charge_power_w * charge_factor
|
||||
raw_capacity_wh = max(battery.max_soc_wh - battery.soc_wh, 0.0)
|
||||
|
||||
if expected_raises:
|
||||
# Should raise
|
||||
with pytest.raises(ValueError):
|
||||
battery.charge_energy(
|
||||
wh=wh,
|
||||
hour=hour,
|
||||
charge_factor=charge_factor,
|
||||
)
|
||||
return
|
||||
|
||||
# Should NOT raise
|
||||
charged_wh, losses_wh = battery.charge_energy(
|
||||
wh=wh,
|
||||
hour=hour,
|
||||
charge_factor=charge_factor,
|
||||
)
|
||||
|
||||
# Expectations
|
||||
assert charged_wh > 0, "Charging should occur with charge factor"
|
||||
assert losses_wh >= 0, "Losses must not be negative"
|
||||
assert charged_wh <= raw_request_wh, "Charging must not exceed request"
|
||||
assert battery.soc_wh > 0, "SOC should increase after charging"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def setup_car_battery():
|
||||
from akkudoktoreos.optimization.genetic0.genetic0params import (
|
||||
Genetic0ElectricVehicleParameters,
|
||||
)
|
||||
|
||||
params = Genetic0ElectricVehicleParameters(
|
||||
device_id="ev1",
|
||||
capacity_wh=40000,
|
||||
initial_soc_percentage=60,
|
||||
min_soc_percentage=10,
|
||||
max_soc_percentage=90,
|
||||
max_charge_power_w=7000,
|
||||
hours=24,
|
||||
)
|
||||
battery = Genetic0Battery(
|
||||
params,
|
||||
prediction_hours=48,
|
||||
)
|
||||
battery.reset()
|
||||
|
||||
# Init for test
|
||||
battery.charge_array = np.full(battery.prediction_hours, 1)
|
||||
battery.discharge_array = np.full(battery.prediction_hours, 1)
|
||||
assert np.all(battery.charge_array == 1)
|
||||
assert np.all(battery.discharge_array == 1)
|
||||
|
||||
return battery
|
||||
|
||||
|
||||
def test_car_and_pv_battery_discharge_and_max_charge_power(setup_pv_battery, setup_car_battery):
|
||||
pv_battery = setup_pv_battery
|
||||
car_battery = setup_car_battery
|
||||
|
||||
# Test discharge for PV battery
|
||||
pv_discharged_wh, pv_loss_wh = pv_battery.discharge_energy(3000, 5)
|
||||
assert pv_discharged_wh > 0, "PV battery should discharge energy"
|
||||
assert pv_battery.current_soc_percentage() >= pv_battery.parameters.min_soc_percentage, (
|
||||
"PV battery SOC should stay above min SOC"
|
||||
)
|
||||
assert pv_battery.parameters.max_charge_power_w == 8000, (
|
||||
"PV battery max charge power should remain as defined"
|
||||
)
|
||||
|
||||
# Test discharge for car battery
|
||||
car_discharged_wh, car_loss_wh = car_battery.discharge_energy(5000, 10)
|
||||
assert car_discharged_wh > 0, "Car battery should discharge energy"
|
||||
assert car_battery.current_soc_percentage() >= car_battery.parameters.min_soc_percentage, (
|
||||
"Car battery SOC should stay above min SOC"
|
||||
)
|
||||
assert car_battery.parameters.max_charge_power_w == 7000, (
|
||||
"Car battery max charge power should remain as defined"
|
||||
)
|
||||
@@ -0,0 +1,343 @@
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from akkudoktoreos.devices.genetic0.genetic0inverter import (
|
||||
Genetic0Inverter,
|
||||
Genetic0InverterParameters,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_battery() -> Mock:
|
||||
mock_battery = Mock()
|
||||
mock_battery.charge_energy = Mock(return_value=(0.0, 0.0))
|
||||
mock_battery.discharge_energy = Mock(return_value=(0.0, 0.0))
|
||||
mock_battery.parameters.device_id = "battery1"
|
||||
return mock_battery
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def inverter(mock_battery) -> Genetic0Inverter:
|
||||
mock_self_consumption_predictor = Mock()
|
||||
mock_self_consumption_predictor.calculate_self_consumption.return_value = 1.0
|
||||
with patch(
|
||||
"akkudoktoreos.devices.genetic0.genetic0inverter.get_genetic0_load_interpolator",
|
||||
return_value=mock_self_consumption_predictor,
|
||||
):
|
||||
iv = Genetic0Inverter(
|
||||
Genetic0InverterParameters(
|
||||
device_id="iv1", max_power_wh=500.0, battery_id=mock_battery.parameters.device_id
|
||||
),
|
||||
battery = mock_battery
|
||||
)
|
||||
return iv
|
||||
|
||||
|
||||
def test_process_energy_excess_generation(inverter, mock_battery):
|
||||
# Battery charges 100 Wh with 10 Wh loss
|
||||
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
||||
generation = 600.0
|
||||
consumption = 200.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == pytest.approx(290.0, rel=1e-2) # 290 Wh feed-in after battery charges
|
||||
assert grid_import == 0.0 # No grid draw
|
||||
assert losses == 10.0 # Battery charging losses
|
||||
assert self_consumption == 200.0 # All consumption is met
|
||||
mock_battery.charge_energy.assert_called_once_with(400.0, hour)
|
||||
mock_battery.discharge_energy.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
||||
consumption, generation
|
||||
)
|
||||
|
||||
|
||||
def test_process_energy_excess_generation_interpolator(inverter, mock_battery):
|
||||
# Battery charges 100 Wh with 10 Wh loss
|
||||
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
||||
mock_battery.discharge_energy.return_value = (20.0, 2.0)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.return_value = 0.95
|
||||
|
||||
generation = 600.0
|
||||
consumption = 200.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == pytest.approx(
|
||||
270.0, rel=1e-2
|
||||
) # 290 Wh feed-in - 5% of generation-consumption self consumption after battery charges
|
||||
assert grid_import == pytest.approx(0.0, rel=1e-2) # No grid draw
|
||||
assert losses == 12.0 # Battery charging losses
|
||||
assert self_consumption == 220.0 # All consumption is met
|
||||
mock_battery.charge_energy.assert_called_once_with(pytest.approx(380.0, rel=1e-2), hour)
|
||||
mock_battery.discharge_energy.assert_called_once_with(pytest.approx(20.0, rel=1e-2), hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
||||
consumption, generation
|
||||
)
|
||||
|
||||
|
||||
def test_process_energy_generation_equals_consumption(inverter, mock_battery):
|
||||
generation = 300.0
|
||||
consumption = 300.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == 0.0 # No feed-in as generation equals consumption
|
||||
assert grid_import == 0.0 # No grid draw
|
||||
assert losses == 0.0 # No losses
|
||||
assert self_consumption == 300.0 # All consumption is met with generation
|
||||
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
||||
consumption, generation
|
||||
)
|
||||
|
||||
|
||||
def test_process_energy_battery_discharges(inverter, mock_battery):
|
||||
# Battery discharges 100 Wh with 10 Wh loss already accounted for in the discharge
|
||||
mock_battery.discharge_energy.return_value = (100.0, 10.0)
|
||||
generation = 100.0
|
||||
consumption = 250.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == 0.0 # No feed-in as generation is insufficient
|
||||
assert grid_import == pytest.approx(
|
||||
50.0, rel=1e-2
|
||||
) # Grid supplies remaining shortfall after battery discharge
|
||||
assert losses == 10.0 # Discharge losses
|
||||
assert self_consumption == 200.0 # Generation + battery discharge
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(150.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
|
||||
|
||||
def test_process_energy_battery_empty(inverter, mock_battery):
|
||||
# Battery is empty, so no energy can be discharged
|
||||
mock_battery.discharge_energy.return_value = (0.0, 0.0)
|
||||
generation = 100.0
|
||||
consumption = 300.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == 0.0 # No feed-in as generation is insufficient
|
||||
assert grid_import == pytest.approx(200.0, rel=1e-2) # Grid has to cover the full shortfall
|
||||
assert losses == 0.0 # No losses as the battery didn't discharge
|
||||
assert self_consumption == 100.0 # Only generation is consumed
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(200.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
|
||||
|
||||
def test_process_energy_battery_full_at_start(inverter, mock_battery):
|
||||
# Battery is full, so no charging happens
|
||||
mock_battery.charge_energy.return_value = (0.0, 0.0)
|
||||
generation = 500.0
|
||||
consumption = 200.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == pytest.approx(
|
||||
300.0, rel=1e-2
|
||||
) # All excess energy should be fed into the grid
|
||||
assert grid_import == 0.0 # No grid draw
|
||||
assert losses == 0.0 # No losses
|
||||
assert self_consumption == 200.0 # Only consumption is met
|
||||
mock_battery.charge_energy.assert_called_once_with(300.0, hour)
|
||||
mock_battery.discharge_energy.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
||||
consumption, generation
|
||||
)
|
||||
|
||||
|
||||
def test_process_energy_insufficient_generation_no_battery(inverter, mock_battery):
|
||||
# Insufficient generation and no battery discharge
|
||||
mock_battery.discharge_energy.return_value = (0.0, 0.0)
|
||||
generation = 100.0
|
||||
consumption = 500.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == 0.0 # No feed-in as generation is insufficient
|
||||
assert grid_import == pytest.approx(400.0, rel=1e-2) # Grid supplies the shortfall
|
||||
assert losses == 0.0 # No losses
|
||||
assert self_consumption == 100.0 # Only generation is consumed
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(400.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
|
||||
|
||||
def test_process_energy_insufficient_generation_battery_assists(inverter, mock_battery):
|
||||
# Battery assists with some discharge to cover the shortfall
|
||||
mock_battery.discharge_energy.return_value = (
|
||||
50.0,
|
||||
5.0,
|
||||
) # Battery discharges 50 Wh with 5 Wh loss
|
||||
generation = 200.0
|
||||
consumption = 400.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == 0.0 # No feed-in as generation is insufficient
|
||||
assert grid_import == pytest.approx(
|
||||
150.0, rel=1e-2
|
||||
) # Grid supplies the remaining shortfall after battery discharge
|
||||
assert losses == 5.0 # Discharge losses
|
||||
assert self_consumption == 250.0 # Generation + battery discharge
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(200.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
|
||||
|
||||
def test_process_energy_zero_generation(inverter, mock_battery):
|
||||
# Zero generation, full reliance on battery and grid
|
||||
mock_battery.discharge_energy.return_value = (
|
||||
100.0,
|
||||
5.0,
|
||||
) # Battery discharges 100 Wh with 5 Wh loss
|
||||
generation = 0.0
|
||||
consumption = 300.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == 0.0 # No feed-in as there is zero generation
|
||||
assert grid_import == pytest.approx(200.0, rel=1e-2) # Grid supplies the remaining shortfall
|
||||
assert losses == 5.0 # Discharge losses
|
||||
assert self_consumption == 100.0 # Only battery discharge is consumed
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(300.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
|
||||
|
||||
def test_process_energy_zero_consumption(inverter, mock_battery):
|
||||
# Generation exceeds consumption, but consumption is zero
|
||||
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
||||
generation = 500.0
|
||||
consumption = 0.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == pytest.approx(390.0, rel=1e-2) # Excess energy after battery charges
|
||||
assert grid_import == 0.0 # No grid draw as no consumption
|
||||
assert losses == 10.0 # Charging losses
|
||||
assert self_consumption == 0.0 # Zero consumption
|
||||
mock_battery.charge_energy.assert_called_once_with(500.0, hour)
|
||||
mock_battery.discharge_energy.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
||||
consumption, generation
|
||||
)
|
||||
|
||||
|
||||
def test_process_energy_zero_generation_zero_consumption(inverter, mock_battery):
|
||||
generation = 0.0
|
||||
consumption = 0.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == 0.0 # No feed-in
|
||||
assert grid_import == 0.0 # No grid draw
|
||||
assert losses == 0.0 # No losses
|
||||
assert self_consumption == 0.0 # No consumption
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_not_called()
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_called_once_with(
|
||||
consumption, generation
|
||||
)
|
||||
|
||||
|
||||
def test_process_energy_partial_battery_discharge(inverter, mock_battery):
|
||||
mock_battery.discharge_energy.return_value = (50.0, 5.0)
|
||||
generation = 200.0
|
||||
consumption = 400.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == 0.0 # No feed-in due to insufficient generation
|
||||
assert grid_import == pytest.approx(
|
||||
150.0, rel=1e-2
|
||||
) # Grid supplies the shortfall after battery assist
|
||||
assert losses == 5.0 # Discharge losses
|
||||
assert self_consumption == 250.0 # Generation + battery discharge
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(200.0, 12)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
|
||||
|
||||
def test_process_energy_consumption_exceeds_max_no_battery(inverter, mock_battery):
|
||||
# Battery is empty, and consumption is much higher than the inverter's max power
|
||||
mock_battery.discharge_energy.return_value = (0.0, 0.0)
|
||||
generation = 100.0
|
||||
consumption = 1000.0 # Exceeds the inverter's max power
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == 0.0 # No feed-in
|
||||
assert grid_import == pytest.approx(900.0, rel=1e-2) # Grid covers the remaining shortfall
|
||||
assert losses == 0.0 # No losses as the battery didn’t assist
|
||||
assert self_consumption == 100.0 # Only the generation is consumed, maxing out the inverter
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(400.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
|
||||
|
||||
def test_process_energy_zero_generation_full_battery_high_consumption(inverter, mock_battery):
|
||||
# Full battery, no generation, and high consumption
|
||||
mock_battery.discharge_energy.return_value = (500.0, 10.0)
|
||||
generation = 0.0
|
||||
consumption = 600.0
|
||||
hour = 12
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inverter.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
assert grid_export == 0.0 # No feed-in due to zero generation
|
||||
assert grid_import == pytest.approx(
|
||||
100.0, rel=1e-2
|
||||
) # Grid covers remaining shortfall after battery discharge
|
||||
assert losses == 10.0 # Battery discharge losses
|
||||
assert self_consumption == 500.0 # Battery fully discharges to meet consumption
|
||||
mock_battery.charge_energy.assert_not_called()
|
||||
mock_battery.discharge_energy.assert_called_once_with(500.0, hour)
|
||||
inverter.self_consumption_predictor.calculate_self_consumption.assert_not_called()
|
||||
@@ -0,0 +1,886 @@
|
||||
"""Tests for inverter AC/DC efficiency separation and AC charging break-even penalty.
|
||||
|
||||
Tests the new inverter parameters:
|
||||
- dc_to_ac_efficiency: DC→AC conversion loss on battery discharge
|
||||
- ac_to_dc_efficiency: AC→DC conversion loss on grid-to-battery charging
|
||||
- max_ac_charge_power_w: Maximum AC charging power limit
|
||||
|
||||
And the economic break-even penalty in GeneticOptimization.evaluate():
|
||||
- Penalises AC grid charging that cannot be recovered given round-trip losses and future prices
|
||||
- Respects free PV-charged energy already in battery when ranking future discharge hours
|
||||
"""
|
||||
|
||||
from types import SimpleNamespace
|
||||
from typing import cast
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from akkudoktoreos.devices.genetic0.genetic0battery import Genetic0Battery
|
||||
from akkudoktoreos.devices.genetic0.genetic0inverter import Genetic0Inverter
|
||||
from akkudoktoreos.optimization.genetic0.genetic0 import (
|
||||
Genetic0Simulation,
|
||||
)
|
||||
from akkudoktoreos.optimization.genetic0.genetic0devices import (
|
||||
Genetic0InverterParameters,
|
||||
Genetic0SolarPanelBatteryParameters,
|
||||
)
|
||||
from akkudoktoreos.optimization.genetic0.genetic0params import (
|
||||
Genetic0EnergyManagementParameters,
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers / Fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _make_inverter(
|
||||
dc_to_ac_efficiency: float = 1.0,
|
||||
ac_to_dc_efficiency: float = 1.0,
|
||||
max_ac_charge_power_w=None,
|
||||
max_power_wh: float = 10000.0,
|
||||
mock_battery=None,
|
||||
) -> Genetic0Inverter:
|
||||
"""Create an Inverter with custom efficiency parameters and a mock battery."""
|
||||
mock_self_consumption_predictor = Mock()
|
||||
mock_self_consumption_predictor.calculate_self_consumption.return_value = 1.0
|
||||
|
||||
params = Genetic0InverterParameters(
|
||||
device_id="inv1",
|
||||
max_power_wh=max_power_wh,
|
||||
battery_id=mock_battery.parameters.device_id if mock_battery else None,
|
||||
dc_to_ac_efficiency=dc_to_ac_efficiency,
|
||||
ac_to_dc_efficiency=ac_to_dc_efficiency,
|
||||
max_ac_charge_power_w=max_ac_charge_power_w,
|
||||
)
|
||||
with patch(
|
||||
"akkudoktoreos.devices.genetic0.genetic0inverter.get_genetic0_load_interpolator",
|
||||
return_value=mock_self_consumption_predictor,
|
||||
):
|
||||
return Genetic0Inverter(params, battery=mock_battery)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_battery() -> Mock:
|
||||
mock_bat = Mock()
|
||||
mock_bat.charge_energy = Mock(return_value=(0.0, 0.0))
|
||||
mock_bat.discharge_energy = Mock(return_value=(0.0, 0.0))
|
||||
mock_bat.parameters.device_id = "battery1"
|
||||
return mock_bat
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# 1. InverterParameters – new fields and defaults
|
||||
# ===================================================================
|
||||
|
||||
|
||||
class TestInverterParametersDefaults:
|
||||
"""Verify backward-compatible defaults for new parameters."""
|
||||
|
||||
def test_defaults(self):
|
||||
params = Genetic0InverterParameters(device_id="inv1", max_power_wh=5000)
|
||||
assert params.dc_to_ac_efficiency == 1.0
|
||||
assert params.ac_to_dc_efficiency == 1.0
|
||||
assert params.max_ac_charge_power_w is None
|
||||
|
||||
def test_custom_values(self):
|
||||
params = Genetic0InverterParameters(
|
||||
device_id="inv1",
|
||||
max_power_wh=5000,
|
||||
dc_to_ac_efficiency=0.95,
|
||||
ac_to_dc_efficiency=0.93,
|
||||
max_ac_charge_power_w=3000,
|
||||
)
|
||||
assert params.dc_to_ac_efficiency == 0.95
|
||||
assert params.ac_to_dc_efficiency == 0.93
|
||||
assert params.max_ac_charge_power_w == 3000
|
||||
|
||||
def test_ac_to_dc_zero_disables_ac_charging(self):
|
||||
params = Genetic0InverterParameters(
|
||||
device_id="inv1", max_power_wh=5000, ac_to_dc_efficiency=0.0
|
||||
)
|
||||
assert params.ac_to_dc_efficiency == 0.0
|
||||
|
||||
def test_dc_to_ac_must_be_positive(self):
|
||||
with pytest.raises(Exception):
|
||||
Genetic0InverterParameters(device_id="inv1", max_power_wh=5000, dc_to_ac_efficiency=0.0)
|
||||
|
||||
def test_max_ac_charge_power_zero(self):
|
||||
params = Genetic0InverterParameters(
|
||||
device_id="inv1", max_power_wh=5000, max_ac_charge_power_w=0
|
||||
)
|
||||
assert params.max_ac_charge_power_w == 0
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# 2. dc_to_ac_efficiency – battery discharge through inverter
|
||||
# ===================================================================
|
||||
|
||||
|
||||
class TestDcToAcEfficiency:
|
||||
"""Battery discharge energy is reduced by dc_to_ac_efficiency."""
|
||||
|
||||
def test_discharge_shortfall_with_95_percent_efficiency(self, mock_battery):
|
||||
"""With 0.95 efficiency, 100 Wh DC from battery → 95 Wh AC delivered."""
|
||||
mock_battery.discharge_energy.return_value = (100.0, 10.0)
|
||||
inv = _make_inverter(dc_to_ac_efficiency=0.95, mock_battery=mock_battery)
|
||||
|
||||
generation = 0.0
|
||||
consumption = 200.0
|
||||
hour = 5
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inv.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
# Battery delivers 100 Wh DC → 95 Wh AC after inverter
|
||||
# Inverter loss = 100 - 95 = 5 Wh
|
||||
battery_discharge_ac = 100.0 * 0.95 # 95 Wh
|
||||
assert self_consumption == pytest.approx(generation + battery_discharge_ac, rel=1e-5)
|
||||
assert grid_import == pytest.approx(consumption - battery_discharge_ac, rel=1e-5)
|
||||
|
||||
# Total losses = battery internal (10) + inverter DC→AC (5)
|
||||
expected_losses = 10.0 + (100.0 * 0.05)
|
||||
assert losses == pytest.approx(expected_losses, rel=1e-5)
|
||||
|
||||
# Battery was asked for more DC to compensate for inverter loss
|
||||
# ac_needed = min(200, max_power_wh - 0) = 200
|
||||
# dc_request = 200 / 0.95 ≈ 210.526
|
||||
expected_dc_request = 200.0 / 0.95
|
||||
mock_battery.discharge_energy.assert_called_once_with(
|
||||
pytest.approx(expected_dc_request, rel=1e-3), hour
|
||||
)
|
||||
|
||||
def test_discharge_with_100_percent_efficiency_unchanged(self, mock_battery):
|
||||
"""With 1.0 efficiency, behavior is identical to the legacy model."""
|
||||
mock_battery.discharge_energy.return_value = (100.0, 10.0)
|
||||
inv = _make_inverter(dc_to_ac_efficiency=1.0, mock_battery=mock_battery)
|
||||
|
||||
generation = 100.0
|
||||
consumption = 300.0
|
||||
hour = 5
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inv.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
# No inverter loss: battery_discharge_ac = 100 Wh
|
||||
assert self_consumption == pytest.approx(200.0, rel=1e-5)
|
||||
assert grid_import == pytest.approx(100.0, rel=1e-5)
|
||||
assert losses == pytest.approx(10.0, rel=1e-5) # Only battery losses
|
||||
|
||||
def test_discharge_surplus_path_with_efficiency(self, mock_battery):
|
||||
"""When generation > consumption but SCR < 1, discharge goes through inverter."""
|
||||
mock_battery.discharge_energy.return_value = (50.0, 5.0)
|
||||
mock_battery.charge_energy.return_value = (100.0, 10.0)
|
||||
|
||||
inv = _make_inverter(dc_to_ac_efficiency=0.90, mock_battery=mock_battery)
|
||||
cast(Mock, inv.self_consumption_predictor).calculate_self_consumption.return_value = 0.90
|
||||
|
||||
generation = 500.0
|
||||
consumption = 200.0
|
||||
hour = 5
|
||||
|
||||
grid_export, grid_import, losses, self_consumption = inv.process_energy(
|
||||
generation, consumption, hour
|
||||
)
|
||||
|
||||
# surplus = 300, remaining_power = 300*0.9 = 270, remaining_load_evq = 300*0.1 = 30
|
||||
# DC request for discharge = 30 / 0.90 = 33.333
|
||||
expected_dc_request = 30.0 / 0.90
|
||||
mock_battery.discharge_energy.assert_called_once_with(
|
||||
pytest.approx(expected_dc_request, rel=1e-3), hour
|
||||
)
|
||||
|
||||
# Battery delivers 50 Wh DC → 45 Wh AC
|
||||
from_battery_ac = 50.0 * 0.90 # 45 Wh
|
||||
inverter_discharge_loss = 50.0 - from_battery_ac # 5 Wh
|
||||
|
||||
assert self_consumption == pytest.approx(consumption + from_battery_ac, rel=1e-5)
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# 3. ac_to_dc_efficiency + max_ac_charge_power_w – in simulation
|
||||
# ===================================================================
|
||||
|
||||
|
||||
class TestAcChargingInSimulation:
|
||||
"""Test AC charging logic with inverter efficiency in GeneticSimulation.
|
||||
|
||||
These tests use a real Battery object (not a mock) and directly exercise
|
||||
the AC charging path in GeneticSimulation.simulate().
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def simulation_setup(self, config_eos):
|
||||
"""Set up a minimal GeneticSimulation with battery and inverter."""
|
||||
from akkudoktoreos.optimization.genetic0.genetic0 import Genetic0Simulation
|
||||
from akkudoktoreos.optimization.genetic0.genetic0params import (
|
||||
Genetic0EnergyManagementParameters,
|
||||
)
|
||||
|
||||
config_eos.merge_settings_from_dict(
|
||||
{"prediction": {"hours": 48}, "optimization": {"hours": 24}}
|
||||
)
|
||||
|
||||
prediction_hours = config_eos.prediction.hours
|
||||
|
||||
def _build(
|
||||
ac_to_dc_efficiency: float = 1.0,
|
||||
dc_to_ac_efficiency: float = 1.0,
|
||||
max_ac_charge_power_w=None,
|
||||
battery_capacity_wh: int = 10000,
|
||||
battery_charging_efficiency: float = 0.90,
|
||||
battery_discharging_efficiency: float = 0.90,
|
||||
battery_initial_soc_pct: int = 50,
|
||||
battery_max_charge_power_w: int = 5000,
|
||||
):
|
||||
akku = Genetic0Battery(
|
||||
Genetic0SolarPanelBatteryParameters(
|
||||
device_id="battery1",
|
||||
capacity_wh=battery_capacity_wh,
|
||||
initial_soc_percentage=battery_initial_soc_pct,
|
||||
charging_efficiency=battery_charging_efficiency,
|
||||
discharging_efficiency=battery_discharging_efficiency,
|
||||
min_soc_percentage=0,
|
||||
max_soc_percentage=100,
|
||||
max_charge_power_w=battery_max_charge_power_w,
|
||||
),
|
||||
prediction_hours=prediction_hours,
|
||||
)
|
||||
akku.reset()
|
||||
|
||||
inverter = Genetic0Inverter(
|
||||
Genetic0InverterParameters(
|
||||
device_id="inverter1",
|
||||
max_power_wh=10000,
|
||||
battery_id="battery1",
|
||||
ac_to_dc_efficiency=ac_to_dc_efficiency,
|
||||
dc_to_ac_efficiency=dc_to_ac_efficiency,
|
||||
max_ac_charge_power_w=max_ac_charge_power_w,
|
||||
),
|
||||
battery=akku,
|
||||
)
|
||||
|
||||
simulation = Genetic0Simulation()
|
||||
simulation.prepare(
|
||||
Genetic0EnergyManagementParameters(
|
||||
pv_prognose_wh=[0.0] * prediction_hours, # No PV
|
||||
strompreis_euro_pro_wh=[0.0003] * prediction_hours, # ~30ct/kWh
|
||||
einspeiseverguetung_euro_pro_wh=0.00008,
|
||||
preis_euro_pro_wh_akku=0.0001,
|
||||
gesamtlast=[1000.0] * prediction_hours, # 1 kW constant load
|
||||
),
|
||||
optimization_hours=config_eos.optimization.genetic0.horizon_hours,
|
||||
prediction_hours=prediction_hours,
|
||||
inverter=inverter,
|
||||
ev=None,
|
||||
home_appliance=None,
|
||||
)
|
||||
return simulation, akku, inverter
|
||||
|
||||
return _build
|
||||
|
||||
def test_ac_charge_with_unity_efficiency_backward_compat(self, simulation_setup):
|
||||
"""With ac_to_dc_efficiency=1.0, behavior matches legacy model."""
|
||||
sim, akku, inverter = simulation_setup(ac_to_dc_efficiency=1.0)
|
||||
|
||||
# Enable AC charging for hour 1 at 50% power
|
||||
sim.ac_charge_hours[1] = 0.5
|
||||
sim.dc_charge_hours[:] = 0
|
||||
sim.bat_discharge_hours[:] = 0
|
||||
|
||||
result = sim.simulate(start_hour=0)
|
||||
|
||||
# At hour 1: AC charge at 50% of 5000W = 2500W DC requested
|
||||
# With efficiency 1.0, AC consumed from grid = DC = 2500W
|
||||
# Battery stores: 2500 * 0.90 (battery eff) = 2250 Wh
|
||||
# Battery loss: 2500 - 2250 = 250 Wh
|
||||
# Total grid consumption for that hour = 1000 (load) + 2500 (AC charge)
|
||||
hour_idx = 1
|
||||
assert result["Netzbezug_Wh_pro_Stunde"][hour_idx] == pytest.approx(3500.0, rel=1e-3)
|
||||
assert result["Verluste_Pro_Stunde"][hour_idx] == pytest.approx(250.0, rel=1e-3)
|
||||
|
||||
def test_ac_charge_with_95_percent_efficiency(self, simulation_setup):
|
||||
"""With ac_to_dc_efficiency=0.95, more AC energy is consumed for same DC charge."""
|
||||
sim, akku, inverter = simulation_setup(ac_to_dc_efficiency=0.95)
|
||||
|
||||
sim.ac_charge_hours[1] = 0.5
|
||||
sim.dc_charge_hours[:] = 0
|
||||
sim.bat_discharge_hours[:] = 0
|
||||
|
||||
result = sim.simulate(start_hour=0)
|
||||
|
||||
# At hour 1: AC charge at 50% of 5000W = 2500W DC requested
|
||||
# With ac_to_dc_efficiency=0.95:
|
||||
# AC consumed = 2500 / 0.95 ≈ 2631.58 Wh
|
||||
# Inverter loss = 2631.58 - 2500 = 131.58 Wh
|
||||
# Battery stores: 2500 * 0.90 = 2250 Wh
|
||||
# Battery loss: 2500 - 2250 = 250 Wh
|
||||
# Total losses: 250 + 131.58 = 381.58 Wh
|
||||
hour_idx = 1
|
||||
dc_energy = 2500.0
|
||||
ac_energy = dc_energy / 0.95
|
||||
inverter_loss = ac_energy - dc_energy
|
||||
battery_loss = dc_energy * (1 - 0.90)
|
||||
total_loss = battery_loss + inverter_loss
|
||||
|
||||
expected_grid = 1000.0 + ac_energy
|
||||
assert result["Netzbezug_Wh_pro_Stunde"][hour_idx] == pytest.approx(
|
||||
expected_grid, rel=1e-3
|
||||
)
|
||||
assert result["Verluste_Pro_Stunde"][hour_idx] == pytest.approx(total_loss, rel=1e-3)
|
||||
|
||||
def test_ac_charge_disabled_by_zero_efficiency(self, simulation_setup):
|
||||
"""With ac_to_dc_efficiency=0.0, AC charging is completely disabled."""
|
||||
sim, akku, inverter = simulation_setup(ac_to_dc_efficiency=0.0)
|
||||
|
||||
sim.ac_charge_hours[1] = 1.0 # Try to AC charge
|
||||
sim.dc_charge_hours[:] = 0
|
||||
sim.bat_discharge_hours[:] = 0
|
||||
|
||||
initial_soc = akku.soc_wh
|
||||
result = sim.simulate(start_hour=0)
|
||||
|
||||
# Battery should not charge at all (AC charging disabled)
|
||||
# Grid consumption = only load
|
||||
hour_idx = 1
|
||||
assert result["Netzbezug_Wh_pro_Stunde"][hour_idx] == pytest.approx(1000.0, rel=1e-3)
|
||||
# Battery SoC should not change (no ac charge, no dc charge, no discharge)
|
||||
assert result["akku_soc_pro_stunde"][hour_idx] == pytest.approx(50.0, rel=1e-3)
|
||||
|
||||
def test_ac_charge_disabled_by_zero_max_power(self, simulation_setup):
|
||||
"""With max_ac_charge_power_w=0, AC charging is disabled."""
|
||||
sim, akku, inverter = simulation_setup(
|
||||
ac_to_dc_efficiency=0.95, max_ac_charge_power_w=0
|
||||
)
|
||||
|
||||
sim.ac_charge_hours[1] = 1.0
|
||||
sim.dc_charge_hours[:] = 0
|
||||
sim.bat_discharge_hours[:] = 0
|
||||
|
||||
result = sim.simulate(start_hour=0)
|
||||
|
||||
hour_idx = 1
|
||||
assert result["Netzbezug_Wh_pro_Stunde"][hour_idx] == pytest.approx(1000.0, rel=1e-3)
|
||||
assert result["akku_soc_pro_stunde"][hour_idx] == pytest.approx(50.0, rel=1e-3)
|
||||
|
||||
def test_ac_charge_limited_by_max_ac_power(self, simulation_setup):
|
||||
"""max_ac_charge_power_w limits the effective charge factor."""
|
||||
# battery max_charge_power_w = 5000, ac_to_dc_efficiency = 0.95
|
||||
# max_ac_charge_power_w = 2000
|
||||
# max_dc_factor = (2000 * 0.95) / 5000 = 0.38
|
||||
sim, akku, inverter = simulation_setup(
|
||||
ac_to_dc_efficiency=0.95, max_ac_charge_power_w=2000
|
||||
)
|
||||
|
||||
sim.ac_charge_hours[1] = 1.0 # Request full power
|
||||
sim.dc_charge_hours[:] = 0
|
||||
sim.bat_discharge_hours[:] = 0
|
||||
|
||||
result = sim.simulate(start_hour=0)
|
||||
|
||||
# Effective charge factor is capped at 0.38
|
||||
# DC energy = 5000 * 0.38 = 1900 W
|
||||
# AC energy = 1900 / 0.95 = 2000 W (respects limit)
|
||||
hour_idx = 1
|
||||
max_dc_factor = (2000 * 0.95) / 5000
|
||||
dc_energy = 5000 * max_dc_factor
|
||||
ac_energy = dc_energy / 0.95
|
||||
expected_grid = 1000.0 + ac_energy # load + AC charge
|
||||
|
||||
assert result["Netzbezug_Wh_pro_Stunde"][hour_idx] == pytest.approx(
|
||||
expected_grid, rel=1e-2
|
||||
)
|
||||
|
||||
def test_discharge_with_dc_to_ac_efficiency(self, simulation_setup):
|
||||
"""dc_to_ac_efficiency affects how much AC energy is delivered from battery."""
|
||||
sim, akku, inverter = simulation_setup(
|
||||
dc_to_ac_efficiency=0.90, battery_initial_soc_pct=80
|
||||
)
|
||||
|
||||
# No PV, no AC charge, discharge only
|
||||
sim.ac_charge_hours[:] = 0
|
||||
sim.dc_charge_hours[:] = 1
|
||||
sim.bat_discharge_hours[:] = 1
|
||||
|
||||
result = sim.simulate(start_hour=0)
|
||||
|
||||
# With dc_to_ac_efficiency=0.90, battery discharge delivers less AC
|
||||
# This means more grid import compared to efficiency=1.0
|
||||
# At hour 0: load=1000, PV=0
|
||||
# Shortfall = 1000
|
||||
# DC request = 1000 / 0.90 ≈ 1111 Wh
|
||||
# Battery delivers (limited by capacity and efficiency):
|
||||
# max raw = min(soc - min_soc, max_charge_power) = min(8000, 5000) = 5000
|
||||
# max deliverable DC = 5000 * 0.90 (battery eff) = 4500
|
||||
# delivered DC = min(1111, 4500) = 1111 Wh
|
||||
# delivered AC = 1111 * 0.90 = 1000 Wh → covers full load
|
||||
# So grid_import should be ≈ 0 for first hours while battery has charge
|
||||
hour_idx = 0
|
||||
assert result["Netzbezug_Wh_pro_Stunde"][hour_idx] == pytest.approx(0.0, abs=5.0)
|
||||
|
||||
# But losses should be higher due to inverter DC→AC conversion
|
||||
# Inverter loss = 1111 * 0.10 = 111 Wh
|
||||
# Battery loss = raw_used - delivered = delivered/bat_eff - delivered
|
||||
# = 1111/0.90 - 1111 ≈ 123.5 Wh
|
||||
assert result["Verluste_Pro_Stunde"][hour_idx] > 200.0 # Significant losses
|
||||
|
||||
def test_round_trip_efficiency_cost_impact(self, simulation_setup):
|
||||
"""Verify AC charge + discharge round-trip losses increase total cost."""
|
||||
# Reference run: no inverter losses
|
||||
sim_ref, akku_ref, inv_ref = simulation_setup(
|
||||
ac_to_dc_efficiency=1.0, dc_to_ac_efficiency=1.0
|
||||
)
|
||||
sim_ref.ac_charge_hours[1] = 0.5
|
||||
sim_ref.dc_charge_hours[:] = 0
|
||||
sim_ref.bat_discharge_hours[:] = 1
|
||||
sim_ref.bat_discharge_hours[1] = 0 # Don't discharge while charging
|
||||
result_ref = sim_ref.simulate(start_hour=0)
|
||||
|
||||
# Test run: with inverter losses
|
||||
sim_test, akku_test, inv_test = simulation_setup(
|
||||
ac_to_dc_efficiency=0.93, dc_to_ac_efficiency=0.93
|
||||
)
|
||||
sim_test.ac_charge_hours[1] = 0.5
|
||||
sim_test.dc_charge_hours[:] = 0
|
||||
sim_test.bat_discharge_hours[:] = 1
|
||||
sim_test.bat_discharge_hours[1] = 0
|
||||
result_test = sim_test.simulate(start_hour=0)
|
||||
|
||||
# With inverter losses, total cost should be HIGHER
|
||||
assert result_test["Gesamtkosten_Euro"] > result_ref["Gesamtkosten_Euro"]
|
||||
# And total losses should be HIGHER
|
||||
assert result_test["Gesamt_Verluste"] > result_ref["Gesamt_Verluste"]
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# 4. Integration with optimizer residual battery value
|
||||
# ===================================================================
|
||||
|
||||
|
||||
class TestResidualBatteryValue:
|
||||
"""Verify that dc_to_ac_efficiency affects residual battery value calculation."""
|
||||
|
||||
def test_current_energy_content_unaffected(self):
|
||||
"""Battery.current_energy_content() is DC-only; inverter eff applied in optimizer."""
|
||||
akku = Genetic0Battery(
|
||||
Genetic0SolarPanelBatteryParameters(
|
||||
device_id="bat1",
|
||||
capacity_wh=10000,
|
||||
initial_soc_percentage=50,
|
||||
discharging_efficiency=0.90,
|
||||
min_soc_percentage=0,
|
||||
),
|
||||
prediction_hours=24,
|
||||
)
|
||||
akku.reset()
|
||||
|
||||
# DC energy content: (5000 - 0) * 0.90 = 4500
|
||||
assert akku.current_energy_content() == pytest.approx(4500.0, rel=1e-5)
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# 5. AC charging break-even penalty in GeneticOptimization.evaluate()
|
||||
# ===================================================================
|
||||
|
||||
|
||||
def _make_mock_simulation(
|
||||
*,
|
||||
# Inverter properties
|
||||
ac_to_dc_efficiency: float = 0.93,
|
||||
dc_to_ac_efficiency: float = 0.95,
|
||||
# Battery properties
|
||||
charging_efficiency: float = 0.95,
|
||||
discharging_efficiency: float = 0.95,
|
||||
capacity_wh: float = 10_000.0,
|
||||
initial_soc_percentage: float = 0.0, # fraction of capacity already stored (0 = empty)
|
||||
min_soc_wh: float = 0.0,
|
||||
max_charge_power_w: float = 5_000.0,
|
||||
# Arrays (must be same length)
|
||||
ac_charge_hours: list | None = None,
|
||||
elect_price_hourly: list | None = None,
|
||||
load_energy_array: list | None = None,
|
||||
):
|
||||
"""Return a mock GeneticSimulation with configurable properties for penalty tests."""
|
||||
n = 24
|
||||
if ac_charge_hours is None:
|
||||
ac_charge_hours = [0.0] * n
|
||||
if elect_price_hourly is None:
|
||||
elect_price_hourly = [0.0003] * n # 30 ct/kWh flat
|
||||
if load_energy_array is None:
|
||||
load_energy_array = [1000.0] * n # 1 kWh constant load
|
||||
|
||||
inv = SimpleNamespace(
|
||||
ac_to_dc_efficiency=ac_to_dc_efficiency,
|
||||
dc_to_ac_efficiency=dc_to_ac_efficiency,
|
||||
)
|
||||
bat = SimpleNamespace(
|
||||
charging_efficiency=charging_efficiency,
|
||||
discharging_efficiency=discharging_efficiency,
|
||||
capacity_wh=capacity_wh,
|
||||
initial_soc_percentage=initial_soc_percentage,
|
||||
min_soc_wh=min_soc_wh,
|
||||
max_charge_power_w=max_charge_power_w,
|
||||
current_energy_content=Mock(return_value=0.0),
|
||||
)
|
||||
|
||||
sim = Mock()
|
||||
sim.battery = bat
|
||||
sim.inverter = inv
|
||||
sim.ev = None
|
||||
sim.ac_charge_hours = np.array(ac_charge_hours, dtype=float)
|
||||
sim.elect_price_hourly = np.array(elect_price_hourly, dtype=float)
|
||||
sim.load_energy_array = np.array(load_energy_array, dtype=float)
|
||||
return sim
|
||||
|
||||
|
||||
def _run_evaluate_with_mocked_sim(
|
||||
config_eos,
|
||||
mock_sim,
|
||||
*,
|
||||
ac_charge_break_even: float = 1.0,
|
||||
start_hour: int = 0,
|
||||
base_gesamtbilanz: float = 0.0,
|
||||
):
|
||||
"""
|
||||
Patch a GeneticOptimization so that:
|
||||
- evaluate_inner() returns a controlled base Gesamtbilanz_Euro
|
||||
- self.simulation is replaced by mock_sim
|
||||
Then call evaluate() and return the fitness tuple.
|
||||
"""
|
||||
from akkudoktoreos.optimization.genetic0.genetic0 import Genetic0Optimization
|
||||
|
||||
config_eos.merge_settings_from_dict(
|
||||
{
|
||||
"prediction": {"hours": 48},
|
||||
"optimization": {"hours": 24},
|
||||
}
|
||||
)
|
||||
config_eos.optimization.genetic0.penalties = {
|
||||
"ev_soc_miss": 10,
|
||||
"ac_charge_break_even": ac_charge_break_even,
|
||||
}
|
||||
|
||||
optim = Genetic0Optimization.__new__(Genetic0Optimization)
|
||||
# Minimal __init__ state expected by evaluate()
|
||||
optim.config = config_eos
|
||||
optim.optimize_ev = False
|
||||
optim.verbose = False
|
||||
optim.opti_param = {"home_appliance": 0}
|
||||
optim.simulation = mock_sim
|
||||
|
||||
# evaluate_inner() just returns the base balance; we test the *additional* penalty
|
||||
dummy_result = {
|
||||
"Gesamtbilanz_Euro": base_gesamtbilanz,
|
||||
"Gesamt_Verluste": 0.0,
|
||||
"EAuto_SoC_pro_Stunde": np.zeros(48),
|
||||
}
|
||||
|
||||
# DEAP individuals are lists that accept attribute assignment; use a trivial subclass
|
||||
class _Ind(list): # noqa: N801
|
||||
pass
|
||||
|
||||
fake_individual = _Ind([0] * 48)
|
||||
|
||||
with patch.object(optim, "evaluate_inner", return_value=dummy_result):
|
||||
fitness = optim.evaluate(
|
||||
fake_individual,
|
||||
parameters=Mock(
|
||||
ems=Mock(price_per_wh_battery=0.0),
|
||||
eauto=None,
|
||||
),
|
||||
start_hour=start_hour,
|
||||
worst_case=False,
|
||||
)
|
||||
return fitness[0]
|
||||
|
||||
|
||||
class TestAcChargeBreakEvenPenalty:
|
||||
"""Break-even penalty in GeneticOptimization.evaluate().
|
||||
|
||||
The penalty adds a positive (bad) contribution to the fitness score whenever
|
||||
AC grid charging is scheduled at an hour where the round-trip loss means the
|
||||
stored energy can never be discharged at a price sufficient to recover costs,
|
||||
taking into account that free PV-charged energy already in the battery covers
|
||||
the most expensive future hours first.
|
||||
"""
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# 5a. No AC charging → no penalty
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
def test_no_ac_charging_no_penalty(self, config_eos):
|
||||
"""When no AC charging is scheduled, fitness equals the base balance."""
|
||||
n = 24
|
||||
sim = _make_mock_simulation(
|
||||
ac_charge_hours=[0.0] * n,
|
||||
elect_price_hourly=[0.0003] * n,
|
||||
load_energy_array=[1000.0] * n,
|
||||
)
|
||||
base = 1.5
|
||||
fitness = _run_evaluate_with_mocked_sim(config_eos, sim, base_gesamtbilanz=base)
|
||||
assert fitness == pytest.approx(base, rel=1e-9)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# 5b. AC charging profitable → no penalty
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
def test_profitable_ac_charging_no_penalty(self, config_eos):
|
||||
"""When future discharge price > P_charge / η, charging is justified → no penalty."""
|
||||
n = 24
|
||||
# Charge at hour 0: 0.0001 €/Wh
|
||||
# Round-trip: 0.93 * 0.95 * 0.95 * 0.95 ≈ 0.7975
|
||||
# break-even price ≈ 0.0001 / 0.7975 ≈ 0.0001254 €/Wh
|
||||
# Future hour 1 price: 0.0003 > break-even → profitable
|
||||
prices = [0.0001] + [0.0003] * (n - 1)
|
||||
ac_charge = [1.0] + [0.0] * (n - 1)
|
||||
loads = [1000.0] * n
|
||||
|
||||
sim = _make_mock_simulation(
|
||||
ac_to_dc_efficiency=0.93,
|
||||
dc_to_ac_efficiency=0.95,
|
||||
charging_efficiency=0.95,
|
||||
discharging_efficiency=0.95,
|
||||
ac_charge_hours=ac_charge,
|
||||
elect_price_hourly=prices,
|
||||
load_energy_array=loads,
|
||||
initial_soc_percentage=0.0, # empty battery → no free PV energy
|
||||
)
|
||||
base = 0.0
|
||||
fitness = _run_evaluate_with_mocked_sim(config_eos, sim, base_gesamtbilanz=base)
|
||||
# penalty should be 0 (or very small due to floating-point rounding)
|
||||
assert fitness == pytest.approx(base, abs=1e-9)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# 5c. AC charging unprofitable → penalty fires
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
def test_unprofitable_ac_charging_adds_penalty(self, config_eos):
|
||||
"""When future discharge prices are too low to justify AC charging, penalty is added."""
|
||||
n = 24
|
||||
# Charge at hour 0: 0.0004 €/Wh
|
||||
# Round-trip: 0.93 * 0.95 * 0.95 * 0.95 ≈ 0.7975
|
||||
# break-even price ≈ 0.0004 / 0.7975 ≈ 0.000501 €/Wh
|
||||
# All future prices: 0.0003 < break-even → unprofitable
|
||||
prices = [0.0004] + [0.0003] * (n - 1)
|
||||
ac_charge = [1.0] + [0.0] * (n - 1)
|
||||
loads = [1000.0] * n
|
||||
|
||||
sim = _make_mock_simulation(
|
||||
ac_to_dc_efficiency=0.93,
|
||||
dc_to_ac_efficiency=0.95,
|
||||
charging_efficiency=0.95,
|
||||
discharging_efficiency=0.95,
|
||||
ac_charge_hours=ac_charge,
|
||||
elect_price_hourly=prices,
|
||||
load_energy_array=loads,
|
||||
initial_soc_percentage=0.0,
|
||||
)
|
||||
base = 0.0
|
||||
fitness = _run_evaluate_with_mocked_sim(config_eos, sim, base_gesamtbilanz=base)
|
||||
# Fitness must be worse (higher) than base
|
||||
assert fitness > base + 1e-6
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# 5d. Free PV energy covers expensive hours → penalty reduced/eliminated
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
def test_free_pv_energy_eliminates_penalty(self, config_eos):
|
||||
"""PV energy covers the best future hour; penalty is larger than without PV energy."""
|
||||
n = 24
|
||||
# Charge at hour 0: 0.0005 €/Wh
|
||||
# Round-trip: 0.93*0.95*0.95*0.95 ≈ 0.7974
|
||||
# break-even ≈ 0.0005 / 0.7974 ≈ 0.000627 €/Wh
|
||||
#
|
||||
# Future: one expensive hour at 0.0006 (< break-even!) and rest at 0.0003
|
||||
# → even the best future price 0.0006 < 0.000627 → AC charging is never profitable
|
||||
#
|
||||
# Empty battery: best_uncovered = 0.0006 < 0.000627 → penalty fires
|
||||
# With PV (50% SoC → ~4512 Wh deliverable free AC): covers the 0.0006 hour (1000 Wh)
|
||||
# → best_uncovered drops to 0.0003 → penalty fires with LARGER excess
|
||||
prices = [0.0005] + [0.0003] * (n - 2) + [0.0006] # expensive hour at end
|
||||
ac_charge = [1.0] + [0.0] * (n - 1)
|
||||
loads = [1000.0] * n
|
||||
|
||||
sim_empty = _make_mock_simulation(
|
||||
ac_to_dc_efficiency=0.93,
|
||||
dc_to_ac_efficiency=0.95,
|
||||
charging_efficiency=0.95,
|
||||
discharging_efficiency=0.95,
|
||||
ac_charge_hours=list(ac_charge),
|
||||
elect_price_hourly=list(prices),
|
||||
load_energy_array=list(loads),
|
||||
initial_soc_percentage=0.0, # no free PV energy
|
||||
)
|
||||
sim_with_pv = _make_mock_simulation(
|
||||
ac_to_dc_efficiency=0.93,
|
||||
dc_to_ac_efficiency=0.95,
|
||||
charging_efficiency=0.95,
|
||||
discharging_efficiency=0.95,
|
||||
ac_charge_hours=list(ac_charge),
|
||||
elect_price_hourly=list(prices),
|
||||
load_energy_array=list(loads),
|
||||
capacity_wh=10_000.0,
|
||||
initial_soc_percentage=50.0, # 5000 Wh free PV energy → ~4512 Wh deliverable AC
|
||||
)
|
||||
|
||||
fitness_empty = _run_evaluate_with_mocked_sim(config_eos, sim_empty, base_gesamtbilanz=0.0)
|
||||
fitness_pv = _run_evaluate_with_mocked_sim(config_eos, sim_with_pv, base_gesamtbilanz=0.0)
|
||||
|
||||
# Both are penalised (break-even > max future price)
|
||||
assert fitness_empty > 1e-6, "Empty battery: best_uncovered=0.0006 < break_even→ penalty"
|
||||
assert fitness_pv > 1e-6, "With PV: free energy covers 0.0006 hour, best drops to 0.0003"
|
||||
|
||||
# With PV the expensive hour is covered for free → uncovered best price is lower
|
||||
# → excess_cost_per_wh = break_even - best_uncovered is larger → penalty is BIGGER
|
||||
assert fitness_pv > fitness_empty, "PV covers expensive hour → uncovered best is cheaper"
|
||||
|
||||
|
||||
def test_free_pv_energy_exposes_only_cheap_future_prices(self, config_eos):
|
||||
"""When PV covers ALL expensive hours, best_uncovered_price = 0 → max penalty."""
|
||||
n = 5
|
||||
capacity_wh = 10_000.0
|
||||
# Free PV: initial 80% → (8000 - 0) * 0.95 * 0.95 = 7220 Wh deliverable AC
|
||||
# Future loads: 2 expensive hours × 1000 Wh = 2000 Wh → all covered by free PV
|
||||
prices = [0.0010, 0.0008, 0.0008, 0.0002, 0.0002]
|
||||
ac_charge = [1.0, 0.0, 0.0, 0.0, 0.0]
|
||||
loads = [1000.0] * n
|
||||
|
||||
sim = _make_mock_simulation(
|
||||
ac_to_dc_efficiency=0.93,
|
||||
dc_to_ac_efficiency=0.95,
|
||||
charging_efficiency=0.95,
|
||||
discharging_efficiency=0.95,
|
||||
capacity_wh=capacity_wh,
|
||||
initial_soc_percentage=80.0,
|
||||
ac_charge_hours=ac_charge,
|
||||
elect_price_hourly=prices,
|
||||
load_energy_array=loads,
|
||||
)
|
||||
|
||||
# break_even = 0.001 / (0.93*0.95*0.95*0.95) ≈ 0.001254
|
||||
# PV covers both 0.0008 hours → best_uncovered = 0.0002 → penalty fires
|
||||
fitness = _run_evaluate_with_mocked_sim(config_eos, sim, base_gesamtbilanz=0.0)
|
||||
assert fitness > 1e-6
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# 5e. Penalty factor scales the penalty
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
def test_penalty_factor_scales_linearly(self, config_eos):
|
||||
"""The ac_charge_break_even factor doubles the penalty when doubled."""
|
||||
n = 24
|
||||
prices = [0.0004] + [0.0003] * (n - 1)
|
||||
ac_charge = [1.0] + [0.0] * (n - 1)
|
||||
loads = [1000.0] * n
|
||||
|
||||
def _fitness(factor):
|
||||
sim = _make_mock_simulation(
|
||||
ac_to_dc_efficiency=0.93,
|
||||
dc_to_ac_efficiency=0.95,
|
||||
charging_efficiency=0.95,
|
||||
discharging_efficiency=0.95,
|
||||
ac_charge_hours=list(ac_charge),
|
||||
elect_price_hourly=list(prices),
|
||||
load_energy_array=list(loads),
|
||||
initial_soc_percentage=0.0,
|
||||
)
|
||||
return _run_evaluate_with_mocked_sim(
|
||||
config_eos, sim, ac_charge_break_even=factor, base_gesamtbilanz=0.0
|
||||
)
|
||||
|
||||
f1 = _fitness(1.0)
|
||||
f2 = _fitness(2.0)
|
||||
|
||||
# With factor=2 the penalty should be exactly double
|
||||
assert f2 == pytest.approx(2.0 * f1, rel=1e-6)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# 5f. Zero or negative AC charge factor → no penalty contribution
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
def test_zero_ac_charge_factor_no_penalty(self, config_eos):
|
||||
"""ac_charge_hours[h] = 0 means no charging, so no penalty."""
|
||||
n = 24
|
||||
prices = [0.0010] * n # Expensive, but no AC charging
|
||||
ac_charge = [0.0] * n
|
||||
loads = [1000.0] * n
|
||||
|
||||
sim = _make_mock_simulation(
|
||||
ac_charge_hours=ac_charge,
|
||||
elect_price_hourly=prices,
|
||||
load_energy_array=loads,
|
||||
)
|
||||
base = 3.0
|
||||
fitness = _run_evaluate_with_mocked_sim(config_eos, sim, base_gesamtbilanz=base)
|
||||
assert fitness == pytest.approx(base, abs=1e-9)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# 5g. No battery / inverter → penalty skipped entirely
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
def test_no_battery_skips_penalty(self, config_eos):
|
||||
"""When no battery is present, the penalty block is skipped."""
|
||||
n = 24
|
||||
sim = _make_mock_simulation(
|
||||
ac_charge_hours=[1.0] * n,
|
||||
elect_price_hourly=[0.001] * n,
|
||||
load_energy_array=[1000.0] * n,
|
||||
)
|
||||
sim.battery = None # no battery → penalty skipped
|
||||
|
||||
base = 2.5
|
||||
fitness = _run_evaluate_with_mocked_sim(config_eos, sim, base_gesamtbilanz=base)
|
||||
assert fitness == pytest.approx(base, abs=1e-9)
|
||||
|
||||
def test_no_inverter_skips_penalty(self, config_eos):
|
||||
"""When no inverter is present, the penalty block is skipped."""
|
||||
n = 24
|
||||
sim = _make_mock_simulation(
|
||||
ac_charge_hours=[1.0] * n,
|
||||
elect_price_hourly=[0.001] * n,
|
||||
load_energy_array=[1000.0] * n,
|
||||
)
|
||||
sim.inverter = None # no inverter → penalty skipped
|
||||
|
||||
base = 2.5
|
||||
fitness = _run_evaluate_with_mocked_sim(config_eos, sim, base_gesamtbilanz=base)
|
||||
assert fitness == pytest.approx(base, abs=1e-9)
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# 5h. Unit-level break-even maths (no optimizer setup needed)
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
def test_break_even_formula(self):
|
||||
"""Verify the break-even formula: P_break_even = P_charge / η_round_trip."""
|
||||
ac_to_dc = 0.93
|
||||
bat_charge = 0.95
|
||||
bat_discharge = 0.95
|
||||
dc_to_ac = 0.95
|
||||
|
||||
eta_rt = ac_to_dc * bat_charge * bat_discharge * dc_to_ac
|
||||
p_charge = 0.0004 # 40 ct/kWh
|
||||
|
||||
break_even = p_charge / eta_rt
|
||||
|
||||
# 1 Wh drawn from grid at p_charge → η_rt Wh delivered
|
||||
# Need discharge price ≥ p_charge / η_rt to break even
|
||||
assert break_even == pytest.approx(p_charge / eta_rt, rel=1e-9)
|
||||
assert break_even > p_charge # Always worse due to losses
|
||||
|
||||
def test_free_pv_energy_formula(self):
|
||||
"""Verify free pv energy: (initial_soc - min_soc) × η_bat_dis × η_inv_dis."""
|
||||
capacity_wh = 10_000.0
|
||||
initial_soc_pct = 60.0
|
||||
min_soc_wh = 500.0
|
||||
bat_dis = 0.95
|
||||
inv_dis = 0.95
|
||||
|
||||
initial_soc_wh = (initial_soc_pct / 100.0) * capacity_wh # 6000
|
||||
free_ac_wh = max(0.0, initial_soc_wh - min_soc_wh) * bat_dis * inv_dis
|
||||
|
||||
# = (6000 - 500) * 0.95 * 0.95 = 5500 * 0.9025 = 4963.75
|
||||
expected = 5500.0 * 0.95 * 0.95
|
||||
assert free_ac_wh == pytest.approx(expected, rel=1e-9)
|
||||
@@ -0,0 +1,155 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from akkudoktoreos.config.config import ConfigEOS
|
||||
from akkudoktoreos.core.cache import CacheEnergyManagementStore
|
||||
from akkudoktoreos.core.coreabc import get_ems
|
||||
from akkudoktoreos.optimization.genetic0.genetic0 import Genetic0Optimization
|
||||
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
|
||||
)
|
||||
|
||||
ems_eos = get_ems(init=True) # init once
|
||||
|
||||
DIR_TESTDATA = Path(__file__).parent / "testdata"
|
||||
|
||||
|
||||
def compare_dict(actual: dict[str, Any], expected: dict[str, Any]):
|
||||
assert set(actual) == set(expected)
|
||||
|
||||
for key, value in expected.items():
|
||||
if isinstance(value, dict):
|
||||
assert isinstance(actual[key], dict)
|
||||
compare_dict(actual[key], value)
|
||||
elif isinstance(value, list):
|
||||
assert isinstance(actual[key], list)
|
||||
assert actual[key] == pytest.approx(value)
|
||||
else:
|
||||
assert actual[key] == pytest.approx(value)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@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),
|
||||
],
|
||||
)
|
||||
async def test_optimize(
|
||||
fn_in: str,
|
||||
fn_out: str,
|
||||
ngen: int,
|
||||
break_even: int,
|
||||
config_eos: ConfigEOS,
|
||||
is_finalize: bool,
|
||||
):
|
||||
"""Test optimize_ems."""
|
||||
# Test parameters
|
||||
fixed_start_hour = 10
|
||||
fixed_seed = 42
|
||||
|
||||
# Assure configuration holds the correct values
|
||||
config_eos.merge_settings_from_dict(
|
||||
{
|
||||
"prediction": {
|
||||
"hours": 48
|
||||
},
|
||||
"optimization": {
|
||||
"algorithm": "GENETIC0",
|
||||
"genetic0": {
|
||||
"horizon_hours": 48,
|
||||
"individuals": 300,
|
||||
"generations": 10,
|
||||
"penalties": {
|
||||
"ev_soc_miss": 10,
|
||||
"ac_charge_break_even": break_even,
|
||||
}
|
||||
}
|
||||
},
|
||||
"devices": {
|
||||
"max_electric_vehicles": 1,
|
||||
"electric_vehicles": [
|
||||
{
|
||||
"charge_rates": [0.0, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0],
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
# Load input and output data
|
||||
file = DIR_TESTDATA / fn_in
|
||||
with 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))
|
||||
|
||||
# Throw away any cached results of the last energy management run.
|
||||
CacheEnergyManagementStore().clear()
|
||||
|
||||
genetic0_optimization = Genetic0Optimization(fixed_seed=fixed_seed)
|
||||
|
||||
# Activate with pytest --finalize
|
||||
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()
|
||||
|
||||
# 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))
|
||||
|
||||
assert genetic0_solution.result.Gesamtbilanz_Euro == pytest.approx(
|
||||
expected_result.result.Gesamtbilanz_Euro
|
||||
)
|
||||
|
||||
# Assert that the output contains all expected entries.
|
||||
# This does not assert that the optimization always gives the same result!
|
||||
# Reproducibility and mathematical accuracy should be tested on the level of individual components.
|
||||
compare_dict(genetic0_solution.model_dump(), expected_result.model_dump())
|
||||
|
||||
# Check the correct generic optimization solution is created
|
||||
optimization_solution = await genetic0_solution.optimization_solution()
|
||||
# @TODO
|
||||
|
||||
# Check the correct generic energy management plan is created
|
||||
plan = genetic0_solution.energy_management_plan()
|
||||
# @TODO
|
||||
@@ -0,0 +1,654 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from akkudoktoreos.config.configabc import TimeWindow, TimeWindowSequence
|
||||
from akkudoktoreos.devices.genetic0.genetic0battery import Genetic0Battery
|
||||
from akkudoktoreos.devices.genetic0.genetic0homeappliance import Genetic0HomeAppliance
|
||||
from akkudoktoreos.devices.genetic0.genetic0inverter import Genetic0Inverter
|
||||
from akkudoktoreos.optimization.genetic0.genetic0 import (
|
||||
Genetic0Simulation,
|
||||
Genetic0SimulationResult,
|
||||
)
|
||||
from akkudoktoreos.optimization.genetic0.genetic0devices import (
|
||||
Genetic0ElectricVehicleParameters,
|
||||
Genetic0HomeApplianceParameters,
|
||||
Genetic0InverterParameters,
|
||||
Genetic0SolarPanelBatteryParameters,
|
||||
)
|
||||
from akkudoktoreos.optimization.genetic0.genetic0params import (
|
||||
Genetic0EnergyManagementParameters,
|
||||
)
|
||||
from akkudoktoreos.utils.datetimeutil import to_duration, to_time
|
||||
|
||||
START_HOUR = 0
|
||||
|
||||
|
||||
# Example initialization of necessary components
|
||||
@pytest.fixture
|
||||
def genetic0_simulation(config_eos) -> Genetic0Simulation:
|
||||
"""Fixture to create an GENETIC2 simualtion instance with given test parameters."""
|
||||
# Assure configuration holds the correct values
|
||||
config_eos.merge_settings_from_dict(
|
||||
{
|
||||
"prediction": {
|
||||
"hours": 48
|
||||
},
|
||||
"optimization": {
|
||||
"hours": 24
|
||||
}
|
||||
}
|
||||
)
|
||||
assert config_eos.prediction.hours == 48
|
||||
assert config_eos.optimization.genetic0.horizon_hours == 24
|
||||
|
||||
# Initialize the battery and the inverter
|
||||
akku = Genetic0Battery(
|
||||
Genetic0SolarPanelBatteryParameters(
|
||||
device_id="battery1",
|
||||
capacity_wh=5000,
|
||||
initial_soc_percentage=80,
|
||||
min_soc_percentage=10,
|
||||
),
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
)
|
||||
akku.reset()
|
||||
|
||||
inverter = Genetic0Inverter(
|
||||
Genetic0InverterParameters(device_id="inverter1", max_power_wh=10000, battery_id=akku.parameters.device_id),
|
||||
battery = akku,
|
||||
)
|
||||
|
||||
# Household device (currently not used, set to None)
|
||||
home_appliance = Genetic0HomeAppliance(
|
||||
Genetic0HomeApplianceParameters(
|
||||
device_id="dishwasher1",
|
||||
consumption_wh=2000,
|
||||
duration_h=2,
|
||||
time_windows=None,
|
||||
),
|
||||
optimization_hours = config_eos.optimization.genetic0.horizon_hours,
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
)
|
||||
|
||||
# Example initialization of electric car battery
|
||||
eauto = Genetic0Battery(
|
||||
Genetic0ElectricVehicleParameters(
|
||||
device_id="ev1", capacity_wh=26400, initial_soc_percentage=10, min_soc_percentage=10
|
||||
),
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
)
|
||||
eauto.set_charge_per_hour(np.full(config_eos.prediction.hours, 1))
|
||||
|
||||
# Parameters based on previous example data
|
||||
pv_prognose_wh = [
|
||||
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,
|
||||
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,
|
||||
]
|
||||
|
||||
strompreis_euro_pro_wh = [
|
||||
0.0003384,
|
||||
0.0003318,
|
||||
0.0003284,
|
||||
0.0003283,
|
||||
0.0003289,
|
||||
0.0003334,
|
||||
0.0003290,
|
||||
0.0003302,
|
||||
0.0003042,
|
||||
0.0002430,
|
||||
0.0002280,
|
||||
0.0002212,
|
||||
0.0002093,
|
||||
0.0001879,
|
||||
0.0001838,
|
||||
0.0002004,
|
||||
0.0002198,
|
||||
0.0002270,
|
||||
0.0002997,
|
||||
0.0003195,
|
||||
0.0003081,
|
||||
0.0002969,
|
||||
0.0002921,
|
||||
0.0002780,
|
||||
0.0003384,
|
||||
0.0003318,
|
||||
0.0003284,
|
||||
0.0003283,
|
||||
0.0003289,
|
||||
0.0003334,
|
||||
0.0003290,
|
||||
0.0003302,
|
||||
0.0003042,
|
||||
0.0002430,
|
||||
0.0002280,
|
||||
0.0002212,
|
||||
0.0002093,
|
||||
0.0001879,
|
||||
0.0001838,
|
||||
0.0002004,
|
||||
0.0002198,
|
||||
0.0002270,
|
||||
0.0002997,
|
||||
0.0003195,
|
||||
0.0003081,
|
||||
0.0002969,
|
||||
0.0002921,
|
||||
0.0002780,
|
||||
]
|
||||
|
||||
einspeiseverguetung_euro_pro_wh = 0.00007
|
||||
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.00,
|
||||
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,
|
||||
]
|
||||
|
||||
# Initialize the energy management system with the respective parameters
|
||||
genetic0_simulation = Genetic0Simulation()
|
||||
genetic0_simulation.prepare(
|
||||
Genetic0EnergyManagementParameters(
|
||||
pv_prognose_wh=pv_prognose_wh,
|
||||
strompreis_euro_pro_wh=strompreis_euro_pro_wh,
|
||||
einspeiseverguetung_euro_pro_wh=einspeiseverguetung_euro_pro_wh,
|
||||
preis_euro_pro_wh_akku=preis_euro_pro_wh_akku,
|
||||
gesamtlast=gesamtlast,
|
||||
),
|
||||
optimization_hours = config_eos.optimization.genetic0.horizon_hours,
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
inverter=inverter,
|
||||
ev=eauto,
|
||||
home_appliance=home_appliance,
|
||||
)
|
||||
|
||||
# Init for test
|
||||
assert genetic0_simulation.ac_charge_hours is not None
|
||||
assert genetic0_simulation.dc_charge_hours is not None
|
||||
assert genetic0_simulation.bat_discharge_hours is not None
|
||||
assert genetic0_simulation.ev_charge_hours is not None
|
||||
genetic0_simulation.ac_charge_hours[START_HOUR] = 1.0
|
||||
genetic0_simulation.dc_charge_hours[START_HOUR] = 1.0
|
||||
genetic0_simulation.bat_discharge_hours[START_HOUR] = 1.0
|
||||
genetic0_simulation.ev_charge_hours[START_HOUR] = 1.0
|
||||
genetic0_simulation.home_appliance_start_hour = 2
|
||||
|
||||
return genetic0_simulation
|
||||
|
||||
@pytest.fixture
|
||||
def genetic0_simulation_2(config_eos) -> Genetic0Simulation:
|
||||
"""Fixture to create an EnergyManagement instance with given test parameters."""
|
||||
# Assure configuration holds the correct values
|
||||
config_eos.merge_settings_from_dict(
|
||||
{
|
||||
"prediction": {
|
||||
"hours": 48
|
||||
},
|
||||
"optimization": {
|
||||
"hours": 24
|
||||
}
|
||||
}
|
||||
)
|
||||
assert config_eos.prediction.hours == 48
|
||||
assert config_eos.optimization.genetic0.horizon_hours == 24
|
||||
|
||||
# Initialize the battery and the inverter
|
||||
akku = Genetic0Battery(
|
||||
Genetic0SolarPanelBatteryParameters(
|
||||
device_id="battery1",
|
||||
capacity_wh=5000,
|
||||
initial_soc_percentage=80,
|
||||
min_soc_percentage=10,
|
||||
),
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
)
|
||||
akku.reset()
|
||||
|
||||
inverter = Genetic0Inverter(
|
||||
Genetic0InverterParameters(device_id="inverter1", max_power_wh=10000, battery_id=akku.parameters.device_id),
|
||||
battery = akku,
|
||||
)
|
||||
|
||||
# Household device (currently not used, set to None)
|
||||
home_appliance = Genetic0HomeAppliance(
|
||||
Genetic0HomeApplianceParameters(
|
||||
device_id="dishwasher1",
|
||||
consumption_wh=2000,
|
||||
duration_h=2,
|
||||
time_windows=None,
|
||||
),
|
||||
optimization_hours = config_eos.optimization.genetic0.horizon_hours,
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
)
|
||||
|
||||
# Example initialization of electric car battery
|
||||
eauto = Genetic0Battery(
|
||||
Genetic0ElectricVehicleParameters(
|
||||
device_id="ev1", capacity_wh=26400, initial_soc_percentage=10, min_soc_percentage=10
|
||||
),
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
)
|
||||
|
||||
# Parameters based on previous example data
|
||||
pv_prognose_wh = [0.0] * config_eos.prediction.hours
|
||||
pv_prognose_wh[10] = 5000.0
|
||||
pv_prognose_wh[11] = 5000.0
|
||||
|
||||
strompreis_euro_pro_wh = [0.001] * config_eos.prediction.hours
|
||||
strompreis_euro_pro_wh[0:10] = [0.00001] * 10
|
||||
strompreis_euro_pro_wh[11:15] = [0.00005] * 4
|
||||
strompreis_euro_pro_wh[20] = 0.00001
|
||||
|
||||
einspeiseverguetung_euro_pro_wh = [0.00007] * len(strompreis_euro_pro_wh)
|
||||
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.00,
|
||||
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,
|
||||
]
|
||||
|
||||
# Initialize the energy management system with the respective parameters
|
||||
simulation = Genetic0Simulation()
|
||||
simulation.prepare(
|
||||
Genetic0EnergyManagementParameters(
|
||||
pv_prognose_wh=pv_prognose_wh,
|
||||
strompreis_euro_pro_wh=strompreis_euro_pro_wh,
|
||||
einspeiseverguetung_euro_pro_wh=einspeiseverguetung_euro_pro_wh,
|
||||
preis_euro_pro_wh_akku=preis_euro_pro_wh_akku,
|
||||
gesamtlast=gesamtlast,
|
||||
),
|
||||
optimization_hours = config_eos.optimization.genetic0.horizon_hours,
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
inverter=inverter,
|
||||
ev=eauto,
|
||||
home_appliance=home_appliance,
|
||||
)
|
||||
|
||||
ac = np.full(config_eos.prediction.hours, 0.0)
|
||||
ac[20] = 1
|
||||
simulation.ac_charge_hours = ac
|
||||
dc = np.full(config_eos.prediction.hours, 0.0)
|
||||
dc[11] = 1
|
||||
simulation.dc_charge_hours = dc
|
||||
simulation.home_appliance_start_hour = 2
|
||||
|
||||
return simulation
|
||||
|
||||
|
||||
def test_genetic0simulation(genetic0_simulation):
|
||||
"""Test the EnergyManagement simulation method."""
|
||||
simulation = genetic0_simulation
|
||||
|
||||
# Simulate starting from hour 1 (this value can be adjusted)
|
||||
|
||||
result = simulation.simulate(start_hour=START_HOUR)
|
||||
|
||||
# visualisiere_ergebnisse(
|
||||
# simulation.gesamtlast,
|
||||
# simulation.pv_prognose_wh,
|
||||
# simulation.strompreis_euro_pro_wh,
|
||||
# result,
|
||||
# simulation.akku.discharge_array+simulation.akku.charge_array,
|
||||
# None,
|
||||
# simulation.pv_prognose_wh,
|
||||
# START_HOUR,
|
||||
# 48,
|
||||
# np.full(48, 0.0),
|
||||
# filename="visualization_results.pdf",
|
||||
# extra_data=None,
|
||||
# )
|
||||
|
||||
# Assertions to validate results
|
||||
assert result is not None, "Result should not be None"
|
||||
assert isinstance(result, dict), "Result should be a dictionary"
|
||||
assert "Last_Wh_pro_Stunde" in result, "Result should contain 'Last_Wh_pro_Stunde'"
|
||||
|
||||
"""
|
||||
Check the result of the simulation based on expected values.
|
||||
"""
|
||||
# Example result returned from the simulation (used for assertions)
|
||||
assert result is not None, "Result should not be None."
|
||||
|
||||
# Check that the result is a dictionary
|
||||
assert isinstance(result, dict), "Result should be a dictionary."
|
||||
assert Genetic0SimulationResult(**result) is not None
|
||||
|
||||
# Check the length of the main arrays
|
||||
assert len(result["Last_Wh_pro_Stunde"]) == 48, (
|
||||
"The length of 'Last_Wh_pro_Stunde' should be 48."
|
||||
)
|
||||
assert len(result["Netzeinspeisung_Wh_pro_Stunde"]) == 48, (
|
||||
"The length of 'Netzeinspeisung_Wh_pro_Stunde' should be 48."
|
||||
)
|
||||
assert len(result["Netzbezug_Wh_pro_Stunde"]) == 48, (
|
||||
"The length of 'Netzbezug_Wh_pro_Stunde' should be 48."
|
||||
)
|
||||
assert len(result["Kosten_Euro_pro_Stunde"]) == 48, (
|
||||
"The length of 'Kosten_Euro_pro_Stunde' should be 48."
|
||||
)
|
||||
assert len(result["akku_soc_pro_stunde"]) == 48, (
|
||||
"The length of 'akku_soc_pro_stunde' should be 48."
|
||||
)
|
||||
|
||||
# Verify specific values in the 'Last_Wh_pro_Stunde' array
|
||||
assert result["Last_Wh_pro_Stunde"][1] == 876.19, (
|
||||
"The value at index 1 of 'Last_Wh_pro_Stunde' should be 876.19."
|
||||
)
|
||||
assert result["Last_Wh_pro_Stunde"][2] == 1527.13, (
|
||||
"The value at index 2 of 'Last_Wh_pro_Stunde' should be 1527.13."
|
||||
)
|
||||
assert result["Last_Wh_pro_Stunde"][12] == 1320.56, (
|
||||
"The value at index 12 of 'Last_Wh_pro_Stunde' should be 1320.56."
|
||||
)
|
||||
|
||||
# Verify that the value at index 0 is 'None'
|
||||
# Check that 'Netzeinspeisung_Wh_pro_Stunde' and 'Netzbezug_Wh_pro_Stunde' are consistent
|
||||
assert result["Netzbezug_Wh_pro_Stunde"][1] == 876.19, (
|
||||
"The value at index 1 of 'Netzbezug_Wh_pro_Stunde' should be 876.19."
|
||||
)
|
||||
|
||||
# Verify the total balance
|
||||
assert abs(result["Gesamtbilanz_Euro"] - 6.883546477556756) < 1e-5, (
|
||||
"Total balance should be 6.883546477556756."
|
||||
)
|
||||
|
||||
# Check total revenue and total costs
|
||||
assert abs(result["Gesamteinnahmen_Euro"] - 1.964301131937134) < 1e-5, (
|
||||
"Total revenue should be 1.964301131937134."
|
||||
)
|
||||
assert abs(result["Gesamtkosten_Euro"] - 8.84784760949389) < 1e-5, (
|
||||
"Total costs should be 8.84784760949389."
|
||||
)
|
||||
|
||||
# Check the losses
|
||||
assert abs(result["Gesamt_Verluste"] - 1620.0) < 1e-5, (
|
||||
"Total losses should be 1620.0 ."
|
||||
)
|
||||
|
||||
# Check the values in 'akku_soc_pro_stunde'
|
||||
assert result["akku_soc_pro_stunde"][-1] == 98.0, (
|
||||
"The value at index -1 of 'akku_soc_pro_stunde' should be 98.0."
|
||||
)
|
||||
assert result["akku_soc_pro_stunde"][1] == 98.0, (
|
||||
"The value at index 1 of 'akku_soc_pro_stunde' should be 98.0."
|
||||
)
|
||||
|
||||
# Check home appliances
|
||||
assert sum(simulation.home_appliance.get_load_curve()) == 2000, (
|
||||
"The sum of 'simulation.home_appliance.get_load_curve()' should be 2000."
|
||||
)
|
||||
|
||||
assert (
|
||||
np.nansum(
|
||||
np.where(
|
||||
result["Home_appliance_wh_per_hour"] is None,
|
||||
np.nan,
|
||||
np.array(result["Home_appliance_wh_per_hour"]),
|
||||
)
|
||||
)
|
||||
== 2000
|
||||
), "The sum of 'Home_appliance_wh_per_hour' should be 2000."
|
||||
|
||||
print("All tests passed successfully.")
|
||||
|
||||
|
||||
|
||||
def test_genetic0simulation_2(genetic0_simulation_2):
|
||||
"""Test the EnergyManagement simulation method."""
|
||||
simulation = genetic0_simulation_2
|
||||
|
||||
# Simulate starting from hour 0 (this value can be adjusted)
|
||||
result = simulation.simulate(start_hour=START_HOUR)
|
||||
|
||||
# --- Pls do not remove! ---
|
||||
# visualisiere_ergebnisse(
|
||||
# simulation.gesamtlast,
|
||||
# simulation.pv_prognose_wh,
|
||||
# simulation.strompreis_euro_pro_wh,
|
||||
# result,
|
||||
# simulation.akku.discharge_array+simulation.akku.charge_array,
|
||||
# None,
|
||||
# simulation.pv_prognose_wh,
|
||||
# START_HOUR,
|
||||
# 48,
|
||||
# np.full(48, 0.0),
|
||||
# filename="visualization_results.pdf",
|
||||
# extra_data=None,
|
||||
# )
|
||||
|
||||
# Assertions to validate results
|
||||
assert result is not None, "Result should not be None"
|
||||
assert isinstance(result, dict), "Result should be a dictionary"
|
||||
assert Genetic0SimulationResult(**result) is not None
|
||||
assert "Last_Wh_pro_Stunde" in result, "Result should contain 'Last_Wh_pro_Stunde'"
|
||||
|
||||
"""
|
||||
Check the result of the simulation based on expected values.
|
||||
"""
|
||||
# Example result returned from the simulation (used for assertions)
|
||||
assert result is not None, "Result should not be None."
|
||||
|
||||
# Check that the result is a dictionary
|
||||
assert isinstance(result, dict), "Result should be a dictionary."
|
||||
|
||||
# Verify that the expected keys are present in the result
|
||||
expected_keys = [
|
||||
"Last_Wh_pro_Stunde",
|
||||
"Netzeinspeisung_Wh_pro_Stunde",
|
||||
"Netzbezug_Wh_pro_Stunde",
|
||||
"Kosten_Euro_pro_Stunde",
|
||||
"akku_soc_pro_stunde",
|
||||
"Einnahmen_Euro_pro_Stunde",
|
||||
"Gesamtbilanz_Euro",
|
||||
"EAuto_SoC_pro_Stunde",
|
||||
"Gesamteinnahmen_Euro",
|
||||
"Gesamtkosten_Euro",
|
||||
"Verluste_Pro_Stunde",
|
||||
"Gesamt_Verluste",
|
||||
"Home_appliance_wh_per_hour",
|
||||
]
|
||||
|
||||
for key in expected_keys:
|
||||
assert key in result, f"The key '{key}' should be present in the result."
|
||||
|
||||
# Check the length of the main arrays
|
||||
assert len(result["Last_Wh_pro_Stunde"]) == 48, (
|
||||
"The length of 'Last_Wh_pro_Stunde' should be 48."
|
||||
)
|
||||
assert len(result["Netzeinspeisung_Wh_pro_Stunde"]) == 48, (
|
||||
"The length of 'Netzeinspeisung_Wh_pro_Stunde' should be 48."
|
||||
)
|
||||
assert len(result["Netzbezug_Wh_pro_Stunde"]) == 48, (
|
||||
"The length of 'Netzbezug_Wh_pro_Stunde' should be 48."
|
||||
)
|
||||
assert len(result["Kosten_Euro_pro_Stunde"]) == 48, (
|
||||
"The length of 'Kosten_Euro_pro_Stunde' should be 48."
|
||||
)
|
||||
assert len(result["akku_soc_pro_stunde"]) == 48, (
|
||||
"The length of 'akku_soc_pro_stunde' should be 48."
|
||||
)
|
||||
|
||||
# Verfify DC and AC Charge Bins
|
||||
assert abs(result["akku_soc_pro_stunde"][2] - 80.0) < 1e-5, (
|
||||
"'akku_soc_pro_stunde[2]' should be 80.0."
|
||||
)
|
||||
assert abs(result["akku_soc_pro_stunde"][10] - 80.0) < 1e-5, (
|
||||
"'akku_soc_pro_stunde[10]' should be 80."
|
||||
)
|
||||
|
||||
assert abs(result["Netzeinspeisung_Wh_pro_Stunde"][10] - 3946.93) < 1e-3, (
|
||||
"'Netzeinspeisung_Wh_pro_Stunde[11]' should be 3946.93."
|
||||
)
|
||||
|
||||
assert abs(result["Netzeinspeisung_Wh_pro_Stunde"][11] - 2799.7263636361786) < 1e-3, (
|
||||
"'Netzeinspeisung_Wh_pro_Stunde[11]' should be 2799.7263636361786."
|
||||
)
|
||||
|
||||
assert abs(result["akku_soc_pro_stunde"][20] - 100) < 1e-5, (
|
||||
"'akku_soc_pro_stunde[20]' should be 100."
|
||||
)
|
||||
assert abs(result["Last_Wh_pro_Stunde"][20] - 1050.98) < 1e-3, (
|
||||
"'Last_Wh_pro_Stunde[20]' should be 1050.98."
|
||||
)
|
||||
|
||||
print("All tests passed successfully.")
|
||||
|
||||
|
||||
def test_set_parameters(genetic0_simulation_2):
|
||||
"""Test the set_parameters method of EnergyManagement."""
|
||||
simulation = genetic0_simulation_2
|
||||
|
||||
# Check if parameters are set correctly
|
||||
assert simulation.load_energy_array is not None, "load_energy_array should not be None"
|
||||
assert simulation.pv_prediction_wh is not None, "pv_prediction_wh should not be None"
|
||||
assert simulation.elect_price_hourly is not None, "elect_price_hourly should not be None"
|
||||
assert simulation.elect_revenue_per_hour_arr is not None, (
|
||||
"elect_revenue_per_hour_arr should not be None"
|
||||
)
|
||||
|
||||
|
||||
def test_reset(genetic0_simulation_2):
|
||||
"""Test the reset method of EnergyManagement."""
|
||||
simulation = genetic0_simulation_2
|
||||
simulation.reset()
|
||||
assert simulation.ev.current_soc_percentage() == simulation.ev.parameters.initial_soc_percentage, "EV SOC should be reset to initial value"
|
||||
assert simulation.battery.current_soc_percentage() == simulation.battery.parameters.initial_soc_percentage, (
|
||||
"Battery SOC should be reset to initial value"
|
||||
)
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
import json
|
||||
|
||||
from akkudoktoreos.optimization.genetic.geneticparams import (
|
||||
GeneticEnergyManagementParameters,
|
||||
from akkudoktoreos.optimization.genetic0.genetic0 import Genetic0SimulationResult
|
||||
from akkudoktoreos.optimization.genetic0.genetic0params import (
|
||||
Genetic0EnergyManagementParameters,
|
||||
)
|
||||
from akkudoktoreos.optimization.genetic.geneticsolution import GeneticSimulationResult
|
||||
|
||||
|
||||
def test_genetic_params_german_input():
|
||||
def test_genetic0_params_german_input():
|
||||
"""Test that German field names are accepted as input."""
|
||||
data_de = {
|
||||
"pv_prognose_wh": [100.0, 200.0],
|
||||
@@ -18,11 +18,11 @@ def test_genetic_params_german_input():
|
||||
"preis_euro_pro_wh_akku": 0.0001,
|
||||
"gesamtlast": [500.0, 600.0],
|
||||
}
|
||||
params = GeneticEnergyManagementParameters(**data_de)
|
||||
params = Genetic0EnergyManagementParameters(**data_de)
|
||||
assert params.pv_forecast_wh == [100.0, 200.0]
|
||||
print("✅ German input accepted")
|
||||
|
||||
def test_genetic_params_english_input():
|
||||
def test_genetic0_params_english_input():
|
||||
"""Test that English field names are accepted as input."""
|
||||
data_en = {
|
||||
"pv_forecast_wh": [100.0, 200.0],
|
||||
@@ -31,11 +31,11 @@ def test_genetic_params_english_input():
|
||||
"price_per_wh_battery": 0.0001,
|
||||
"total_load": [500.0, 600.0],
|
||||
}
|
||||
params = GeneticEnergyManagementParameters(**data_en)
|
||||
params = Genetic0EnergyManagementParameters(**data_en)
|
||||
assert params.pv_forecast_wh == [100.0, 200.0]
|
||||
print("✅ English input accepted")
|
||||
|
||||
def test_genetic_params_english_output():
|
||||
def test_genetic0_params_english_output():
|
||||
"""Test that both English and German field names are in JSON output (backward compatibility)."""
|
||||
data_de = {
|
||||
"pv_prognose_wh": [100.0, 200.0],
|
||||
@@ -44,7 +44,7 @@ def test_genetic_params_english_output():
|
||||
"preis_euro_pro_wh_akku": 0.0001,
|
||||
"gesamtlast": [500.0, 600.0],
|
||||
}
|
||||
params = GeneticEnergyManagementParameters(**data_de)
|
||||
params = Genetic0EnergyManagementParameters(**data_de)
|
||||
json_output = json.loads(params.model_dump_json(by_alias=True))
|
||||
|
||||
# English names should be in output
|
||||
@@ -66,7 +66,7 @@ def test_genetic_params_english_output():
|
||||
assert json_output["electricity_price_per_wh"] == json_output["strompreis_euro_pro_wh"]
|
||||
print("✅ Both English and German output generated")
|
||||
|
||||
def test_simulation_result_translations():
|
||||
def test_genetic0_simulation_result_translations():
|
||||
"""Test simulation result field translations."""
|
||||
data_de = {
|
||||
"Last_Wh_pro_Stunde": [100.0, 200.0],
|
||||
@@ -84,7 +84,7 @@ def test_simulation_result_translations():
|
||||
"akku_soc_pro_stunde": [80.0, 90.0],
|
||||
"Electricity_price": [0.0003, 0.0003],
|
||||
}
|
||||
result = GeneticSimulationResult(**data_de)
|
||||
result = Genetic0SimulationResult(**data_de)
|
||||
json_output = json.loads(result.model_dump_json(by_alias=True))
|
||||
|
||||
# Check English field names in output
|
||||
@@ -121,16 +121,16 @@ def test_simulation_result_translations():
|
||||
print("✅ Simulation result translations work")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_genetic_params_german_input()
|
||||
test_genetic_params_english_input()
|
||||
test_genetic_params_english_output()
|
||||
test_simulation_result_translations()
|
||||
test_genetic0_params_german_input()
|
||||
test_genetic0_params_english_input()
|
||||
test_genetic0_params_english_output()
|
||||
test_genetic0_simulation_result_translations()
|
||||
print("\n✅✅✅ All translation tests passed! ✅✅✅")
|
||||
|
||||
def test_optimization_parameters_device_translations():
|
||||
def test_genetic0_optimization_parameters_device_translations():
|
||||
"""Test that German device field names are accepted and re-emitted."""
|
||||
from akkudoktoreos.optimization.genetic.geneticparams import (
|
||||
GeneticOptimizationParameters,
|
||||
from akkudoktoreos.optimization.genetic0.genetic0params import (
|
||||
Genetic0OptimizationParameters,
|
||||
)
|
||||
|
||||
ems_de = {
|
||||
@@ -140,7 +140,7 @@ def test_optimization_parameters_device_translations():
|
||||
"preis_euro_pro_wh_akku": 0.0001,
|
||||
"gesamtlast": [500.0, 600.0],
|
||||
}
|
||||
params = GeneticOptimizationParameters(
|
||||
params = Genetic0OptimizationParameters(
|
||||
ems=ems_de,
|
||||
pv_akku={"device_id": "battery1", "capacity_wh": 8000},
|
||||
inverter=None,
|
||||
@@ -153,7 +153,7 @@ def test_optimization_parameters_device_translations():
|
||||
assert params.ev.capacity_wh == 60000
|
||||
|
||||
# English input names work as well
|
||||
params_en = GeneticOptimizationParameters(
|
||||
params_en = Genetic0OptimizationParameters(
|
||||
ems=ems_de,
|
||||
pv_battery={"device_id": "battery1", "capacity_wh": 8000},
|
||||
inverter=None,
|
||||
@@ -67,8 +67,9 @@ async def test_optimize(
|
||||
"hours": 48
|
||||
},
|
||||
"optimization": {
|
||||
"horizon_hours": 48,
|
||||
"algorithm": "GENETIC",
|
||||
"genetic": {
|
||||
"horizon_hours": 48,
|
||||
"individuals": 300,
|
||||
"generations": 10,
|
||||
"penalties": {
|
||||
|
||||
@@ -31,7 +31,7 @@ def genetic_simulation(config_eos) -> GeneticSimulation:
|
||||
{"prediction": {"hours": 48}, "optimization": {"hours": 24}}
|
||||
)
|
||||
assert config_eos.prediction.hours == 48
|
||||
assert config_eos.optimization.horizon_hours == 24
|
||||
assert config_eos.optimization.genetic.horizon_hours == 24
|
||||
|
||||
# Initialize the battery and the inverter
|
||||
akku = Battery(
|
||||
@@ -58,7 +58,7 @@ def genetic_simulation(config_eos) -> GeneticSimulation:
|
||||
duration_h=2,
|
||||
time_windows=None,
|
||||
),
|
||||
optimization_hours = config_eos.optimization.horizon_hours,
|
||||
optimization_hours = config_eos.optimization.genetic.horizon_hours,
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
)
|
||||
|
||||
@@ -238,7 +238,7 @@ def genetic_simulation(config_eos) -> GeneticSimulation:
|
||||
preis_euro_pro_wh_akku=preis_euro_pro_wh_akku,
|
||||
gesamtlast=gesamtlast,
|
||||
),
|
||||
optimization_hours = config_eos.optimization.horizon_hours,
|
||||
optimization_hours = config_eos.optimization.genetic.horizon_hours,
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
inverter=inverter,
|
||||
ev=eauto,
|
||||
|
||||
@@ -31,7 +31,7 @@ def genetic_simulation_2(config_eos) -> GeneticSimulation:
|
||||
{"prediction": {"hours": 48}, "optimization": {"hours": 24}}
|
||||
)
|
||||
assert config_eos.prediction.hours == 48
|
||||
assert config_eos.optimization.horizon_hours == 24
|
||||
assert config_eos.optimization.genetic.horizon_hours == 24
|
||||
|
||||
# Initialize the battery and the inverter
|
||||
akku = Battery(
|
||||
@@ -58,7 +58,7 @@ def genetic_simulation_2(config_eos) -> GeneticSimulation:
|
||||
duration_h=2,
|
||||
time_windows=None,
|
||||
),
|
||||
optimization_hours = config_eos.optimization.horizon_hours,
|
||||
optimization_hours = config_eos.optimization.genetic.horizon_hours,
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
)
|
||||
|
||||
@@ -144,7 +144,7 @@ def genetic_simulation_2(config_eos) -> GeneticSimulation:
|
||||
preis_euro_pro_wh_akku=preis_euro_pro_wh_akku,
|
||||
gesamtlast=gesamtlast,
|
||||
),
|
||||
optimization_hours = config_eos.optimization.horizon_hours,
|
||||
optimization_hours = config_eos.optimization.genetic.horizon_hours,
|
||||
prediction_hours = config_eos.prediction.hours,
|
||||
inverter=inverter,
|
||||
ev=eauto,
|
||||
|
||||
@@ -266,7 +266,7 @@ class TestAcChargingInSimulation:
|
||||
preis_euro_pro_wh_akku=0.0001,
|
||||
gesamtlast=[1000.0] * prediction_hours, # 1 kW constant load
|
||||
),
|
||||
optimization_hours=config_eos.optimization.horizon_hours,
|
||||
optimization_hours=config_eos.optimization.genetic.horizon_hours,
|
||||
prediction_hours=prediction_hours,
|
||||
inverter=inverter,
|
||||
ev=None,
|
||||
|
||||
+11
-2
@@ -177,10 +177,19 @@
|
||||
]
|
||||
},
|
||||
"optimization": {
|
||||
"horizon_hours": 24,
|
||||
"interval": 3600,
|
||||
"algorithm": "GENETIC",
|
||||
"genetic": {
|
||||
"interval_sec": 3600,
|
||||
"horizon_hours": 24,
|
||||
"individuals": 400,
|
||||
"generations": 400,
|
||||
"seed": null,
|
||||
"penalties": {
|
||||
"ev_soc_miss": 10
|
||||
}
|
||||
},
|
||||
"genetic0": {
|
||||
"horizon_hours": 24,
|
||||
"individuals": 400,
|
||||
"generations": 400,
|
||||
"seed": null,
|
||||
|
||||
+132
-15
@@ -8,10 +8,9 @@
|
||||
| Name | Environment Variable | Type | Read-Only | Default | Description |
|
||||
| ---- | -------------------- | ---- | --------- | ------- | ----------- |
|
||||
| algorithm | `EOS_OPTIMIZATION__ALGORITHM` | `str` | `rw` | `GENETIC` | The optimization algorithm. Defaults to GENETIC |
|
||||
| genetic | `EOS_OPTIMIZATION__GENETIC` | `GeneticCommonSettings` | `rw` | `required` | Genetic optimization algorithm configuration. |
|
||||
| horizon | | `int` | `ro` | `N/A` | Number of optimization steps. |
|
||||
| horizon_hours | `EOS_OPTIMIZATION__HORIZON_HOURS` | `int` | `rw` | `24` | The general time window within which the energy optimization goal shall be achieved [h]. Defaults to 24 hours. |
|
||||
| interval | `EOS_OPTIMIZATION__INTERVAL` | `int` | `rw` | `3600` | The optimization interval [sec]. Defaults to 3600 seconds (1 hour) |
|
||||
| algorithms | | `list[str]` | `ro` | `N/A` | Available optimization algorithms. |
|
||||
| genetic | `EOS_OPTIMIZATION__GENETIC` | `GeneticCommonSettings` | `rw` | `required` | GENETIC optimization algorithm configuration. |
|
||||
| genetic0 | `EOS_OPTIMIZATION__GENETIC0` | `Genetic0CommonSettings` | `rw` | `required` | GENETIC0 optimization algorithm configuration. |
|
||||
| keys | | `list[str]` | `ro` | `N/A` | The keys of the solution. |
|
||||
:::
|
||||
<!-- pyml enable line-length -->
|
||||
@@ -24,10 +23,19 @@
|
||||
```json
|
||||
{
|
||||
"optimization": {
|
||||
"horizon_hours": 24,
|
||||
"interval": 3600,
|
||||
"algorithm": "GENETIC",
|
||||
"genetic": {
|
||||
"interval_sec": 3600,
|
||||
"horizon_hours": 24,
|
||||
"individuals": 400,
|
||||
"generations": 400,
|
||||
"seed": null,
|
||||
"penalties": {
|
||||
"ev_soc_miss": 10
|
||||
}
|
||||
},
|
||||
"genetic0": {
|
||||
"horizon_hours": 24,
|
||||
"individuals": 400,
|
||||
"generations": 400,
|
||||
"seed": null,
|
||||
@@ -48,49 +56,68 @@
|
||||
```json
|
||||
{
|
||||
"optimization": {
|
||||
"horizon_hours": 24,
|
||||
"interval": 3600,
|
||||
"algorithm": "GENETIC",
|
||||
"genetic": {
|
||||
"interval_sec": 3600,
|
||||
"horizon_hours": 24,
|
||||
"individuals": 400,
|
||||
"generations": 400,
|
||||
"seed": null,
|
||||
"penalties": {
|
||||
"ev_soc_miss": 10
|
||||
}
|
||||
},
|
||||
"horizon": 24
|
||||
},
|
||||
"keys": [],
|
||||
"horizon": 24
|
||||
"genetic0": {
|
||||
"horizon_hours": 24,
|
||||
"individuals": 400,
|
||||
"generations": 400,
|
||||
"seed": null,
|
||||
"penalties": {
|
||||
"ev_soc_miss": 10
|
||||
},
|
||||
"interval_sec": 3600,
|
||||
"horizon": 24
|
||||
},
|
||||
"algorithms": [
|
||||
"GENETIC",
|
||||
"GENETIC0"
|
||||
],
|
||||
"keys": []
|
||||
}
|
||||
}
|
||||
```
|
||||
<!-- pyml enable line-length -->
|
||||
|
||||
### General Genetic Optimization Algorithm Configuration
|
||||
### GENETIC0 Optimization Algorithm Configuration
|
||||
|
||||
<!-- pyml disable line-length -->
|
||||
:::{table} optimization::genetic
|
||||
:::{table} optimization::genetic0
|
||||
:widths: 10 10 5 5 30
|
||||
:align: left
|
||||
|
||||
| Name | Type | Read-Only | Default | Description |
|
||||
| ---- | ---- | --------- | ------- | ----------- |
|
||||
| generations | `int | None` | `rw` | `400` | Number of generations to evolve [>= 10]. Defaults to 400. |
|
||||
| horizon | `int` | `ro` | `N/A` | Number of optimization steps. |
|
||||
| horizon_hours | `int` | `rw` | `24` | The general time window within which the energy optimization goal shall be achieved [h]. Defaults to 24 hours. |
|
||||
| individuals | `int | None` | `rw` | `300` | Number of individuals (solutions) in the population [>= 10]. Defaults to 300. |
|
||||
| interval_sec | `int` | `ro` | `N/A` | The optimization interval [sec]. Fixed to 1 hour (3600 seconds). |
|
||||
| penalties | `dict[str, float | int | str]` | `rw` | `required` | Penalty parameters used in fitness evaluation. |
|
||||
| seed | `int | None` | `rw` | `None` | Random seed for reproducibility. None = random. |
|
||||
:::
|
||||
<!-- pyml enable line-length -->
|
||||
|
||||
<!-- pyml disable no-emphasis-as-heading -->
|
||||
**Example Input/Output**
|
||||
**Example Input**
|
||||
<!-- pyml enable no-emphasis-as-heading -->
|
||||
|
||||
<!-- pyml disable line-length -->
|
||||
```json
|
||||
{
|
||||
"optimization": {
|
||||
"genetic": {
|
||||
"genetic0": {
|
||||
"horizon_hours": 24,
|
||||
"individuals": 300,
|
||||
"generations": 400,
|
||||
"seed": null,
|
||||
@@ -102,3 +129,93 @@
|
||||
}
|
||||
```
|
||||
<!-- pyml enable line-length -->
|
||||
|
||||
<!-- pyml disable no-emphasis-as-heading -->
|
||||
**Example Output**
|
||||
<!-- pyml enable no-emphasis-as-heading -->
|
||||
|
||||
<!-- pyml disable line-length -->
|
||||
```json
|
||||
{
|
||||
"optimization": {
|
||||
"genetic0": {
|
||||
"horizon_hours": 24,
|
||||
"individuals": 300,
|
||||
"generations": 400,
|
||||
"seed": null,
|
||||
"penalties": {
|
||||
"ev_soc_miss": 10
|
||||
},
|
||||
"interval_sec": 3600,
|
||||
"horizon": 24
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
<!-- pyml enable line-length -->
|
||||
|
||||
### GENETIC Optimization Algorithm Configuration
|
||||
|
||||
<!-- pyml disable line-length -->
|
||||
:::{table} optimization::genetic
|
||||
:widths: 10 10 5 5 30
|
||||
:align: left
|
||||
|
||||
| Name | Type | Read-Only | Default | Description |
|
||||
| ---- | ---- | --------- | ------- | ----------- |
|
||||
| generations | `int | None` | `rw` | `400` | Number of generations to evolve [>= 10]. Defaults to 400. |
|
||||
| horizon | `int` | `ro` | `N/A` | Number of optimization steps. |
|
||||
| horizon_hours | `int` | `rw` | `24` | The general time window within which the energy optimization goal shall be achieved [h]. Defaults to 24 hours. |
|
||||
| individuals | `int | None` | `rw` | `300` | Number of individuals (solutions) in the population [>= 10]. Defaults to 300. |
|
||||
| interval_sec | `int` | `rw` | `3600` | The optimization interval [sec]. Defaults to 3600 seconds (1 hour) |
|
||||
| penalties | `dict[str, float | int | str]` | `rw` | `required` | Penalty parameters used in fitness evaluation. |
|
||||
| seed | `int | None` | `rw` | `None` | Random seed for reproducibility. None = random. |
|
||||
:::
|
||||
<!-- pyml enable line-length -->
|
||||
|
||||
<!-- pyml disable no-emphasis-as-heading -->
|
||||
**Example Input**
|
||||
<!-- pyml enable no-emphasis-as-heading -->
|
||||
|
||||
<!-- pyml disable line-length -->
|
||||
```json
|
||||
{
|
||||
"optimization": {
|
||||
"genetic": {
|
||||
"interval_sec": 3600,
|
||||
"horizon_hours": 24,
|
||||
"individuals": 300,
|
||||
"generations": 400,
|
||||
"seed": null,
|
||||
"penalties": {
|
||||
"ev_soc_miss": 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
<!-- pyml enable line-length -->
|
||||
|
||||
<!-- pyml disable no-emphasis-as-heading -->
|
||||
**Example Output**
|
||||
<!-- pyml enable no-emphasis-as-heading -->
|
||||
|
||||
<!-- pyml disable line-length -->
|
||||
```json
|
||||
{
|
||||
"optimization": {
|
||||
"genetic": {
|
||||
"interval_sec": 3600,
|
||||
"horizon_hours": 24,
|
||||
"individuals": 300,
|
||||
"generations": 400,
|
||||
"seed": null,
|
||||
"penalties": {
|
||||
"ev_soc_miss": 10
|
||||
},
|
||||
"horizon": 24
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
<!-- pyml enable line-length -->
|
||||
|
||||
+4
-2
@@ -38,7 +38,9 @@
|
||||
]
|
||||
},
|
||||
"optimization": {
|
||||
"horizon_hours": 48
|
||||
"genetic0": {
|
||||
"horizon_hours": 48
|
||||
}
|
||||
},
|
||||
"elecprice": {
|
||||
"provider": "ElecPriceAkkudoktor",
|
||||
@@ -79,4 +81,4 @@
|
||||
"verbose": true,
|
||||
"eosdash_host": "0.0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -6,7 +6,9 @@
|
||||
"longitude": 13.4
|
||||
},
|
||||
"optimization": {
|
||||
"horizon_hours": 48
|
||||
"genetic0": {
|
||||
"horizon_hours": 48
|
||||
}
|
||||
},
|
||||
"elecprice": {
|
||||
"provider": "ElecPriceImport",
|
||||
@@ -16,4 +18,4 @@
|
||||
"host": "0.0.0.0",
|
||||
"eosdash_host": "0.0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"ems": {
|
||||
"preis_euro_pro_wh_akku": 0.0001,
|
||||
"einspeiseverguetung_euro_pro_wh": [
|
||||
0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007,
|
||||
0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007,
|
||||
0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007,
|
||||
0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007,
|
||||
0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007, 0.00007,
|
||||
0.00007, 0.00007, 0.00007
|
||||
],
|
||||
"gesamtlast": [
|
||||
676.71, 876.19, 527.13, 468.88, 531.38, 517.95, 483.15, 472.28, 1011.68, 995.00,
|
||||
1053.07, 1063.91, 1320.56, 1132.03, 1163.67, 1176.82, 1216.22, 1103.78, 1129.12,
|
||||
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, 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, 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
|
||||
],
|
||||
"strompreis_euro_pro_wh": [
|
||||
0.0003384, 0.0003318, 0.0003284, 0.0003283, 0.0003289, 0.0003334, 0.0003290,
|
||||
0.0003302, 0.0003042, 0.0002430, 0.0002280, 0.0002212, 0.0002093, 0.0001879,
|
||||
0.0001838, 0.0002004, 0.0002198, 0.0002270, 0.0002997, 0.0003195, 0.0003081,
|
||||
0.0002969, 0.0002921, 0.0002780, 0.0003384, 0.0003318, 0.0003284, 0.0003283,
|
||||
0.0003289, 0.0003334, 0.0003290, 0.0003302, 0.0003042, 0.0002430, 0.0002280,
|
||||
0.0002212, 0.0002093, 0.0001879, 0.0001838, 0.0002004, 0.0002198, 0.0002270,
|
||||
0.0002997, 0.0003195, 0.0003081, 0.0002969, 0.0002921, 0.0002780
|
||||
]
|
||||
},
|
||||
"pv_akku": {
|
||||
"device_id": "battery1",
|
||||
"capacity_wh": 26400,
|
||||
"max_charge_power_w": 5000,
|
||||
"initial_soc_percentage": 80,
|
||||
"min_soc_percentage": 15
|
||||
},
|
||||
"inverter": {
|
||||
"device_id": "inverter1",
|
||||
"max_power_wh": 10000,
|
||||
"battery_id": "battery1"
|
||||
},
|
||||
"eauto": {
|
||||
"device_id": "ev1",
|
||||
"capacity_wh": 60000,
|
||||
"charging_efficiency": 0.95,
|
||||
"discharging_efficiency": 1.0,
|
||||
"max_charge_power_w": 11040,
|
||||
"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, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 0, 0, 0, 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, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
||||
]
|
||||
}
|
||||
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
{
|
||||
"ems": {
|
||||
"preis_euro_pro_wh_akku": 0.0,
|
||||
"einspeiseverguetung_euro_pro_wh": 0.00007,
|
||||
"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_prognose_wh": [
|
||||
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,
|
||||
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
|
||||
],
|
||||
"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
|
||||
]
|
||||
},
|
||||
"pv_akku": {
|
||||
"device_id": "battery1",
|
||||
"capacity_wh": 26400,
|
||||
"initial_soc_percentage": 80,
|
||||
"min_soc_percentage": 0
|
||||
},
|
||||
"inverter": {
|
||||
"device_id": "inverter1",
|
||||
"max_power_wh": 10000,
|
||||
"battery_id": "battery1"
|
||||
},
|
||||
"eauto": {
|
||||
"device_id": "ev1",
|
||||
"capacity_wh": 60000,
|
||||
"charging_efficiency": 0.95,
|
||||
"max_charge_power_w": 11040,
|
||||
"initial_soc_percentage": 5,
|
||||
"min_soc_percentage": 80
|
||||
},
|
||||
"dishwasher": {
|
||||
"device_id": "dishwasher1",
|
||||
"consumption_wh": 5000,
|
||||
"duration_h": 2
|
||||
},
|
||||
"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
|
||||
}
|
||||
+772
@@ -0,0 +1,772 @@
|
||||
{
|
||||
"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
|
||||
],
|
||||
"eautocharge_hours_float": null,
|
||||
"result": {
|
||||
"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
|
||||
]
|
||||
},
|
||||
"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
|
||||
},
|
||||
"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
|
||||
}
|
||||
+772
@@ -0,0 +1,772 @@
|
||||
{
|
||||
"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
|
||||
],
|
||||
"eautocharge_hours_float": null,
|
||||
"result": {
|
||||
"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
|
||||
]
|
||||
},
|
||||
"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
|
||||
},
|
||||
"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
|
||||
}
|
||||
+818
@@ -0,0 +1,818 @@
|
||||
{
|
||||
"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
|
||||
],
|
||||
"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
|
||||
],
|
||||
"result": {
|
||||
"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
|
||||
]
|
||||
},
|
||||
"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
|
||||
},
|
||||
"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
|
||||
}
|
||||
+818
@@ -0,0 +1,818 @@
|
||||
{
|
||||
"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
|
||||
],
|
||||
"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
|
||||
],
|
||||
"result": {
|
||||
"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
|
||||
]
|
||||
},
|
||||
"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
|
||||
},
|
||||
"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
|
||||
}
|
||||
+818
@@ -0,0 +1,818 @@
|
||||
{
|
||||
"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
|
||||
],
|
||||
"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
|
||||
],
|
||||
"result": {
|
||||
"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
|
||||
]
|
||||
},
|
||||
"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
|
||||
},
|
||||
"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
|
||||
}
|
||||
Reference in New Issue
Block a user